]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinsearchbox.cpp
Adds autocompletition to the dolphin-search-box. So far existing tags and commands...
[dolphin.git] / src / dolphinsearchbox.cpp
1 /***************************************************************************
2 * Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at> *
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 #include "dolphinsearchbox.h"
20
21 #include <config-nepomuk.h>
22
23 #include <kdialog.h>
24 #include <kglobalsettings.h>
25 #include <klineedit.h>
26 #include <klocale.h>
27 #include <kiconloader.h>
28
29 #include <QEvent>
30 #include <QKeyEvent>
31 #include <QHBoxLayout>
32 #include <QStandardItemModel>
33 #include <QtGui/QCompleter>
34 #include <QtGui/QTreeView>
35 #include <QToolButton>
36
37 #ifdef HAVE_NEPOMUK
38 #include <Nepomuk/ResourceManager>
39 #include <Nepomuk/Tag>
40 #endif //HAVE_NEPOMUK
41
42
43 DolphinSearchCompleter::DolphinSearchCompleter(KLineEdit* linedit) :
44 QObject(0),
45 q(linedit),
46 m_completer(0),
47 m_completionModel(0),
48 m_wordStart(-1),
49 m_wordEnd(-1)
50 {
51 }
52
53 void DolphinSearchCompleter::init()
54 {
55 m_completionModel = new QStandardItemModel(this);
56
57 #ifdef HAVE_NEPOMUK
58 if (!Nepomuk::ResourceManager::instance()->init()) {
59 //read all currently set tags
60 //NOTE if the user changes tags elsewhere they won't get updated here
61 QList<Nepomuk::Tag> tags = Nepomuk::Tag::allTags();
62 foreach (const Nepomuk::Tag& tag, tags) {
63 const QString tagText = tag.label();
64 addCompletionItem(tagText,
65 "tag:\"" + tagText + '\"',
66 i18nc("Tag as in Nepomuk::Tag", "Tag"),
67 KIcon("mail-tagged"));
68 }
69 }
70 #endif //HAVE_NEPOMUK
71
72 //add "and", "or" and "-" (not) logic operators
73 addCompletionItem(i18nc("and ss in a logic operator to connect search terms", "and"),
74 "and",
75 "logic operator and");
76 addCompletionItem(i18nc("or as in a logic operator to connect search terms", "or"),
77 "or",
78 "logic operator or");
79 addCompletionItem(i18nc("not as in a logic operator to connect search terms", "not"),
80 "-",
81 "logic operator not");
82
83 m_completionModel->sort(0, Qt::AscendingOrder);
84
85 m_completer = new QCompleter(m_completionModel, this);
86 m_completer->setWidget(q);
87 m_completer->setCaseSensitivity(Qt::CaseInsensitive);
88 QTreeView *view = new QTreeView;
89 m_completer->setPopup(view);
90 view->setRootIsDecorated(false);
91 view->setHeaderHidden(true);
92
93 connect(q, SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited(QString)));
94 connect(m_completer, SIGNAL(activated(QModelIndex)), this, SLOT(completionActivated(QModelIndex)));
95 connect(m_completer, SIGNAL(highlighted(QModelIndex)), this, SLOT(highlighted(QModelIndex)));
96 }
97
98 void DolphinSearchCompleter::addCompletionItem(const QString& displayed, const QString& usedForCompletition, const QString& description, const KIcon& icon)
99 {
100 QList<QStandardItem*> items;
101 QStandardItem *item = new QStandardItem();
102 item->setData(QVariant(displayed), Qt::DisplayRole);
103 item->setData(QVariant(usedForCompletition), Qt::UserRole);
104 items << item;
105
106 item = new QStandardItem(description);
107 item->setIcon(icon);
108 items << item;
109
110 m_completionModel->insertRow(m_completionModel->rowCount(), items);
111 }
112
113 void DolphinSearchCompleter::findText(int* wordStart, int* wordEnd, QString* newWord, int cursorPos, const QString &input)
114 {
115 --cursorPos;//decrease to get a useful position (not the end of the word e.g.)
116
117 if (!wordStart || !wordEnd) {
118 return;
119 }
120
121 *wordStart = -1;
122 *wordEnd = -1;
123
124 //the word might contain "" and thus maybe spaces
125 if (input.contains('\"')) {
126 int tempStart = -1;
127 int tempEnd = -1;
128
129 do {
130 tempStart = input.indexOf('\"', tempEnd + 1);
131 tempEnd = input.indexOf('\"', tempStart + 1);
132 if ((cursorPos >= tempStart) && (cursorPos <= tempEnd)) {
133 *wordStart = tempStart;
134 *wordEnd = tempEnd;
135 break;
136 } else if ((tempEnd == -1) && (cursorPos >= tempStart)) {
137 //one " found, so probably the beginning of the new word
138 *wordStart = tempStart;
139 break;
140 }
141 } while ((tempStart != -1) && (tempEnd != -1));
142 }
143
144 if (*wordEnd > -1) {
145 *wordEnd = input.indexOf(' ', *wordEnd) - 1;
146 } else {
147 *wordEnd = input.indexOf(' ', cursorPos) - 1;
148 }
149 if (*wordEnd < 0) {
150 *wordEnd = input.length() - 1;
151 }
152
153 if (*wordStart > -1) {
154 *wordStart = input.lastIndexOf(' ', *wordStart + 1) + 1;
155 } else {
156 *wordStart = input.lastIndexOf(' ', cursorPos) + 1;
157 }
158 if (*wordStart < 0) {
159 *wordStart = 0;
160 }
161
162
163 QString word = input.mid(*wordStart, *wordEnd - *wordStart + 1);
164
165 //remove opening braces or negations ('-' = not) at the beginning
166 while (word.count() && ((word[0] == '(') || (word[0] == '-'))) {
167 word.remove(0, 1);
168 ++(*wordStart);
169 }
170
171 //remove ending braces at the end
172 while (word.count() && (word[word.count() - 1] == ')')) {
173 word.remove(word.count() - 1, 1);
174 --(*wordEnd);
175 }
176
177 if (newWord) {
178 *newWord = word;
179 }
180 }
181
182 void DolphinSearchCompleter::slotTextEdited(const QString& text)
183 {
184 findText(&m_wordStart, &m_wordEnd, &m_userText, q->cursorPosition(), text);
185
186 if (!m_userText.isEmpty()) {
187 const int role = m_completer->completionRole();
188
189 //change the role used for comparison depending on what the user entered
190 if (m_userText.contains(':') || m_userText.contains('\"')) {
191 //assume that m_userText contains searchinformation like 'tag:"..."'
192 if (role != Qt::UserRole) {
193 m_completer->setCompletionRole(Qt::UserRole);
194 }
195 } else if (role != Qt::EditRole) {
196 m_completer->setCompletionRole(Qt::EditRole);
197 }
198
199 m_completer->setCompletionPrefix(m_userText);
200 m_completer->complete();
201 }
202 }
203
204 void DolphinSearchCompleter::highlighted(const QModelIndex& index)
205 {
206 QString text = q->text();
207 int wordStart;
208 int wordEnd;
209
210 findText(&wordStart, &wordEnd, 0, q->cursorPosition(), text);
211
212 QString replace = index.sibling(index.row(), 0).data(Qt::UserRole).toString();
213 //show the originally entered text
214 if (replace.isEmpty()) {
215 replace = m_userText;
216 }
217
218 text.replace(wordStart, wordEnd - wordStart + 1, replace);
219 q->setText(text);
220 q->setCursorPosition(wordStart + replace.length());
221 }
222
223 void DolphinSearchCompleter::activated(const QModelIndex& index)
224 {
225 if ((m_wordStart == -1) || (m_wordStart == -1)) {
226 return;
227 }
228
229 const QString replace = index.sibling(index.row(), 0).data(Qt::UserRole).toString();
230 QString newText = q->text();
231 newText.replace(m_wordStart, m_wordEnd - m_wordStart + 1, replace);
232 q->setText(newText);
233 }
234
235 DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
236 QWidget(parent),
237 m_searchInput(0),
238 m_searchButton(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 hLayout->addWidget(m_searchInput);
250 connect(m_searchInput, SIGNAL(returnPressed()),
251 this, SLOT(emitSearchSignal()));
252
253 m_completer = new DolphinSearchCompleter(m_searchInput);
254 m_completer->init();
255
256 m_searchButton = new QToolButton(this);
257 m_searchButton->setAutoRaise(true);
258 m_searchButton->setIcon(KIcon("edit-find"));
259 m_searchButton->setToolTip(i18nc("@info:tooltip", "Click to begin the search"));
260 hLayout->addWidget(m_searchButton);
261 connect(m_searchButton, SIGNAL(clicked()),
262 this, SLOT(emitSearchSignal()));
263 }
264
265 DolphinSearchBox::~DolphinSearchBox()
266 {
267 delete m_completer;
268 }
269
270 bool DolphinSearchBox::event(QEvent* event)
271 {
272 if (event->type() == QEvent::Polish) {
273 m_searchInput->setFont(KGlobalSettings::generalFont());
274 } else if (event->type() == QEvent::KeyPress) {
275 if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
276 m_searchInput->clear();
277 }
278 }
279 return QWidget::event(event);
280 }
281
282 void DolphinSearchBox::emitSearchSignal()
283 {
284 emit search(KUrl("nepomuksearch:/" + m_searchInput->text()));
285 }
286
287 #include "dolphinsearchbox.moc"