on cedbc3f help sign in
unity8/plugins/HudClient/hudclient.cpp
309 lines 11 kB history raw
1/*
2 * Copyright (C) 2012, 2013 Canonical, Ltd.
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; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include "hudclient.h"
19#include "hudtoolbarmodel.h"
21#include <deelistmodel.h>
23#include <QDebug>
25namespace
28extern "C"
31void loadingCB(GObject* /*src*/, gpointer dst)
33 static_cast<HudClient*>(dst)->voiceQueryLoading();
36void listeningCB(GObject* /*src*/, gpointer dst)
38 static_cast<HudClient*>(dst)->voiceQueryListening();
41void heardSomethingCB(GObject* /*src*/, gpointer dst)
43 static_cast<HudClient*>(dst)->voiceQueryHeardSomething();
46void failedCB(GObject* /*src*/, const gchar * /*reason*/, gpointer dst)
48 static_cast<HudClient*>(dst)->voiceQueryFailed();
51void finishedCB(GObject* /*src*/, const gchar* query, gpointer dst)
53 static_cast<HudClient*>(dst)->voiceQueryFinished(QString::fromUtf8(query));
56void modelReadyCB(GObject* /*src*/, gpointer dst)
58 static_cast<HudClient*>(dst)->modelReady(true);
61void modelReallyReadyCB(GObject* /*src*/, gint /*position*/, gint /*removed*/, gint /*added*/, gpointer dst)
63 static_cast<HudClient*>(dst)->modelReallyReady(true);
66static void modelsChangedCB(GObject* /*src*/, gpointer dst)
68 static_cast<HudClient*>(dst)->queryModelsChanged();
71static void toolBarUpdatedCB(GObject* /*src*/, gpointer dst)
73 static_cast<HudToolBarModel*>(dst)->updatedByBackend();
76} // extern "C"
78} // namespace
80HudClient::HudClient()
82 m_results = new DeeListModel();
83 m_clientQuery = hud_client_query_new("");
84 m_toolBarModel = new HudToolBarModel(m_clientQuery);
85 m_currentActionParam = NULL;
86 m_results->setModel(hud_client_query_get_results_model(m_clientQuery));
88 g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-loading", G_CALLBACK(loadingCB), this);
89 g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-listening", G_CALLBACK(listeningCB), this);
90 g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-heard-something", G_CALLBACK(heardSomethingCB), this);
91 g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-finished", G_CALLBACK(finishedCB), this);
92 g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-failed", G_CALLBACK(failedCB), this);
93 g_signal_connect(G_OBJECT(m_clientQuery), HUD_CLIENT_QUERY_SIGNAL_MODELS_CHANGED, G_CALLBACK(modelsChangedCB), this);
94 g_signal_connect(G_OBJECT(m_clientQuery), HUD_CLIENT_QUERY_SIGNAL_TOOLBAR_UPDATED, G_CALLBACK(toolBarUpdatedCB), m_toolBarModel);
97// Terrible hack to get around GLib. GLib stores function pointers as gpointer, which violates the C and C++ spec
98// because data and function pointers may have different sizes. gcc rightfully emits a warning. There is no #pragma
99// in gcc to selectively turn off the warning, however. This hack gets around the problem, by using a union (ick) to
100// convert between the two types.
102class ToGPointer
104public:
105 ToGPointer(void (*cb)())
106 {
107 u_.cb = cb;
108 }
110 operator gpointer()
111 {
112 return u_.p;
113 }
115private:
116 union
117 {
118 void (*cb)();
119 gpointer p;
120 } u_;
121};
123#define TO_GPOINTER(cb) (ToGPointer(reinterpret_cast<void(*)()>((cb))))
125HudClient::~HudClient()
127 g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(loadingCB), this);
128 g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(listeningCB), this);
129 g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(heardSomethingCB), this);
130 g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(finishedCB), this);
131 g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(toolBarUpdatedCB), m_toolBarModel);
133 delete m_results;
134 delete m_toolBarModel;
136 g_object_unref(m_clientQuery);
139void HudClient::setQuery(const QString &new_query)
141 hud_client_query_set_query(m_clientQuery, new_query.toUtf8().constData());
144void HudClient::startVoiceQuery()
146 hud_client_query_voice_query(m_clientQuery);
149void HudClient::executeParametrizedAction(const QVariant &values)
151 updateParametrizedAction(values);
152 hud_client_param_send_commit(m_currentActionParam);
153 g_object_unref(m_currentActionParam);
154 m_currentActionParam = NULL;
155 Q_EMIT commandExecuted();
158void HudClient::updateParametrizedAction(const QVariant &values)
160 if (m_currentActionParam != NULL) {
161 const QVariantMap map = values.value<QVariantMap>();
162 GActionGroup *ag = hud_client_param_get_actions(m_currentActionParam);
164 auto it = map.begin();
165 for ( ; it != map.end(); ++it) {
166 const QString action = it.key();
167 const QVariant value = it.value();
168 const GVariantType *actionType = g_action_group_get_action_parameter_type(ag, action.toUtf8().constData());
169 if (g_variant_type_equal(actionType, G_VARIANT_TYPE_DOUBLE) && value.canConvert(QVariant::Double)) {
170 g_action_group_activate_action(ag, action.toUtf8().constData(), g_variant_new_double(value.toDouble()));
171 } else {
172 qWarning() << "Unsuported action type in HudClient::executeParametrizedAction";
173 }
174 }
175 } else {
176 qWarning() << "Got to HudClient::updateParametrizedAction with no m_currentActionParam";
177 }
180void HudClient::cancelParametrizedAction()
182 if (m_currentActionParam != NULL) {
183 hud_client_param_send_cancel(m_currentActionParam);
184 g_object_unref(m_currentActionParam);
185 m_currentActionParam = NULL;
186 }
189void HudClient::executeToolBarAction(HudClientQueryToolbarItems action)
191 hud_client_query_execute_toolbar_item(m_clientQuery, action, /* timestamp */ 0);
192 Q_EMIT commandExecuted();
195DeeListModel *HudClient::results() const
197 return m_results;
200QAbstractItemModel *HudClient::toolBarModel() const
202 return m_toolBarModel;
205void HudClient::executeCommand(int index)
207 m_currentActionIndex = index;
208 DeeModel *model = hud_client_query_get_results_model(m_clientQuery);
209 DeeModelIter *iter = dee_model_get_iter_at_row(model, index);
211 GVariant *command_key = dee_model_get_value(model, iter, 0);
212 GVariant *is_parametrized = dee_model_get_value(model, iter, 7);
213 if (g_variant_get_boolean(is_parametrized)) {
214 m_currentActionParam = hud_client_query_execute_param_command(m_clientQuery, command_key, /* timestamp */ 0);
215 if (m_currentActionParam != NULL) {
216 GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam);
217 if (menuModel == NULL) {
218 g_signal_connect(m_currentActionParam, HUD_CLIENT_PARAM_SIGNAL_MODEL_READY, G_CALLBACK(modelReadyCB), this);
219 } else {
220 modelReady(false);
221 }
222 } else {
223 qWarning() << "HudClient::executeCommand::Could not get the HudClientParam for parametrized action with index" << index;
224 }
225 } else {
226 hud_client_query_execute_command(m_clientQuery, command_key, /* timestamp */ 0);
227 Q_EMIT commandExecuted();
228 }
229 g_variant_unref(command_key);
230 g_variant_unref(is_parametrized);
233void HudClient::modelReady(bool needDisconnect)
235 if (needDisconnect) {
236 g_signal_handlers_disconnect_by_func(m_currentActionParam, TO_GPOINTER(modelReadyCB), this);
237 }
238 GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam);
239 if (g_menu_model_get_n_items(menuModel) == 0) {
240 g_signal_connect(menuModel, "items-changed", G_CALLBACK(modelReallyReadyCB), this);
241 } else {
242 modelReallyReady(false);
243 }
246static QVariant QVariantFromGVariant(GVariant *value)
248 // Only handle the cases we care for now
249 switch (g_variant_classify(value)) {
250 case G_VARIANT_CLASS_BOOLEAN:
251 return QVariant((bool) g_variant_get_boolean(value));
252 case G_VARIANT_CLASS_DOUBLE:
253 return QVariant(g_variant_get_double(value));
254 case G_VARIANT_CLASS_STRING:
255 return QVariant(QString::fromUtf8(g_variant_get_string(value, NULL)));
256 default:
257 return QVariant();
258 }
261static void addAttribute(QVariantMap &properties, GMenuModel *menuModel, int item, const char *attribute) {
262 GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, NULL);
264 if (v == NULL)
265 return;
267 properties.insert(attribute, QVariantFromGVariant(v));
268 g_variant_unref(v);
271void HudClient::modelReallyReady(bool needDisconnect)
273 GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam);
274 if (needDisconnect) {
275 g_signal_handlers_disconnect_by_func(menuModel, TO_GPOINTER(modelReallyReadyCB), this);
276 }
278 QVariantList items;
279 for (int i = 0; i < g_menu_model_get_n_items(menuModel); i++) {
280 GVariant *v = g_menu_model_get_item_attribute_value(menuModel, i, "parameter-type", G_VARIANT_TYPE_STRING);
282 if (v == NULL)
283 continue;
285 const QString type = QString::fromUtf8(g_variant_get_string(v, NULL));
286 if (type == "slider") {
287 const char *sliderAttributes[] = { "label", "min", "max", "step", "value", "live", "action" };
288 QVariantMap properties;
289 properties.insert("parameter-type", "slider");
290 for (uint j = 0; j < sizeof(sliderAttributes)/sizeof(sliderAttributes[0]); ++j) {
291 addAttribute(properties, menuModel, i, sliderAttributes[j]);
292 }
293 items << properties;
294 }
295 g_variant_unref(v);
296 }
298 DeeModel *model = hud_client_query_get_results_model(m_clientQuery);
299 DeeModelIter *iter = dee_model_get_iter_at_row(model, m_currentActionIndex);
300 GVariant *actionTextVariant = dee_model_get_value(model, iter, 1);
301 const QString actionText = QString::fromUtf8(g_variant_get_string(actionTextVariant, NULL));
302 g_variant_unref(actionTextVariant);
303 Q_EMIT showParametrizedAction(actionText, QVariant::fromValue(items));
306void HudClient::queryModelsChanged()
308 m_results->setModel(hud_client_query_get_results_model(m_clientQuery));