~elementary-os/elementaryos/os-patch-gnome-bluetooth-bionic

« back to all changes in this revision

Viewing changes to lib/bluetooth-chooser-button.c

  • Committer: RabbitBot
  • Date: 2018-02-05 12:57:34 UTC
  • Revision ID: rabbitbot@elementary.io-20180205125734-a49s78k7asb5pokc
Initial import, version 3.26.1-3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * (C) Copyright 2007-2009 Bastien Nocera <hadess@hadess.net>
 
4
 *
 
5
 * This library is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU Library General Public
 
7
 * License as published by the Free Software Foundation; either
 
8
 * version 2 of the License, or (at your option) any later version.
 
9
 *
 
10
 * This library is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * Library General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU Library General Public
 
16
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
/**
 
20
 * SECTION:bluetooth-chooser-button
 
21
 * @short_description: a Bluetooth chooser button
 
22
 * @stability: Stable
 
23
 * @include: bluetooth-chooser-button.h
 
24
 *
 
25
 * A button used to select Bluetooth devices which will pop-up a
 
26
 * #BluetoothChooser widget inside a dialogue when clicked.
 
27
 **/
 
28
 
 
29
#ifdef HAVE_CONFIG_H
 
30
#include "config.h"
 
31
#endif
 
32
 
 
33
#include <glib/gi18n.h>
 
34
#include <gtk/gtk.h>
 
35
 
 
36
#include "bluetooth-chooser-button.h"
 
37
#include "bluetooth-client.h"
 
38
#include "bluetooth-chooser.h"
 
39
#include "bluetooth-utils.h"
 
40
 
 
41
struct _BluetoothChooserButton {
 
42
        GtkButton          parent;
 
43
 
 
44
        BluetoothClient   *client;
 
45
        GtkWidget         *image;
 
46
        GtkWidget         *dialog;
 
47
        GtkWidget         *chooser;
 
48
        char              *bdaddr;
 
49
        guint              is_available : 1;
 
50
        guint              has_selection : 1;
 
51
};
 
52
 
 
53
enum {
 
54
        PROP_0,
 
55
        PROP_DEVICE,
 
56
        PROP_IS_AVAILABLE,
 
57
};
 
58
 
 
59
enum {
 
60
        CHOOSER_CREATED,
 
61
        LAST_SIGNAL
 
62
};
 
63
 
 
64
static int signals[LAST_SIGNAL] = { 0 };
 
65
 
 
66
static void     bluetooth_chooser_button_class_init     (BluetoothChooserButtonClass * klass);
 
67
static void     bluetooth_chooser_button_init           (BluetoothChooserButton      * button);
 
68
 
 
69
G_DEFINE_TYPE(BluetoothChooserButton, bluetooth_chooser_button, GTK_TYPE_BUTTON);
 
70
 
 
71
#define DEFAULT_STR N_("Click to select device…")
 
72
 
 
73
static void
 
74
set_btdevname (BluetoothChooserButton *button, const char *bdaddr, const char *name, const char *icon)
 
75
{
 
76
        char *found_name, *found_icon;
 
77
 
 
78
        found_name = NULL;
 
79
        found_icon = NULL;
 
80
 
 
81
        if (bdaddr != NULL && (name == NULL || icon == NULL)) {
 
82
                GtkTreeModel *model;
 
83
                GtkTreeIter iter;
 
84
                gboolean cont = FALSE;
 
85
 
 
86
                model = bluetooth_client_get_device_model (button->client);
 
87
                if (model != NULL) {
 
88
                        cont = gtk_tree_model_iter_children (GTK_TREE_MODEL(model),
 
89
                                                             &iter, NULL);
 
90
                }
 
91
 
 
92
                while (cont == TRUE) {
 
93
                        char *value;
 
94
 
 
95
                        gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
 
96
                                           BLUETOOTH_COLUMN_ADDRESS, &value, -1);
 
97
                        if (g_ascii_strcasecmp(bdaddr, value) == 0) {
 
98
                                gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
 
99
                                                   BLUETOOTH_COLUMN_ALIAS, &found_name,
 
100
                                                   BLUETOOTH_COLUMN_ICON, &found_icon,
 
101
                                                   -1);
 
102
                                g_free (value);
 
103
                                break;
 
104
                        }
 
105
                        g_free (value);
 
106
                        cont = gtk_tree_model_iter_next (GTK_TREE_MODEL(model), &iter);
 
107
                }
 
108
 
 
109
                if (model != NULL)
 
110
                        g_object_unref (model);
 
