3 This file is part of Minitube.
4 Copyright 2013, Flavio Tordini <flavio.tordini@gmail.com>
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.
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.
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/>.
20 #include "autocomplete.h"
21 #include "suggester.h"
23 #include "searchlineedit_mac.h"
26 #include "searchlineedit.h"
29 #include <QListWidget>
31 #ifndef QT_NO_DEBUG_OUTPUT
32 /// Gives human-readable event type information.
33 QDebug operator<<(QDebug str, const QEvent * ev) {
34 static int eventEnumIndex = QEvent::staticMetaObject.indexOfEnumerator("Type");
37 QString name = QEvent::staticMetaObject.enumerator(eventEnumIndex).valueToKey(ev->type());
38 if (!name.isEmpty()) str << name; else str << ev->type();
42 return str.maybeSpace();
46 AutoComplete::AutoComplete(SearchLineEdit *buddy, QLineEdit *lineEdit):
47 QObject(buddy), buddy(buddy), lineEdit(lineEdit), enabled(true), suggester(0), itemHovering(false) {
49 popup = new QListWidget();
50 popup->setWindowFlags(Qt::Popup);
51 popup->setFocusProxy(buddy);
52 popup->installEventFilter(this);
53 buddy->window()->installEventFilter(this);
54 popup->setMouseTracking(true);
57 popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
58 popup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
59 popup->setWindowOpacity(.9);
60 popup->setProperty("suggest", true);
62 connect(popup, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(acceptSuggestion()));
63 connect(popup, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
64 SLOT(currentItemChanged(QListWidgetItem*)));
65 connect(popup, SIGNAL(itemEntered(QListWidgetItem*)), SLOT(itemEntered(QListWidgetItem *)));
67 timer = new QTimer(this);
68 timer->setSingleShot(true);
69 timer->setInterval(500);
70 connect(timer, SIGNAL(timeout()), SLOT(suggest()));
71 connect(buddy, SIGNAL(textEdited(QString)), timer, SLOT(start()));
74 bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) {
90 if (ev->type() == QEvent::Leave) {
91 popup->setCurrentItem(0);
92 popup->clearSelection();
93 if (!originalText.isEmpty()) buddy->setText(originalText);
97 if (ev->type() == QEvent::FocusOut || ev->type() == QEvent::MouseButtonPress) {
102 if (ev->type() == QEvent::KeyPress) {
103 bool consumed = false;
104 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
105 // qWarning() << keyEvent->text();
106 switch (keyEvent->key()) {
109 if (popup->currentItem()) {
124 if (popup->currentRow() == 0) {
125 popup->setCurrentItem(0);
126 popup->clearSelection();
127 buddy->setText(originalText);
137 case Qt::Key_PageDown:
142 // qDebug() << keyEvent->text();
154 void AutoComplete::showSuggestions(const QList<Suggestion *> &suggestions) {
155 if (suggestions.isEmpty()) {
159 popup->setUpdatesEnabled(false);
161 for (int i = 0; i < suggestions.count(); ++i) {
162 QListWidgetItem *item = new QListWidgetItem(popup);
163 Suggestion *s = suggestions[i];
164 item->setText(s->value);
165 if (!s->type.isEmpty())
166 item->setIcon(QIcon(":/images/" + s->type + ".png"));
168 popup->setCurrentItem(0);
169 int h = popup->frameWidth() * 2;
170 for (int i = 0; i < suggestions.count(); ++i)
171 h += popup->sizeHintForRow(i);
173 popup->resize(buddy->width(), h);
175 popup->setUpdatesEnabled(true);
177 if (popup->isHidden()) {
178 itemHovering = false;
180 QTimer::singleShot(100, this, SLOT(enableItemHovering()));
184 void AutoComplete::acceptSuggestion() {
185 int index = popup->currentIndex().row();
186 if (index >= 0 && index < suggestions.size()) {
187 Suggestion* suggestion = suggestions.at(index);
188 buddy->setText(suggestion->value);
189 emit suggestionAccepted(suggestion);
190 emit suggestionAccepted(suggestion->value);
191 originalText.clear();
193 } else qWarning() << "No suggestion for index" << index;
196 void AutoComplete::preventSuggest() {
202 void AutoComplete::enableSuggest() {
206 void AutoComplete::setSuggester(Suggester* suggester) {
207 if (this->suggester) this->suggester->disconnect();
208 this->suggester = suggester;
209 connect(suggester, SIGNAL(ready(QList<Suggestion*>)), SLOT(suggestionsReady(QList<Suggestion*>)));
212 void AutoComplete::suggest() {
213 if (!enabled) return;
215 popup->setCurrentItem(0);
216 popup->clearSelection();
218 originalText = buddy->text();
219 if (originalText.isEmpty()) {
224 if (suggester) suggester->suggest(originalText);
227 void AutoComplete::suggestionsReady(const QList<Suggestion *> &suggestions) {
228 qDeleteAll(this->suggestions);
229 this->suggestions = suggestions;
230 if (!enabled) return;
231 if (!buddy->hasFocus()) return;
232 showSuggestions(suggestions);
235 void AutoComplete::adjustPosition() {
236 popup->move(buddy->mapToGlobal(QPoint(0, buddy->height())));
239 void AutoComplete::enableItemHovering() {
243 void AutoComplete::hideSuggestions() {
244 itemHovering = false;
246 mac::fadeOutWindow(popup);
251 if (!originalText.isEmpty()) {
252 buddy->setText(originalText);
253 originalText.clear();
259 void AutoComplete::itemEntered(QListWidgetItem *item) {
260 if (!itemHovering) return;
262 item->setSelected(true);
263 popup->setCurrentItem(item);
266 void AutoComplete::currentItemChanged(QListWidgetItem *item) {
268 buddy->setText(item->text());
269 // lineEdit->setSelection(originalText.length(), lineEdit->text().length());