~gerboland/unity/8-refactor-wm-and-test

« back to all changes in this revision

Viewing changes to plugins/HudClient/hudclient.cpp

  • Committer: Michał Sawicz
  • Date: 2013-06-05 22:03:08 UTC
  • Revision ID: michal.sawicz@canonical.com-20130605220308-yny8fv3futtr04fg
Inital unity8 commit.

Previous history can be found at https://code.launchpad.net/~unity-team/unity/phablet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 = NULL;
 
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 = NULL;
 
155
    Q_EMIT commandExecuted();
 
156
}
 
157
 
 
158
void HudClient::updateParametrizedAction(const QVariant &values)
 
159
{
 
160
    if (m_currentActionParam != NULL) {
 
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 != NULL) {
 
183
        hud_client_param_send_cancel(m_currentActionParam);
 
184
        g_object_unref(m_currentActionParam);
 
185
        m_currentActionParam = NULL;
 
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 != 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);
 
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 QVariant QVariantFromGVariant(GVariant *value)
 
247
{
 
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
    }
 
259
}
 
260
 
 
261
static 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);
 
263
 
 
264
    if (v == NULL)
 
265
        return;
 
266
 
 
267
    properties.insert(attribute, QVariantFromGVariant(v));
 
268
    g_variant_unref(v);
 
269
}
 
270
 
 
271
void HudClient::modelReallyReady(bool needDisconnect)
 
272
{
 
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
    }
 
277
 
 
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);
 
281
 
 
282
        if (v == NULL)
 
283
            continue;
 
284
 
 
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
    }
 
297
 
 
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));
 
304
}
 
305
 
 
306
void HudClient::queryModelsChanged()
 
307
{
 
308
    m_results->setModel(hud_client_query_get_results_model(m_clientQuery));
 
309
}