111
 
 
112
                if (found_name == NULL) {
 
113
                        found_name = g_strdup (bdaddr);
 
114
                        g_strdelimit (found_name, ":", '-');
 
115
                }
 
116
                if (found_icon == NULL)
 
117
                        found_icon = g_strdup ("bluetooth");
 
118
        }
 
119
 
 
120
        if (bdaddr != NULL) {
 
121
                /* Update the name */
 
122
                if (name == NULL)
 
123
                        gtk_button_set_label (GTK_BUTTON (button), found_name);
 
124
                else
 
125
                        gtk_button_set_label (GTK_BUTTON (button), name);
 
126
                /* And the icon */
 
127
                if (icon == NULL)
 
128
                        gtk_image_set_from_icon_name (GTK_IMAGE (button->image), found_icon, GTK_ICON_SIZE_MENU);
 
129
                else
 
130
                        gtk_image_set_from_icon_name (GTK_IMAGE (button->image), icon, GTK_ICON_SIZE_MENU);
 
131
 
 
132
                /* And our copy of the address, and notify if it's actually changed */
 
133
                if (button->bdaddr == NULL || strcmp (bdaddr, button->bdaddr) != 0) {
 
134
                        g_free (button->bdaddr);
 
135
                        button->bdaddr = g_strdup (bdaddr);
 
136
                        g_object_notify (G_OBJECT (button), "device");
 
137
                }
 
138
        } else {
 
139
                gtk_button_set_label (GTK_BUTTON (button), _(DEFAULT_STR));
 
140
                if (button->bdaddr != NULL) {
 
141
                        g_free (button->bdaddr);
 
142
                        button->bdaddr = NULL;
 
143
                        gtk_image_clear (GTK_IMAGE (button->image));
 
144
                        g_object_notify (G_OBJECT (button), "device");
 
145
                }
 
146
        }
 
147
 
 
148
        g_free (found_name);
 
149
        g_free (found_icon);
 
150
}
 
151
 
 
152
static void select_device_changed(BluetoothChooser *self, gchar *address, gpointer data)
 
153
{
 
154
        BluetoothChooserButton *button = BLUETOOTH_CHOOSER_BUTTON (data);
 
155
 
 
156
        button->has_selection = (address != NULL);
 
157
        gtk_dialog_set_response_sensitive(GTK_DIALOG (button->dialog), GTK_RESPONSE_ACCEPT,
 
158
                                          button->has_selection && button->is_available);
 
159
}
 
160
 
 
161
static void
 
162
dialog_response_cb (GtkDialog *dialog, int response_id, gpointer data)
 
163
{
 
164
        BluetoothChooserButton *button = BLUETOOTH_CHOOSER_BUTTON (data);
 
165
        char *bdaddr, *icon, *name;
 
166
 
 
167
        if (response_id == GTK_RESPONSE_ACCEPT) {
 
168
                BluetoothChooser *chooser = BLUETOOTH_CHOOSER (button->chooser);
 
169
                bdaddr = bluetooth_chooser_get_selected_device (chooser);
 
170
                name = bluetooth_chooser_get_selected_device_name (chooser);
 
171
                icon = bluetooth_chooser_get_selected_device_icon (chooser);
 
172
        }
 
173
 
 
174
        gtk_widget_destroy (GTK_WIDGET (dialog));
 
175
        button->dialog = NULL;
 
176
 
 
177
        if (response_id != GTK_RESPONSE_ACCEPT)
 
178
                return;
 
179
 
 
180
        set_btdevname (button, bdaddr, name, icon);
 
181
        g_free (bdaddr);
 
182
        g_free (name);
 
183
        g_free (icon);
 
184
}
 
185
 
 
186
static void
 
187
bluetooth_chooser_button_clicked (GtkButton *widget)
 
188
{
 
189
        BluetoothChooserButton *button = BLUETOOTH_CHOOSER_BUTTON (widget);
 
190
        GtkWidget *parent;
 
191
 
 
192
        if (button->dialog != NULL) {
 
193
                gtk_window_present (GTK_WINDOW (button->dialog));
 
194
                return;
 
195
        }
 
196
 
 
197
        parent = gtk_widget_get_toplevel (GTK_WIDGET (button));
 
198
        //FIXME title
 
199
        button->dialog = gtk_dialog_new_with_buttons("", GTK_WINDOW (parent),
 
200
                                                     GTK_DIALOG_MODAL,
 
201
                                                     _("_Cancel"), GTK_RESPONSE_REJECT,
 
202
                                                     _("_OK"), GTK_RESPONSE_ACCEPT, NULL);
 
203
        g_signal_connect (button->dialog, "response",
 
204
                          G_CALLBACK (dialog_response_cb), button);
 
205
        gtk_dialog_set_response_sensitive (GTK_DIALOG(button->dialog),
 
206
                                           GTK_RESPONSE_ACCEPT, FALSE);
 
207
        gtk_window_set_default_size (GTK_WINDOW(button->dialog), 480, 400);
 
208
 
 
209
        gtk_container_set_border_width (GTK_CONTAINER (button->dialog), 5);
 
210
        gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (button->dialog))), 2);
 
