]> git.sur5r.net Git - minitube/blob - src/googlesuggest.cpp
autocompleter refinements
[minitube] / src / googlesuggest.cpp
1 #include "googlesuggest.h"
2 #include "networkaccess.h"
3
4 #define GSUGGEST_URL "http://suggestqueries.google.com/complete/search?ds=yt&output=toolbar&hl=%1&q=%2"
5
6 namespace The {
7     NetworkAccess* http();
8 }
9
10 GSuggestCompletion::GSuggestCompletion(QWidget *parent, QLineEdit *editor):
11         QObject(parent), buddy(parent), editor(editor) {
12
13     enabled = true;
14
15     popup = new QListWidget;
16     popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
17     popup->installEventFilter(this);
18     popup->setMouseTracking(true);
19
20     connect(popup, SIGNAL(itemClicked(QListWidgetItem*)),
21             SLOT(doneCompletion()));
22
23     // connect(popup, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
24     //    SLOT(currentItemChanged(QListWidgetItem *)));
25
26     // mouse hover
27     // connect(popup, SIGNAL(itemEntered(QListWidgetItem*)),
28     //    SLOT(currentItemChanged(QListWidgetItem *)));
29
30     popup->setWindowFlags(Qt::Popup);
31     popup->setFocusPolicy(Qt::NoFocus);
32     popup->setFocusProxy(parent);
33
34     timer = new QTimer(this);
35     timer->setSingleShot(true);
36     timer->setInterval(300);
37     connect(timer, SIGNAL(timeout()), SLOT(autoSuggest()));
38     connect(editor, SIGNAL(textEdited(QString)), timer, SLOT(start()));
39
40 }
41
42 GSuggestCompletion::~GSuggestCompletion() {
43     delete popup;
44 }
45
46 bool GSuggestCompletion::eventFilter(QObject *obj, QEvent *ev) {
47     if (obj != popup)
48         return false;
49
50     if (ev->type() == QEvent::MouseButtonPress) {
51         popup->hide();
52         editor->setFocus();
53         editor->setText(originalText);
54         return true;
55     }
56
57     if (ev->type() == QEvent::KeyPress) {
58
59         bool consumed = false;
60
61         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
62         int key = keyEvent->key();
63         switch (key) {
64         case Qt::Key_Enter:
65         case Qt::Key_Return:
66             if (popup->currentItem()) {
67                 doneCompletion();
68                 consumed = true;
69             } else {
70                 editor->setFocus();
71                 editor->event(ev);
72                 popup->hide();
73             }
74             break;
75
76         case Qt::Key_Escape:
77             editor->setFocus();
78             editor->setText(originalText);
79             popup->hide();
80             consumed = true;
81             break;
82
83         case Qt::Key_Up:
84         case Qt::Key_Down:
85         case Qt::Key_Home:
86         case Qt::Key_End:
87         case Qt::Key_PageUp:
88         case Qt::Key_PageDown:
89             break;
90
91         default:
92
93             editor->setFocus();
94             editor->event(ev);
95             popup->hide();
96             break;
97         }
98
99         return consumed;
100     }
101
102     return false;
103 }
104
105 void GSuggestCompletion::showCompletion(const QStringList &choices) {
106
107     if (choices.isEmpty())
108         return;
109
110     popup->setUpdatesEnabled(false);
111     popup->clear();
112     for (int i = 0; i < choices.count(); ++i) {
113         QListWidgetItem * item;
114         item = new QListWidgetItem(popup);
115         item->setText(choices[i]);
116     }
117     popup->setCurrentItem(0);
118     popup->adjustSize();
119     popup->setUpdatesEnabled(true);
120
121     int h = popup->sizeHintForRow(0) * choices.count() + 4;
122     popup->resize(buddy->width(), h);
123
124     popup->move(buddy->mapToGlobal(QPoint(0, buddy->height())));
125
126     popup->setFocus();
127     popup->show();
128 }
129
130 void GSuggestCompletion::doneCompletion() {
131     timer->stop();
132     popup->hide();
133     editor->setFocus();
134     QListWidgetItem *item = popup->currentItem();
135     if (item) {
136         editor->setText(item->text());
137         QKeyEvent *e;
138         e = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
139         QApplication::postEvent(editor, e);
140         e = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier);
141         QApplication::postEvent(editor, e);
142     }
143 }
144
145 void GSuggestCompletion::preventSuggest() {
146     timer->stop();
147     enabled = false;
148     popup->hide();
149 }
150
151 void GSuggestCompletion::enableSuggest() {
152     enabled = true;
153 }
154
155 void GSuggestCompletion::autoSuggest() {
156     if (!enabled) return;
157
158     QString query = editor->text();
159     originalText = query;
160     qDebug() << "originalText" << originalText;
161     if (query.isEmpty()) return;
162
163     QString locale = QLocale::system().name().replace("_", "-");
164     // case for system locales such as "C"
165     if (locale.length() < 2) {
166         locale = "en-US";
167     }
168
169     QString url = QString(GSUGGEST_URL).arg(locale, query);
170
171     QObject *reply = The::http()->get(url);
172     connect(reply, SIGNAL(data(QByteArray)), SLOT(handleNetworkData(QByteArray)));
173 }
174
175 void GSuggestCompletion::handleNetworkData(QByteArray response) {
176     if (!enabled) return;
177
178     QStringList choices;
179
180     QXmlStreamReader xml(response);
181     while (!xml.atEnd()) {
182         xml.readNext();
183         if (xml.tokenType() == QXmlStreamReader::StartElement)
184             if (xml.name() == "suggestion") {
185             QStringRef str = xml.attributes().value("data");
186             choices << str.toString();
187         }
188     }
189
190     showCompletion(choices);
191
192 }
193
194 void GSuggestCompletion::currentItemChanged(QListWidgetItem *current) {
195     if (current) {
196         qDebug() << "current" << current->text();
197         current->setSelected(true);
198         editor->setText(current->text());
199         editor->setSelection(originalText.length(), editor->text().length());
200     }
201 }