]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinsearchbox.cpp
move the search-button from the toolbar into the configurator widget
[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
21 #include "dolphinsearchbox.h"
22
23 #include <config-nepomuk.h>
24
25 #include <KConfigGroup>
26 #include <KDesktopFile>
27 #include <kglobalsettings.h>
28 #include <klineedit.h>
29 #include <klocale.h>
30 #include <kiconloader.h>
31 #include <KStandardDirs>
32
33 #include <QEvent>
34 #include <QKeyEvent>
35 #include <QHBoxLayout>
36 #include <QStandardItemModel>
37 #include <QtGui/QCompleter>
38 #include <QtGui/QTreeView>
39 #include <QToolButton>
40
41 #ifdef HAVE_NEPOMUK
42 #include <Nepomuk/ResourceManager>
43 #include <Nepomuk/Tag>
44 #endif
45
46 DolphinSearchCompleter::DolphinSearchCompleter(KLineEdit* linedit) :
47 QObject(0),
48 q(linedit),
49 m_completer(0),
50 m_completionModel(0),
51 m_wordStart(-1),
52 m_wordEnd(-1)
53 {
54 m_completionModel = new QStandardItemModel(this);
55
56 #ifdef HAVE_NEPOMUK
57 if (!Nepomuk::ResourceManager::instance()->init()) {
58 //read all currently set tags
59 //NOTE if the user changes tags elsewhere they won't get updated here
60 QList<Nepomuk::Tag> tags = Nepomuk::Tag::allTags();
61 foreach (const Nepomuk::Tag& tag, tags) {
62 const QString tagText = tag.label();
63 addCompletionItem(tagText,
64 "tag:\"" + tagText + '\"',
65 i18nc("Tag as in Nepomuk::Tag", "Tag"),
66 QString(),
67 KIcon("mail-tagged"));
68 }
69 }
70 #endif //HAVE_NEPOMUK
71
72 // load the completions stored in the desktop file
73 KDesktopFile file(KStandardDirs::locate("data", "dolphin/dolphinsearchcommands.desktop"));
74 foreach (const QString &group, file.groupList()) {
75 KConfigGroup cg(&file, group);
76 const QString displayed = cg.readEntry("Name", QString());
77 const QString usedForCompletition = cg.readEntry("Completion", QString());
78 const QString description = cg.readEntry("Comment", QString());
79 const QString toolTip = cg.readEntry("GenericName", QString());
80 const QString icon = cg.readEntry("Icon", QString());
81
82 if (icon.isEmpty()) {
83 addCompletionItem(displayed, usedForCompletition, description, toolTip);
84 } else {
85 addCompletionItem(displayed, usedForCompletition, description, toolTip, KIcon(icon));
86 }
87 }
88
89 m_completionModel->sort(0, Qt::AscendingOrder);
90
91 m_completer = new QCompleter(m_completionModel, this);
92 m_completer->setWidget(q);
93 m_completer->setCaseSensitivity(Qt::CaseInsensitive);
94 QTreeView *view = new QTreeView;
95 m_completer->setPopup(view);
96 view->setRootIsDecorated(false);
97 view->setHeaderHidden(true);
98
99 connect(q, SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited(QString)));
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 DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
237 QWidget(parent),
238 m_searchInput(0),
239 m_completer(0)
240 {
241 QHBoxLayout* hLayout = new QHBoxLayout(this);
242 hLayout->setMargin(0);
243 hLayout->setSpacing(0);
244
245 m_searchInput = new KLineEdit(this);
246 m_searchInput->setClearButtonShown(true);
247 m_searchInput->setMinimumWidth(150);
248 m_searchInput->setClickMessage(i18nc("@label:textbox", "Search..."));
249 m_searchInput->installEventFilter(this);
250 hLayout->addWidget(m_searchInput);
251 connect(m_searchInput, SIGNAL(returnPressed()),
252 this, SLOT(emitSearchSignal()));
253 }
254
255 DolphinSearchBox::~DolphinSearchBox()
256 {
257 }
258
259 bool DolphinSearchBox::event(QEvent* event)
260 {
261 if (event->type() == QEvent::Polish) {
262 m_searchInput->setFont(KGlobalSettings::generalFont());
263 } else if (event->type() == QEvent::KeyPress) {
264 if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
265 m_searchInput->clear();
266 }
267 }
268 return QWidget::event(event);
269 }
270
271 bool DolphinSearchBox::eventFilter(QObject* watched, QEvent* event)
272 {
273 if ((watched == m_searchInput) && (event->type() == QEvent::FocusIn)) {
274 // Postpone the creation of the search completer until
275 // the search box is used. This decreases the startup time
276 // of Dolphin.
277 if (m_completer == 0) {
278 m_completer = new DolphinSearchCompleter(m_searchInput);
279 }
280 emit requestSearchOptions();
281 }
282
283 return QWidget::eventFilter(watched, event);
284 }
285
286
287 void DolphinSearchBox::emitSearchSignal()
288 {
289 emit search(KUrl("nepomuksearch:/" + m_searchInput->text()));
290 }
291
292 #include "dolphinsearchbox.moc"