Unity 8
 All Classes Functions
hudclient.cpp
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  */
16 
17 #include "hudclient.h"
18 
19 #include "hudtoolbarmodel.h"
20 
21 #include <deelistmodel.h>
22 
23 #include <QDebug>
24 
25 namespace
26 {
27 
28 extern "C"
29 {
30 
31 void loadingCB(GObject* /*src*/, gpointer dst)
32 {
33  static_cast<HudClient*>(dst)->voiceQueryLoading();
34 }
35 
36 void listeningCB(GObject* /*src*/, gpointer dst)
37 {
38  static_cast<HudClient*>(dst)->voiceQueryListening();
39 }
40 
41 void heardSomethingCB(GObject* /*src*/, gpointer dst)
42 {
43  static_cast<HudClient*>(dst)->voiceQueryHeardSomething();
44 }
45 
46 void failedCB(GObject* /*src*/, const gchar * /*reason*/, gpointer dst)
47 {
48  static_cast<HudClient*>(dst)->voiceQueryFailed();
49 }
50 
51 void finishedCB(GObject* /*src*/, const gchar* query, gpointer dst)
52 {
53  static_cast<HudClient*>(dst)->voiceQueryFinished(QString::fromUtf8(query));
54 }
55 
56 void modelReadyCB(GObject* /*src*/, gpointer dst)
57 {
58  static_cast<HudClient*>(dst)->modelReady(true);
59 }
60 
61 void modelReallyReadyCB(GObject* /*src*/, gint /*position*/, gint /*removed*/, gint /*added*/, gpointer dst)
62 {
63  static_cast<HudClient*>(dst)->modelReallyReady(true);
64 }
65 
66 static void modelsChangedCB(GObject* /*src*/, gpointer dst)
67 {
68  static_cast<HudClient*>(dst)->queryModelsChanged();
69 }
70 
71 static void toolBarUpdatedCB(GObject* /*src*/, gpointer dst)
72 {
73  static_cast<HudToolBarModel*>(dst)->updatedByBackend();
74 }
75 
76 } // extern "C"
77 
78 } // namespace
79 
80 HudClient::HudClient()
81 {
82  m_results = new DeeListModel();
83  m_clientQuery = hud_client_query_new("");
84  m_toolBarModel = new HudToolBarModel(m_clientQuery);
85  m_currentActionParam = nullptr;
86  m_results->setModel(hud_client_query_get_results_model(m_clientQuery));
87 
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);
95 }
96 
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.
101 
102 class ToGPointer
103 {
104 public:
105  ToGPointer(void (*cb)())
106  {
107  u_.cb = cb;
108  }
109 
110  operator gpointer()
111  {
112  return u_.p;
113  }
114 
115 private:
116  union
117  {
118  void (*cb)();
119  gpointer p;
120  } u_;
121 };
122 
123 #define TO_GPOINTER(cb) (ToGPointer(reinterpret_cast<void(*)()>((cb))))
124 
125 HudClient::~HudClient()
126 {
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);
132 
133  delete m_results;
134  delete m_toolBarModel;
135 
136  g_object_unref(m_clientQuery);
137 }
138 
139 void HudClient::setQuery(const QString &new_query)
140 {
141  hud_client_query_set_query(m_clientQuery, new_query.toUtf8().constData());
142 }
143 
144 void HudClient::startVoiceQuery()
145 {
146  hud_client_query_voice_query(m_clientQuery);
147 }
148 
149 void HudClient::executeParametrizedAction(const QVariant &values)
150 {
151  updateParametrizedAction(values);
152  hud_client_param_send_commit(m_currentActionParam);
153  g_object_unref(m_currentActionParam);
154  m_currentActionParam = nullptr;
155  Q_EMIT commandExecuted();
156 }
157 
158 void HudClient::updateParametrizedAction(const QVariant &values)
159 {
160  if (m_currentActionParam != nullptr) {
161  const QVariantMap map = values.value<QVariantMap>();
162  GActionGroup *ag = hud_client_param_get_actions(m_currentActionParam);
163 
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  }
178 }
179 
180 void HudClient::cancelParametrizedAction()
181 {
182  if (m_currentActionParam != nullptr) {
183  hud_client_param_send_cancel(m_currentActionParam);
184  g_object_unref(m_currentActionParam);
185  m_currentActionParam = nullptr;
186  }
187 }
188 
189 void HudClient::executeToolBarAction(HudClientQueryToolbarItems action)
190 {
191  hud_client_query_execute_toolbar_item(m_clientQuery, action, /* timestamp */ 0);
192  Q_EMIT commandExecuted();
193 }
194 
195 DeeListModel *HudClient::results() const
196 {
197  return m_results;
198 }
199 
200 QAbstractItemModel *HudClient::toolBarModel() const
201 {
202  return m_toolBarModel;
203 }
204 
205 void HudClient::executeCommand(int index)
206 {
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);
210 
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 != nullptr) {
216  GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam);
217  if (menuModel == nullptr) {
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);
231 }
232 
233 void HudClient::modelReady(bool needDisconnect)
234 {
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  }
244 }
245 
246 static void addAttribute(QVariantMap &properties, GMenuModel *menuModel, int item, const char *attribute) {
247  GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, nullptr);
248 
249  if (v == nullptr)
250  return;
251 
252  properties.insert(attribute, DeeListModel::VariantForData(v));
253  g_variant_unref(v);
254 }
255 
256 void HudClient::modelReallyReady(bool needDisconnect)
257 {
258  GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam);
259  if (needDisconnect) {
260  g_signal_handlers_disconnect_by_func(menuModel, TO_GPOINTER(modelReallyReadyCB), this);
261  }
262 
263  QVariantList items;
264  for (int i = 0; i < g_menu_model_get_n_items(menuModel); i++) {
265  GVariant *v = g_menu_model_get_item_attribute_value(menuModel, i, "parameter-type", G_VARIANT_TYPE_STRING);
266 
267  if (v == nullptr)
268  continue;
269 
270  const QString type = QString::fromUtf8(g_variant_get_string(v, nullptr));
271  if (type == "slider") {
272  const char *sliderAttributes[] = { "label", "min", "max", "step", "value", "live", "action" };
273  QVariantMap properties;
274  properties.insert("parameter-type", "slider");
275  for (uint j = 0; j < sizeof(sliderAttributes)/sizeof(sliderAttributes[0]); ++j) {
276  addAttribute(properties, menuModel, i, sliderAttributes[j]);
277  }
278  items << properties;
279  }
280  g_variant_unref(v);
281  }
282 
283  DeeModel *model = hud_client_query_get_results_model(m_clientQuery);
284  DeeModelIter *iter = dee_model_get_iter_at_row(model, m_currentActionIndex);
285  GVariant *actionTextVariant = dee_model_get_value(model, iter, 1);
286  const QString actionText = QString::fromUtf8(g_variant_get_string(actionTextVariant, nullptr));
287  g_variant_unref(actionTextVariant);
288  Q_EMIT showParametrizedAction(actionText, QVariant::fromValue(items));
289 }
290 
291 void HudClient::queryModelsChanged()
292 {
293  m_results->setModel(hud_client_query_get_results_model(m_clientQuery));
294 }