/* * Copyright (C) 2012, 2013 Canonical, Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "hudclient.h" #include "hudtoolbarmodel.h" #include #include namespace { extern "C" { void loadingCB(GObject* /*src*/, gpointer dst) { static_cast(dst)->voiceQueryLoading(); } void listeningCB(GObject* /*src*/, gpointer dst) { static_cast(dst)->voiceQueryListening(); } void heardSomethingCB(GObject* /*src*/, gpointer dst) { static_cast(dst)->voiceQueryHeardSomething(); } void failedCB(GObject* /*src*/, const gchar * /*reason*/, gpointer dst) { static_cast(dst)->voiceQueryFailed(); } void finishedCB(GObject* /*src*/, const gchar* query, gpointer dst) { static_cast(dst)->voiceQueryFinished(QString::fromUtf8(query)); } void modelReadyCB(GObject* /*src*/, gpointer dst) { static_cast(dst)->modelReady(true); } void modelReallyReadyCB(GObject* /*src*/, gint /*position*/, gint /*removed*/, gint /*added*/, gpointer dst) { static_cast(dst)->modelReallyReady(true); } static void modelsChangedCB(GObject* /*src*/, gpointer dst) { static_cast(dst)->queryModelsChanged(); } static void toolBarUpdatedCB(GObject* /*src*/, gpointer dst) { static_cast(dst)->updatedByBackend(); } } // extern "C" } // namespace HudClient::HudClient() { m_results = new DeeListModel(); m_clientQuery = hud_client_query_new(""); m_toolBarModel = new HudToolBarModel(m_clientQuery); m_currentActionParam = nullptr; m_results->setModel(hud_client_query_get_results_model(m_clientQuery)); g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-loading", G_CALLBACK(loadingCB), this); g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-listening", G_CALLBACK(listeningCB), this); g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-heard-something", G_CALLBACK(heardSomethingCB), this); g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-finished", G_CALLBACK(finishedCB), this); g_signal_connect(G_OBJECT(m_clientQuery), "voice-query-failed", G_CALLBACK(failedCB), this); g_signal_connect(G_OBJECT(m_clientQuery), HUD_CLIENT_QUERY_SIGNAL_MODELS_CHANGED, G_CALLBACK(modelsChangedCB), this); g_signal_connect(G_OBJECT(m_clientQuery), HUD_CLIENT_QUERY_SIGNAL_TOOLBAR_UPDATED, G_CALLBACK(toolBarUpdatedCB), m_toolBarModel); } // Terrible hack to get around GLib. GLib stores function pointers as gpointer, which violates the C and C++ spec // because data and function pointers may have different sizes. gcc rightfully emits a warning. There is no #pragma // in gcc to selectively turn off the warning, however. This hack gets around the problem, by using a union (ick) to // convert between the two types. class ToGPointer { public: ToGPointer(void (*cb)()) { u_.cb = cb; } operator gpointer() { return u_.p; } private: union { void (*cb)(); gpointer p; } u_; }; #define TO_GPOINTER(cb) (ToGPointer(reinterpret_cast((cb)))) HudClient::~HudClient() { g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(loadingCB), this); g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(listeningCB), this); g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(heardSomethingCB), this); g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(finishedCB), this); g_signal_handlers_disconnect_by_func(G_OBJECT(m_clientQuery), TO_GPOINTER(toolBarUpdatedCB), m_toolBarModel); delete m_results; delete m_toolBarModel; g_object_unref(m_clientQuery); } void HudClient::setQuery(const QString &new_query) { hud_client_query_set_query(m_clientQuery, new_query.toUtf8().constData()); } void HudClient::startVoiceQuery() { hud_client_query_voice_query(m_clientQuery); } void HudClient::executeParametrizedAction(const QVariant &values) { updateParametrizedAction(values); hud_client_param_send_commit(m_currentActionParam); g_object_unref(m_currentActionParam); m_currentActionParam = nullptr; Q_EMIT commandExecuted(); } void HudClient::updateParametrizedAction(const QVariant &values) { if (m_currentActionParam != nullptr) { const QVariantMap map = values.value(); GActionGroup *ag = hud_client_param_get_actions(m_currentActionParam); auto it = map.begin(); for ( ; it != map.end(); ++it) { const QString action = it.key(); const QVariant value = it.value(); const GVariantType *actionType = g_action_group_get_action_parameter_type(ag, action.toUtf8().constData()); if (g_variant_type_equal(actionType, G_VARIANT_TYPE_DOUBLE) && value.canConvert(QVariant::Double)) { g_action_group_activate_action(ag, action.toUtf8().constData(), g_variant_new_double(value.toDouble())); } else { qWarning() << "Unsuported action type in HudClient::executeParametrizedAction"; } } } else { qWarning() << "Got to HudClient::updateParametrizedAction with no m_currentActionParam"; } } void HudClient::cancelParametrizedAction() { if (m_currentActionParam != nullptr) { hud_client_param_send_cancel(m_currentActionParam); g_object_unref(m_currentActionParam); m_currentActionParam = nullptr; } } void HudClient::executeToolBarAction(HudClientQueryToolbarItems action) { hud_client_query_execute_toolbar_item(m_clientQuery, action, /* timestamp */ 0); Q_EMIT commandExecuted(); } DeeListModel *HudClient::results() const { return m_results; } QAbstractItemModel *HudClient::toolBarModel() const { return m_toolBarModel; } void HudClient::executeCommand(int index) { m_currentActionIndex = index; DeeModel *model = hud_client_query_get_results_model(m_clientQuery); DeeModelIter *iter = dee_model_get_iter_at_row(model, index); GVariant *command_key = dee_model_get_value(model, iter, 0); GVariant *is_parametrized = dee_model_get_value(model, iter, 7); if (g_variant_get_boolean(is_parametrized)) { m_currentActionParam = hud_client_query_execute_param_command(m_clientQuery, command_key, /* timestamp */ 0); if (m_currentActionParam != nullptr) { GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam); if (menuModel == nullptr) { g_signal_connect(m_currentActionParam, HUD_CLIENT_PARAM_SIGNAL_MODEL_READY, G_CALLBACK(modelReadyCB), this); } else { modelReady(false); } } else { qWarning() << "HudClient::executeCommand::Could not get the HudClientParam for parametrized action with index" << index; } } else { hud_client_query_execute_command(m_clientQuery, command_key, /* timestamp */ 0); Q_EMIT commandExecuted(); } g_variant_unref(command_key); g_variant_unref(is_parametrized); } void HudClient::modelReady(bool needDisconnect) { if (needDisconnect) { g_signal_handlers_disconnect_by_func(m_currentActionParam, TO_GPOINTER(modelReadyCB), this); } GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam); if (g_menu_model_get_n_items(menuModel) == 0) { g_signal_connect(menuModel, "items-changed", G_CALLBACK(modelReallyReadyCB), this); } else { modelReallyReady(false); } } static void addAttribute(QVariantMap &properties, GMenuModel *menuModel, int item, const char *attribute) { GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, nullptr); if (v == nullptr) return; properties.insert(attribute, DeeListModel::VariantForData(v)); g_variant_unref(v); } void HudClient::modelReallyReady(bool needDisconnect) { GMenuModel *menuModel = hud_client_param_get_model (m_currentActionParam); if (needDisconnect) { g_signal_handlers_disconnect_by_func(menuModel, TO_GPOINTER(modelReallyReadyCB), this); } QVariantList items; for (int i = 0; i < g_menu_model_get_n_items(menuModel); i++) { GVariant *v = g_menu_model_get_item_attribute_value(menuModel, i, "parameter-type", G_VARIANT_TYPE_STRING); if (v == nullptr) continue; const QString type = QString::fromUtf8(g_variant_get_string(v, nullptr)); if (type == "slider") { const char *sliderAttributes[] = { "label", "min", "max", "step", "value", "live", "action" }; QVariantMap properties; properties.insert("parameter-type", "slider"); for (uint j = 0; j < sizeof(sliderAttributes)/sizeof(sliderAttributes[0]); ++j) { addAttribute(properties, menuModel, i, sliderAttributes[j]); } items << properties; } g_variant_unref(v); } DeeModel *model = hud_client_query_get_results_model(m_clientQuery); DeeModelIter *iter = dee_model_get_iter_at_row(model, m_currentActionIndex); GVariant *actionTextVariant = dee_model_get_value(model, iter, 1); const QString actionText = QString::fromUtf8(g_variant_get_string(actionTextVariant, nullptr)); g_variant_unref(actionTextVariant); Q_EMIT showParametrizedAction(actionText, QVariant::fromValue(items)); } void HudClient::queryModelsChanged() { m_results->setModel(hud_client_query_get_results_model(m_clientQuery)); }