211
 
 
212
        /* Create the button->chooser */
 
213
        button->chooser = bluetooth_chooser_new ();
 
214
        g_signal_connect(button->chooser, "selected-device-changed",
 
215
                         G_CALLBACK(select_device_changed), button);
 
216
        g_signal_emit (G_OBJECT (button),
 
217
                       signals[CHOOSER_CREATED],
 
218
                       0, button->chooser);
 
219
        g_object_set (G_OBJECT (button->chooser), "device-selected", button->bdaddr, NULL);
 
220
        gtk_container_set_border_width (GTK_CONTAINER(button->chooser), 5);
 
221
        gtk_widget_show (button->chooser);
 
222
        gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (button->dialog))), button->chooser,
 
223
                            TRUE, TRUE, 0);
 
224
 
 
225
        gtk_widget_show (button->dialog);
 
226
}
 
227
 
 
228
static void
 
229
default_adapter_changed (GObject *object, GParamSpec *pspec, gpointer data)
 
230
{
 
231
        BluetoothChooserButton *button = BLUETOOTH_CHOOSER_BUTTON (data);
 
232
        char *adapter;
 
233
        gboolean powered;
 
234
 
 
235
        g_object_get (G_OBJECT (button->client),
 
236
                      "default-adapter", &adapter,
 
237
                      "default-adapter-powered", &powered,
 
238
                      NULL);
 
239
        if (adapter != NULL)
 
240
                button->is_available = powered;
 
241
        else
 
242
                button->is_available = FALSE;
 
243
 
 
244
        if (adapter != NULL && button->bdaddr != NULL)
 
245
                set_btdevname (button, button->bdaddr, NULL, NULL);
 
246
        g_free (adapter);
 
247
 
 
248
        if (button->dialog != NULL)
 
249
                gtk_dialog_set_response_sensitive (GTK_DIALOG (button->dialog), GTK_RESPONSE_ACCEPT,
 
250
                                                   button->has_selection && button->is_available);
 
251
 
 
252
        g_object_notify (G_OBJECT (button), "is-available");
 
253
}
 
254
 
 
255
static void
 
256
bluetooth_chooser_button_finalize (GObject *object)
 
257
{
 
258
        BluetoothChooserButton *button = BLUETOOTH_CHOOSER_BUTTON (object);
 
259
 
 
260
        g_clear_object (&button->client);
 
261
 
 
262
        if (button->dialog != NULL) {
 
263
                gtk_widget_destroy (button->dialog);
 
264
                button->dialog = NULL;
 
265
                button->chooser = NULL;
 
266
        }
 
267
 
 
268
        G_OBJECT_CLASS (bluetooth_chooser_button_parent_class)->finalize (object);
 
269
}
 
270
 
 
271
static void
 
272
bluetooth_chooser_button_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
 
273
{
 
274
        BluetoothChooserButton *button;
 
275
 
 
276
        g_return_if_fail (BLUETOOTH_IS_CHOOSER_BUTTON (object));
 
277
        button = BLUETOOTH_CHOOSER_BUTTON (object);
 
278
 
 
279
        switch (property_id) {
 
280
        case PROP_DEVICE: {
 
281
                const char *str = g_value_get_string (value);
 
282
                g_return_if_fail (str == NULL || bluetooth_verify_address (str));
 
283
                set_btdevname (button, str, NULL, NULL);
 
284
                break;
 
285
                }
 
286
        default:
 
287
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
288
        }
 
289
}
 
290
 
 
291
static void
 
292
bluetooth_chooser_button_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
 
293
{
 
294
        BluetoothChooserButton *button;
 
295
 
 
296
        g_return_if_fail (BLUETOOTH_IS_CHOOSER_BUTTON (object));
 
297
        button = BLUETOOTH_CHOOSER_BUTTON (object);
 
298
 
 
299
        switch (property_id) {
 
300
        case PROP_DEVICE:
 
301
                g_value_set_string (value, button->bdaddr);
 
302
                break;
 
303
        case PROP_IS_AVAILABLE:
 
304
                g_value_set_boolean (value, button->is_available);
 
305
                break;
 
306
        default:
 
307
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
308
        }
 
309
}
 
