]> git.sur5r.net Git - minitube/blob - src/autocomplete.cpp
e6e0d429af0fd4e9d08966c35a465d8371439154
[minitube] / src / autocomplete.cpp
1 /* $BEGIN_LICENSE
2
3 This file is part of Minitube.
4 Copyright 2013, Flavio Tordini <flavio.tordini@gmail.com>
5
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
18
19 $END_LICENSE */
20 #include "autocomplete.h"
21 #include "suggester.h"
22 #ifdef APP_MAC
23 #include "searchlineedit_mac.h"
24 #else
25 #include "searchlineedit.h"
26 #endif
27
28 AutoComplete::AutoComplete(SearchLineEdit *buddy, QLineEdit *lineEdit):
29     QObject(buddy), buddy(buddy), lineEdit(lineEdit), suggester(0) {
30
31     enabled = true;
32
33     popup = new QListWidget();
34     popup->setMouseTracking(true);
35     popup->setWindowFlags(Qt::Popup);
36     popup->setAttribute(Qt::WA_ShowWithoutActivating);
37     popup->setFocusPolicy(Qt::NoFocus);
38     popup->setFocusProxy(buddy);
39     popup->installEventFilter(this);
40
41     popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
42     popup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
43     popup->setWindowOpacity(.9);
44     popup->setProperty("suggest", true);
45     popup->setFrameShape(QFrame::NoFrame);
46     popup->setAttribute(Qt::WA_TranslucentBackground);
47     popup->viewport()->setStyleSheet("border:0; border-radius:5px; background:palette(base)");
48
49     connect(popup, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(acceptSuggestion()));
50     connect(popup, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
51         SLOT(currentItemChanged(QListWidgetItem*)));
52     connect(popup, SIGNAL(itemEntered(QListWidgetItem*)), SLOT(itemEntered(QListWidgetItem *)));
53
54     timer = new QTimer(this);
55     timer->setSingleShot(true);
56     timer->setInterval(500);
57     connect(timer, SIGNAL(timeout()), SLOT(suggest()));
58     connect(buddy, SIGNAL(textEdited(QString)), timer, SLOT(start()));
59 }
60
61 AutoComplete::~AutoComplete() {
62     delete popup;
63 }
64
65 bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) {
66     if (obj != popup) return false;
67
68     if (ev->type() == QEvent::Leave) {
69         popup->setCurrentItem(0);
70         popup->clearSelection();
71         if (!originalText.isEmpty()) buddy->setText(originalText);
72         return true;
73     }
74
75     if (ev->type() == QEvent::FocusOut) {
76         popup->hide();
77         buddy->setText(originalText);
78         buddy->setFocus();
79         return true;
80     }
81
82     if (ev->type() == QEvent::KeyPress) {
83         bool consumed = false;
84         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
85         // qWarning() << keyEvent->text();
86         switch (keyEvent->key()) {
87         case Qt::Key_Enter:
88         case Qt::Key_Return:
89             if (popup->currentItem()) {
90                 acceptSuggestion();
91                 consumed = true;
92             } else {
93                 buddy->setFocus();
94                 lineEdit->event(ev);
95                 popup->hide();
96             }
97             break;
98
99         case Qt::Key_Escape:
100             popup->hide();
101             popup->clear();
102             buddy->setText(originalText);
103             buddy->setFocus();
104             consumed = true;
105             break;
106
107         case Qt::Key_Up:
108             if (popup->currentRow() == 0) {
109                 popup->setCurrentItem(0);
110                 popup->clearSelection();
111                 buddy->setText(originalText);
112                 buddy->setFocus();
113                 consumed = true;
114             }
115             break;
116
117         case Qt::Key_Down:
118         case Qt::Key_Home:
119         case Qt::Key_End:
120         case Qt::Key_PageUp:
121         case Qt::Key_PageDown:
122             // qDebug() << key;
123             break;
124
125         default:
126             // qDebug() << keyEvent->text();
127             lineEdit->event(ev);
128             consumed = true;
129             break;
130         }
131
132         return consumed;
133     }
134
135     return false;
136 }
137
138 void AutoComplete::showCompletion(const QList<Suggestion *> &suggestions) {
139     if (suggestions.isEmpty()) {
140         popup->clear();
141         popup->hide();
142         return;
143     }
144     popup->setUpdatesEnabled(false);
145     popup->clear();
146     for (int i = 0; i < suggestions.count(); ++i) {
147         QListWidgetItem * item;
148         item = new QListWidgetItem(popup);
149         Suggestion *s = suggestions[i];
150         item->setText(s->value);
151         if (!s->type.isEmpty())
152             item->setIcon(QIcon(":/images/" + s->type + ".png"));
153     }
154     popup->setCurrentItem(0);
155     int h = 0;
156     for (int i = 0; i < suggestions.count(); ++i)
157         h += popup->sizeHintForRow(i);
158     popup->resize(buddy->width(), h);
159     popup->move(buddy->mapToGlobal(QPoint(0, buddy->height())));
160     popup->setFocus();
161
162     if (popup->isHidden()) popup->show();
163     popup->setUpdatesEnabled(true);
164 }
165
166 void AutoComplete::acceptSuggestion() {
167     timer->stop();
168     originalText.clear();
169     popup->hide();
170     buddy->setFocus();
171     int index = popup->currentIndex().row();
172     if (index >= 0 && index < suggestions.size()) {
173         Suggestion* suggestion = suggestions.at(index);
174         buddy->setText(suggestion->value);
175         emit suggestionAccepted(suggestion);
176         emit suggestionAccepted(suggestion->value);
177         popup->clear();
178     } else qWarning() << "No suggestion for index" << index;
179 }
180
181 void AutoComplete::preventSuggest() {
182     timer->stop();
183     enabled = false;
184     popup->hide();
185     popup->setFrameShape(QFrame::NoFrame);
186 }
187
188 void AutoComplete::enableSuggest() {
189     enabled = true;
190 }
191
192 void AutoComplete::setSuggester(Suggester* suggester) {
193     if (this->suggester) this->suggester->disconnect();
194     this->suggester = suggester;
195     connect(suggester, SIGNAL(ready(QList<Suggestion*>)), SLOT(suggestionsReady(QList<Suggestion*>)));
196 }
197
198 void AutoComplete::suggest() {
199     if (!enabled) return;
200     if (!buddy->hasFocus()) return;
201
202     popup->setCurrentItem(0);
203     popup->clearSelection();
204
205     originalText = buddy->text();
206     if (originalText.isEmpty()) {
207         popup->hide();
208         buddy->setFocus();
209         return;
210     }
211
212     if (suggester) suggester->suggest(originalText);
213 }
214
215 void AutoComplete::suggestionsReady(const QList<Suggestion *> &suggestions) {
216     qDeleteAll(this->suggestions);
217     this->suggestions = suggestions;
218     if (!enabled) return;
219     if (!buddy->hasFocus()) return;
220     showCompletion(suggestions);
221 }
222
223 void AutoComplete::itemEntered(QListWidgetItem *item) {
224     if (!item) return;
225     item->setSelected(true);
226     popup->setCurrentItem(item);
227 }
228
229 void AutoComplete::currentItemChanged(QListWidgetItem *item) {
230     if (!item) return;
231     buddy->setText(item->text());
232     // lineEdit->setSelection(originalText.length(), editor->text().length());
233 }