Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #include "config.h"
5 : :
6 : : #include <glib/gi18n-lib.h>
7 : : #include <adwaita.h>
8 : : #include <gtk/gtk.h>
9 : : #include <pango/pango.h>
10 : : #include <libvalent-core.h>
11 : : #include <libvalent-device.h>
12 : :
13 : : #include "valent-device-gadget.h"
14 : : #include "valent-device-page.h"
15 : : #include "valent-device-preferences-dialog.h"
16 : : #include "valent-menu-list.h"
17 : : #include "valent-menu-stack.h"
18 : :
19 : :
20 : : struct _ValentDevicePage
21 : : {
22 : : AdwNavigationPage parent_instance;
23 : :
24 : : ValentDevice *device;
25 : : GHashTable *plugins;
26 : : AdwDialog *preferences;
27 : :
28 : : /* template */
29 : : GtkStack *stack;
30 : :
31 : : GtkWidget *pair_request;
32 : : GtkSpinner *pair_spinner;
33 : : GtkWidget *verification_key;
34 : :
35 : : GtkWidget *gadgets;
36 : : ValentMenuStack *menu_actions;
37 : : };
38 : :
39 [ + + + - ]: 98 : G_DEFINE_FINAL_TYPE (ValentDevicePage, valent_device_page, ADW_TYPE_NAVIGATION_PAGE)
40 : :
41 : : enum {
42 : : PROP_0,
43 : : PROP_DEVICE,
44 : : N_PROPERTIES
45 : : };
46 : :
47 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
48 : :
49 : :
50 : : /*
51 : : * Plugin Callbacks
52 : : */
53 : : typedef struct
54 : : {
55 : : GtkWidget *gadgets;
56 : : GtkWidget *gadget;
57 : : } PluginData;
58 : :
59 : : static void
60 : 9 : plugin_data_free (gpointer data)
61 : : {
62 : 9 : PluginData *plugin = (PluginData *)data;
63 : :
64 [ + + + - ]: 9 : if (plugin->gadgets != NULL && plugin->gadget != NULL)
65 : 4 : gtk_box_remove (GTK_BOX (plugin->gadgets), plugin->gadget);
66 : :
67 : 9 : g_free (plugin);
68 : 9 : }
69 : :
70 : : static void
71 : 9 : valent_device_page_add_plugin (ValentDevicePage *self,
72 : : const char *module)
73 : : {
74 : 9 : PeasEngine *engine;
75 : 9 : PeasPluginInfo *info;
76 : 9 : PluginData *plugin;
77 : :
78 [ + - ]: 9 : g_assert (VALENT_IS_DEVICE_PAGE (self));
79 [ + - - + ]: 9 : g_assert (module != NULL && *module != '\0');
80 : :
81 : 9 : engine = valent_get_plugin_engine ();
82 : 9 : info = peas_engine_get_plugin_info (engine, module);
83 : 9 : plugin = g_new0 (PluginData, 1);
84 : :
85 : : /* Gadgets (eg. HeaderBar widgets) */
86 [ + + ]: 9 : if (peas_engine_provides_extension (engine, info, VALENT_TYPE_DEVICE_GADGET))
87 : : {
88 : 4 : GObject *gadget;
89 : :
90 : 4 : gadget = peas_engine_create_extension (engine,
91 : : info,
92 : : VALENT_TYPE_DEVICE_GADGET,
93 : : "device", self->device,
94 : : NULL);
95 : :
96 [ + - ]: 4 : if (gadget != NULL)
97 : : {
98 : 4 : gtk_box_append (GTK_BOX (self->gadgets), GTK_WIDGET (gadget));
99 : 4 : plugin->gadgets = GTK_WIDGET (self->gadgets);
100 : 4 : plugin->gadget = GTK_WIDGET (gadget);
101 : : }
102 : : }
103 : :
104 [ - + ]: 9 : g_hash_table_replace (self->plugins,
105 : 9 : g_strdup (module),
106 : : g_steal_pointer (&plugin));
107 : 9 : }
108 : :
109 : : static void
110 : 6 : on_plugins_changed (ValentDevice *device,
111 : : GParamSpec *pspec,
112 : : ValentDevicePage *self)
113 : : {
114 : 12 : g_auto (GStrv) plugins = NULL;
115 : 6 : GHashTableIter iter;
116 : 6 : const char *module;
117 : :
118 : 6 : plugins = valent_device_get_plugins (device);
119 : 6 : g_hash_table_iter_init (&iter, self->plugins);
120 : :
121 [ + + ]: 14 : while (g_hash_table_iter_next (&iter, (void **)&module, NULL))
122 : : {
123 [ + + ]: 2 : if (!g_strv_contains ((const char * const *)plugins, module))
124 : 1 : g_hash_table_iter_remove (&iter);
125 : : }
126 : :
127 [ + + ]: 16 : for (unsigned int i = 0; plugins[i] != NULL; i++)
128 : : {
129 [ + + ]: 10 : if (!g_hash_table_contains (self->plugins, plugins[i]))
130 : 9 : valent_device_page_add_plugin (self, plugins[i]);
131 : : }
132 : 6 : }
133 : :
134 : : /*
135 : : * Pairing
136 : : */
137 : : static void
138 : 9 : on_state_changed (ValentDevice *device,
139 : : GParamSpec *pspec,
140 : : ValentDevicePage *self)
141 : : {
142 : 9 : ValentDeviceState state = VALENT_DEVICE_STATE_NONE;
143 : 9 : gboolean connected, paired;
144 : :
145 [ + - ]: 9 : g_assert (VALENT_IS_DEVICE (device));
146 [ - + ]: 9 : g_assert (VALENT_IS_DEVICE_PAGE (self));
147 : :
148 : 9 : state = valent_device_get_state (self->device);
149 : 9 : connected = (state & VALENT_DEVICE_STATE_CONNECTED) != 0;
150 : 9 : paired = (state & VALENT_DEVICE_STATE_PAIRED) != 0;
151 : :
152 : : /* Ensure the proper controls are displayed */
153 : 9 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "page.pair", !paired);
154 : 9 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "page.unpair", paired);
155 : :
156 [ + + ]: 9 : if (!connected)
157 : : {
158 : 7 : gtk_stack_set_visible_child_name (self->stack, "disconnected");
159 : : }
160 [ - + ]: 2 : else if (!paired)
161 : : {
162 : 9 : g_autoptr (ValentChannel) channel = NULL;
163 : 0 : const char *verification_key = NULL;
164 : 0 : gboolean pair_incoming, pair_outgoing;
165 : :
166 : : /* Get the channel verification key */
167 [ # # ]: 0 : if ((channel = valent_device_ref_channel (self->device)) != NULL)
168 : 0 : verification_key = valent_channel_get_verification_key (channel);
169 : : else
170 : 0 : verification_key = _("Unavailable");
171 : :
172 : 0 : gtk_label_set_text (GTK_LABEL (self->verification_key), verification_key);
173 : :
174 : : /* Adjust the actions */
175 : 0 : pair_incoming = (state & VALENT_DEVICE_STATE_PAIR_INCOMING) != 0;
176 : 0 : pair_outgoing = (state & VALENT_DEVICE_STATE_PAIR_OUTGOING) != 0;
177 : :
178 : 0 : gtk_widget_set_visible (self->pair_request, !pair_incoming);
179 : 0 : gtk_widget_set_sensitive (self->pair_request, !pair_outgoing);
180 : 0 : gtk_spinner_set_spinning (self->pair_spinner, pair_outgoing);
181 : :
182 [ # # ]: 0 : gtk_stack_set_visible_child_name (self->stack, "pairing");
183 : : }
184 : : else
185 : : {
186 : 2 : gtk_stack_set_visible_child_name (self->stack, "connected");
187 : : }
188 : 9 : }
189 : :
190 : : static void
191 : 2 : page_preferences_action (GtkWidget *widget,
192 : : const char *action_name,
193 : : GVariant *parameter)
194 : : {
195 : 2 : ValentDevicePage *self = VALENT_DEVICE_PAGE (widget);
196 : 2 : GtkRoot *window = gtk_widget_get_root (widget);
197 : :
198 [ + - ]: 2 : if (self->preferences == NULL)
199 : : {
200 : :
201 : :
202 : 2 : self->preferences = g_object_new (VALENT_TYPE_DEVICE_PREFERENCES_DIALOG,
203 : : "device", self->device,
204 : : NULL);
205 : :
206 : 2 : g_object_add_weak_pointer (G_OBJECT (self->preferences),
207 : 2 : (gpointer)&self->preferences);
208 : : }
209 : :
210 : 2 : adw_dialog_present (ADW_DIALOG (self->preferences), GTK_WIDGET (window));
211 : 2 : }
212 : :
213 : : static void
214 : 1 : page_pair_action (GtkWidget *widget,
215 : : const char *action_name,
216 : : GVariant *parameter)
217 : : {
218 : 1 : ValentDevicePage *self = VALENT_DEVICE_PAGE (widget);
219 : :
220 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE (self->device));
221 : :
222 : 1 : g_action_group_activate_action (G_ACTION_GROUP (self->device), "pair", NULL);
223 : 1 : }
224 : :
225 : : static void
226 : 1 : page_unpair_action (GtkWidget *widget,
227 : : const char *action_name,
228 : : GVariant *parameter)
229 : : {
230 : 1 : ValentDevicePage *self = VALENT_DEVICE_PAGE (widget);
231 : :
232 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE (self->device));
233 : :
234 : 1 : g_action_group_activate_action (G_ACTION_GROUP (self->device), "unpair", NULL);
235 : 1 : }
236 : :
237 : : /*
238 : : * GObject
239 : : */
240 : : static void
241 : 5 : valent_device_page_constructed (GObject *object)
242 : : {
243 : 5 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
244 : 5 : GMenuModel *menu;
245 : :
246 : 5 : g_object_bind_property (self->device, "id",
247 : : self, "tag",
248 : : G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
249 : 5 : g_object_bind_property (self->device, "name",
250 : : self, "title",
251 : : G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
252 : :
253 : : /* Actions & Menu */
254 : 5 : gtk_widget_insert_action_group (GTK_WIDGET (self),
255 : : "device",
256 : 5 : G_ACTION_GROUP (self->device));
257 : :
258 : 5 : menu = valent_device_get_menu (self->device);
259 : 5 : valent_menu_stack_set_menu_model (self->menu_actions, menu);
260 : :
261 : : /* Pair Section */
262 : 5 : g_signal_connect_object (self->device,
263 : : "notify::state",
264 : : G_CALLBACK (on_state_changed),
265 : : self, 0);
266 : 5 : on_state_changed (self->device, NULL, self);
267 : :
268 : : /* Plugin Gadgets */
269 : 5 : g_signal_connect_object (self->device,
270 : : "notify::plugins",
271 : : G_CALLBACK (on_plugins_changed),
272 : : self, 0);
273 : 5 : on_plugins_changed (self->device, NULL, self);
274 : :
275 : 5 : G_OBJECT_CLASS (valent_device_page_parent_class)->constructed (object);
276 : 5 : }
277 : :
278 : : static void
279 : 5 : valent_device_page_dispose (GObject *object)
280 : : {
281 : 5 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
282 : :
283 [ + - ]: 5 : g_clear_object (&self->device);
284 [ + - ]: 5 : g_clear_pointer (&self->plugins, g_hash_table_unref);
285 [ - + ]: 5 : g_clear_pointer (&self->preferences, adw_dialog_force_close);
286 : :
287 : 5 : gtk_widget_dispose_template (GTK_WIDGET (object), VALENT_TYPE_DEVICE_PAGE);
288 : :
289 : 5 : G_OBJECT_CLASS (valent_device_page_parent_class)->dispose (object);
290 : 5 : }
291 : :
292 : : static void
293 : 1 : valent_device_page_get_property (GObject *object,
294 : : guint prop_id,
295 : : GValue *value,
296 : : GParamSpec *pspec)
297 : : {
298 : 1 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
299 : :
300 [ + - ]: 1 : switch (prop_id)
301 : : {
302 : 1 : case PROP_DEVICE:
303 : 1 : g_value_set_object (value, self->device);
304 : 1 : break;
305 : :
306 : 0 : default:
307 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
308 : : }
309 : 1 : }
310 : :
311 : : static void
312 : 5 : valent_device_page_set_property (GObject *object,
313 : : guint prop_id,
314 : : const GValue *value,
315 : : GParamSpec *pspec)
316 : : {
317 : 5 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
318 : :
319 [ + - ]: 5 : switch (prop_id)
320 : : {
321 : 5 : case PROP_DEVICE:
322 : 5 : self->device = g_value_dup_object (value);
323 : 5 : break;
324 : :
325 : 0 : default:
326 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
327 : : }
328 : 5 : }
329 : :
330 : : static void
331 : 3 : valent_device_page_class_init (ValentDevicePageClass *klass)
332 : : {
333 : 3 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
334 : 3 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
335 : :
336 : 3 : object_class->constructed = valent_device_page_constructed;
337 : 3 : object_class->dispose = valent_device_page_dispose;
338 : 3 : object_class->get_property = valent_device_page_get_property;
339 : 3 : object_class->set_property = valent_device_page_set_property;
340 : :
341 : : /* template */
342 : 3 : gtk_widget_class_set_template_from_resource (widget_class, "/ca/andyholmes/Valent/ui/valent-device-page.ui");
343 : 3 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, gadgets);
344 : 3 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, stack);
345 : 3 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, pair_request);
346 : 3 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, pair_spinner);
347 : 3 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, verification_key);
348 : 3 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, menu_actions);
349 : :
350 : 3 : gtk_widget_class_install_action (widget_class, "page.preferences", NULL, page_preferences_action);
351 : 3 : gtk_widget_class_install_action (widget_class, "page.pair", NULL, page_pair_action);
352 : 3 : gtk_widget_class_install_action (widget_class, "page.unpair", NULL, page_unpair_action);
353 : :
354 : : /**
355 : : * ValentDevicePage:device:
356 : : *
357 : : * The device this panel controls and represents.
358 : : */
359 : 6 : properties [PROP_DEVICE] =
360 : 3 : g_param_spec_object ("device", NULL, NULL,
361 : : VALENT_TYPE_DEVICE,
362 : : (G_PARAM_READWRITE |
363 : : G_PARAM_CONSTRUCT_ONLY |
364 : : G_PARAM_EXPLICIT_NOTIFY |
365 : : G_PARAM_STATIC_STRINGS));
366 : :
367 : 3 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
368 : :
369 : : /* Ensure the private types we need are ready */
370 : 3 : g_type_ensure (VALENT_TYPE_MENU_LIST);
371 : 3 : g_type_ensure (VALENT_TYPE_MENU_STACK);
372 : 3 : }
373 : :
374 : : static void
375 : 5 : valent_device_page_init (ValentDevicePage *self)
376 : : {
377 : 5 : gtk_widget_init_template (GTK_WIDGET (self));
378 : :
379 : 5 : self->plugins = g_hash_table_new_full (g_str_hash,
380 : : g_str_equal,
381 : : g_free,
382 : : plugin_data_free);
383 : 5 : }
384 : :
|