310
 
 
311
static void
 
312
bluetooth_chooser_button_class_init (BluetoothChooserButtonClass *klass)
 
313
{
 
314
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
315
        GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
 
316
 
 
317
        object_class->finalize = bluetooth_chooser_button_finalize;
 
318
        object_class->set_property = bluetooth_chooser_button_set_property;
 
319
        object_class->get_property = bluetooth_chooser_button_get_property;
 
320
 
 
321
        button_class->clicked = bluetooth_chooser_button_clicked;
 
322
 
 
323
        /**
 
324
         * BluetoothChooserButton::chooser-created:
 
325
         * @self: a #BluetoothChooserButton widget
 
326
         * @chooser: a #BluetoothChooser widget
 
327
         *
 
328
         * The signal is sent when a popup dialogue is created for the user to select
 
329
         * a device. This signal allows you to change the configuration and filtering
 
330
         * of the tree from its defaults.
 
331
         **/
 
332
        signals[CHOOSER_CREATED] =
 
333
                g_signal_new ("chooser-created",
 
334
                              G_TYPE_FROM_CLASS (klass),
 
335
                              G_SIGNAL_RUN_LAST,
 
336
                              G_STRUCT_OFFSET (BluetoothChooserButtonClass, chooser_created),
 
337
                              NULL, NULL,
 
338
                              g_cclosure_marshal_VOID__OBJECT,
 
339
                              G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
340
 
 
341
        /**
 
342
         * BluetoothChooserButton:device:
 
343
         *
 
344
         * The Bluetooth address of the selected device or %NULL.
 
345
         **/
 
346
        g_object_class_install_property (object_class, PROP_DEVICE,
 
347
                                         g_param_spec_string ("device", "Device", "The Bluetooth address of the selected device.",
 
348
                                                              NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
349
        /**
 
350
         * BluetoothChooserButton:is-available:
 
351
         *
 
352
         * %TRUE if there is a powered Bluetooth adapter available.
 
353
         *
 
354
         * See also: bluetooth_chooser_button_available()
 
355
         **/
 
356
        g_object_class_install_property (object_class, PROP_IS_AVAILABLE,
 
357
                                         g_param_spec_boolean ("is-available", "Bluetooth is available", "Whether Bluetooth is available.",
 
358
                                                               TRUE, G_PARAM_READABLE));
 
359
}
 
360
 
 
361
static void
 
362
bluetooth_chooser_button_init (BluetoothChooserButton *button)
 
363
{
 
364
        gtk_button_set_label (GTK_BUTTON (button), _(DEFAULT_STR));
 
365
 
 
366
        button->image = gtk_image_new ();
 
367
        gtk_button_set_image (GTK_BUTTON (button), button->image);
 
368
 
 
369
        button->bdaddr = NULL;
 
370
        button->dialog = NULL;
 
371
 
 
372
        button->client = bluetooth_client_new ();
 
373
        g_signal_connect (G_OBJECT (button->client), "notify::default-adapter",
 
374
                          G_CALLBACK (default_adapter_changed), button);
 
375
        g_signal_connect (G_OBJECT (button->client), "notify::default-adapter-powered",
 
376
                          G_CALLBACK (default_adapter_changed), button);
 
377
 
 
378
        /* And set the default value already */
 
379
        default_adapter_changed (NULL, NULL, button);
 
380
}
 
381
 
 
382
/**
 
383
 * bluetooth_chooser_button_new:
 
384
 *
 
385
 * Returns a new #BluetoothChooserButton widget.
 
386
 *
 
387
 * Return value: a #BluetoothChooserButton widget.
 
388
 **/
 
389
GtkWidget *
 
390
bluetooth_chooser_button_new (void)
 
391
{
 
392
        return g_object_new (BLUETOOTH_TYPE_CHOOSER_BUTTON,
 
393
                             NULL);
 
394
}
 
395
 
 
396
/**
 
397
 * bluetooth_chooser_button_available:
 
398
 * @button: a #BluetoothChooserButton
 
399
 *
 
400
 * Returns whether there is a powered Bluetooth adapter.
 
401
 *
 
402
 * Return value: %TRUE if there is a powered Bluetooth adapter available, and the button should be sensitive.
 
403
 **/
 
404
gboolean
 
405
bluetooth_chooser_button_available (BluetoothChooserButton *button)
 
406
{
 
407
        g_return_val_if_fail (BLUETOOTH_IS_CHOOSER_BUTTON (button), FALSE);
 
408
 
 
409
        return button->is_available;
 
410
}
 
411