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 <valent.h>
11 : :
12 : : #include "valent-device-page.h"
13 : : #include "valent-device-preferences-dialog.h"
14 : : #include "valent-menu-list.h"
15 : : #include "valent-menu-stack.h"
16 : :
17 : :
18 : : struct _ValentDevicePage
19 : : {
20 : : AdwNavigationPage parent_instance;
21 : :
22 : : ValentDevice *device;
23 : : GHashTable *plugins;
24 : : AdwDialog *preferences;
25 : :
26 : : /* template */
27 : : GtkStack *stack;
28 : : GtkWidget *gadgets;
29 : : GtkWidget *battery_status;
30 : : GtkWidget *battery_status_label;
31 : : GtkWidget *battery_status_level;
32 : : GtkWidget *connectivity_status;
33 : : GtkWidget *connectivity_status_box;
34 : : AdwStatusPage *pair_page;
35 : : GtkWidget *pair_request;
36 : : GtkSpinner *pair_spinner;
37 : : GtkWidget *verification_key;
38 : :
39 : : ValentMenuStack *menu_actions;
40 : : };
41 : :
42 [ + + + - ]: 45 : G_DEFINE_FINAL_TYPE (ValentDevicePage, valent_device_page, ADW_TYPE_NAVIGATION_PAGE)
43 : :
44 : : typedef enum {
45 : : PROP_DEVICE = 1,
46 : : } ValentDevicePageProperty;
47 : :
48 : : static GParamSpec *properties[PROP_DEVICE + 1] = { NULL, };
49 : :
50 : : /*
51 : : * Pairing
52 : : */
53 : : static void
54 : 6 : on_state_changed (ValentDevice *device,
55 : : GParamSpec *pspec,
56 : : ValentDevicePage *self)
57 : : {
58 : 6 : ValentDeviceState state = VALENT_DEVICE_STATE_NONE;
59 : 6 : gboolean connected, paired;
60 : :
61 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE (device));
62 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_PAGE (self));
63 : :
64 : 6 : state = valent_device_get_state (self->device);
65 : 6 : connected = (state & VALENT_DEVICE_STATE_CONNECTED) != 0;
66 : 6 : paired = (state & VALENT_DEVICE_STATE_PAIRED) != 0;
67 : :
68 : : /* Ensure the proper controls are displayed */
69 : 6 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "page.pair", !paired);
70 : 6 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "page.unpair", paired);
71 : :
72 [ + - ]: 6 : if (!connected)
73 : : {
74 : 6 : gtk_stack_set_visible_child_name (self->stack, "disconnected");
75 : : }
76 [ # # ]: 0 : else if (!paired)
77 : : {
78 : 6 : g_autoptr (ValentChannel) channel = NULL;
79 [ # # ]: 0 : g_autofree char *description = NULL;
80 : 0 : const char *verification_key = NULL;
81 : 0 : gboolean pair_incoming, pair_outgoing;
82 : :
83 : 0 : description = g_strdup_printf (_("Please confirm the verification key "
84 : : "below matches the one on “%s”"),
85 : : valent_device_get_name (self->device));
86 : 0 : adw_status_page_set_description (self->pair_page, description);
87 : :
88 : : /* Get the channel verification key */
89 [ # # ]: 0 : if ((channel = valent_device_ref_channel (self->device)) != NULL)
90 : 0 : verification_key = valent_channel_get_verification_key (channel);
91 : : else
92 : 0 : verification_key = _("Unavailable");
93 : :
94 : 0 : gtk_label_set_text (GTK_LABEL (self->verification_key), verification_key);
95 : :
96 : : /* Adjust the actions */
97 : 0 : pair_incoming = (state & VALENT_DEVICE_STATE_PAIR_INCOMING) != 0;
98 : 0 : pair_outgoing = (state & VALENT_DEVICE_STATE_PAIR_OUTGOING) != 0;
99 : :
100 : 0 : gtk_widget_set_visible (self->pair_request, !pair_incoming);
101 : 0 : gtk_widget_set_sensitive (self->pair_request, !pair_outgoing);
102 : 0 : gtk_spinner_set_spinning (self->pair_spinner, pair_outgoing);
103 : :
104 : 0 : gtk_stack_set_visible_child_name (self->stack, "pairing");
105 : : }
106 : : else
107 : : {
108 : 0 : gtk_stack_set_visible_child_name (self->stack, "connected");
109 : : }
110 : 6 : }
111 : :
112 : : /*
113 : : * Battery
114 : : */
115 : : static void
116 : 0 : on_battery_state_changed (GActionGroup *action_group,
117 : : const char *action_name,
118 : : GVariant *value,
119 : : ValentDevicePage *self)
120 : : {
121 : 0 : g_autofree char *label = NULL;
122 : 0 : gboolean charging = FALSE;
123 : 0 : gboolean is_present = FALSE;
124 : 0 : double percentage = 0.0;
125 : 0 : const char *icon_name;
126 : :
127 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_PAGE (self));
128 : :
129 : 0 : g_variant_lookup (value, "is-present", "b", &is_present);
130 [ # # ]: 0 : if (!is_present)
131 : : {
132 : 0 : gtk_widget_set_visible (self->battery_status, FALSE);
133 : 0 : return;
134 : : }
135 : :
136 [ # # # # ]: 0 : if (!g_variant_lookup (value, "percentage", "d", &percentage) ||
137 : 0 : !g_variant_lookup (value, "charging", "b", &charging))
138 : : {
139 : 0 : gtk_widget_set_visible (self->battery_status, FALSE);
140 : 0 : return;
141 : : }
142 : :
143 [ # # ]: 0 : if (!g_variant_lookup (value, "icon-name", "&s", &icon_name))
144 : 0 : icon_name = "battery-missing-symbolic";
145 : :
146 [ # # ]: 0 : if (percentage >= 100.0)
147 : : {
148 : : /* TRANSLATORS: When the battery level is 100%
149 : : */
150 [ # # ]: 0 : label = g_strdup (_("Fully Charged"));
151 : : }
152 : : else
153 : : {
154 : 0 : int64_t total_seconds = 0;
155 : 0 : unsigned int total_minutes;
156 : 0 : unsigned int minutes;
157 : 0 : unsigned int hours;
158 : :
159 [ # # ]: 0 : if (charging)
160 : 0 : g_variant_lookup (value, "time-to-full", "x", &total_seconds);
161 : : else
162 : 0 : g_variant_lookup (value, "time-to-empty", "x", &total_seconds);
163 : :
164 [ # # ]: 0 : if (total_seconds > 0)
165 : : {
166 : 0 : total_minutes = (unsigned int)floor (total_seconds / 60);
167 : 0 : minutes = total_minutes % 60;
168 : 0 : hours = (unsigned int)floor (total_minutes / 60);
169 : : }
170 : :
171 [ # # ]: 0 : if (total_seconds <= 0)
172 : : {
173 : : /* TRANSLATORS: This is <percentage> (Estimating…)
174 : : */
175 : 0 : label = g_strdup_printf (_("%g%% (Estimating…)"), percentage);
176 : : }
177 [ # # ]: 0 : else if (charging)
178 : : {
179 : : /* TRANSLATORS: This is <percentage> (<hours>:<minutes> Until Full)
180 : : */
181 : 0 : label = g_strdup_printf (_("%g%% (%d∶%02d Until Full)"),
182 : : percentage, hours, minutes);
183 : : }
184 : : else
185 : : {
186 : : /* TRANSLATORS: This is <percentage> (<hours>:<minutes> Remaining)
187 : : */
188 : 0 : label = g_strdup_printf (_("%g%% (%d∶%02d Remaining)"),
189 : : percentage, hours, minutes);
190 : : }
191 : : }
192 : :
193 [ # # ]: 0 : if (g_action_group_get_action_enabled (action_group, action_name))
194 : : {
195 : 0 : gtk_widget_set_visible (self->battery_status, TRUE);
196 : 0 : gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (self->battery_status),
197 : : icon_name);
198 : :
199 : 0 : gtk_label_set_text (GTK_LABEL (self->battery_status_label), label);
200 : 0 : gtk_level_bar_set_value (GTK_LEVEL_BAR (self->battery_status_level),
201 : : percentage);
202 : : }
203 : : }
204 : :
205 : : static void
206 : 3 : on_battery_enabled_changed (GActionGroup *action_group,
207 : : const char *action_name,
208 : : gboolean enabled,
209 : : ValentDevicePage *self)
210 : : {
211 : 6 : g_autoptr (GVariant) state = NULL;
212 : :
213 : 3 : gtk_widget_set_visible (self->battery_status, enabled);
214 : :
215 [ - + ]: 3 : if (enabled)
216 : 0 : state = g_action_group_get_action_state (action_group, action_name);
217 : :
218 [ # # ]: 0 : if (state != NULL)
219 : 0 : on_battery_state_changed (action_group, action_name, state, self);
220 : 3 : }
221 : :
222 : : /*
223 : : * Connectivity Status
224 : : */
225 : : static void
226 : 0 : on_connectivity_state_changed (GActionGroup *action_group,
227 : : const char *action_name,
228 : : GVariant *value,
229 : : ValentDevicePage *self)
230 : : {
231 : 0 : GtkWidget *child;
232 : 0 : g_autoptr (GVariant) signal_strengths = NULL;
233 : 0 : GVariantIter iter;
234 : 0 : char *signal_id = NULL;
235 : 0 : GVariant *signal_state;
236 : 0 : const char *icon_name;
237 : 0 : const char *title;
238 : :
239 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_PAGE (self));
240 : :
241 : : /* Clear the popup
242 : : */
243 [ # # ]: 0 : while ((child = gtk_widget_get_first_child (self->connectivity_status_box)) != NULL)
244 : 0 : gtk_box_remove (GTK_BOX (self->connectivity_status_box), child);
245 : :
246 [ # # ]: 0 : if (!g_variant_lookup (value, "signal-strengths", "@a{sv}", &signal_strengths))
247 : : {
248 : 0 : gtk_widget_set_visible (self->connectivity_status, FALSE);
249 [ # # ]: 0 : return;
250 : : }
251 : :
252 : : /* Add each signal
253 : : */
254 : 0 : g_variant_iter_init (&iter, signal_strengths);
255 : :
256 [ # # ]: 0 : while (g_variant_iter_loop (&iter, "{sv}", &signal_id, &signal_state))
257 : : {
258 : 0 : GtkWidget *box;
259 : 0 : GtkWidget *level;
260 : 0 : GtkWidget *icon;
261 : 0 : const char *signal_icon;
262 : 0 : const char *network_type;
263 : 0 : int64_t signal_strength;
264 : :
265 : 0 : box = g_object_new (GTK_TYPE_BOX,
266 : : "spacing", 6,
267 : : NULL);
268 : :
269 : 0 : icon = g_object_new (GTK_TYPE_IMAGE,
270 : : "pixel-size", 16,
271 : : "valign", GTK_ALIGN_CENTER,
272 : : NULL);
273 : 0 : gtk_box_append (GTK_BOX (box), icon);
274 : :
275 : 0 : level = g_object_new (GTK_TYPE_LEVEL_BAR,
276 : : "mode", GTK_LEVEL_BAR_MODE_DISCRETE,
277 : : "min-value", 0.0,
278 : : "max-value", 5.0,
279 : : "value", 0.0,
280 : : "valign", GTK_ALIGN_CENTER,
281 : : "hexpand", TRUE,
282 : : "height-request", 3,
283 : : "width-request", 64,
284 : : NULL);
285 : 0 : gtk_box_append (GTK_BOX (box), level);
286 : :
287 [ # # ]: 0 : if (g_variant_lookup (signal_state, "icon-name", "&s", &signal_icon))
288 : 0 : gtk_image_set_from_icon_name (GTK_IMAGE (icon), signal_icon);
289 : :
290 [ # # ]: 0 : if (g_variant_lookup (signal_state, "network-type", "&s", &network_type))
291 : 0 : gtk_widget_set_tooltip_text (GTK_WIDGET (icon), network_type);
292 : :
293 [ # # ]: 0 : if (g_variant_lookup (signal_state, "signal-strength", "x", &signal_strength))
294 : 0 : gtk_level_bar_set_value (GTK_LEVEL_BAR (level), signal_strength);
295 : :
296 : 0 : gtk_box_append (GTK_BOX (self->connectivity_status_box), box);
297 : : }
298 : :
299 : : /* Add status properties
300 : : */
301 [ # # ]: 0 : if (g_action_group_get_action_enabled (action_group, action_name))
302 : : {
303 : 0 : gtk_widget_set_visible (self->connectivity_status, TRUE);
304 : :
305 [ # # ]: 0 : if (g_variant_lookup (value, "icon-name", "&s", &icon_name))
306 : 0 : gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (self->connectivity_status), icon_name);
307 : :
308 [ # # ]: 0 : if (g_variant_lookup (value, "title", "&s", &title))
309 : 0 : gtk_accessible_update_property (GTK_ACCESSIBLE (self->connectivity_status),
310 : : GTK_ACCESSIBLE_PROPERTY_LABEL, title,
311 : : -1);
312 : : }
313 : : }
314 : :
315 : : static void
316 : 3 : on_connectivity_enabled_changed (GActionGroup *action_group,
317 : : const char *action_name,
318 : : gboolean enabled,
319 : : ValentDevicePage *self)
320 : : {
321 : 6 : g_autoptr (GVariant) state = NULL;
322 : :
323 : : /* gtk_widget_set_visible (self->button, enabled); */
324 : :
325 [ - + ]: 3 : if (enabled)
326 : 0 : state = g_action_group_get_action_state (action_group, action_name);
327 : :
328 [ # # ]: 0 : if (state != NULL)
329 : 0 : on_connectivity_state_changed (action_group, action_name, state, self);
330 : 3 : }
331 : :
332 : : /*
333 : : * GAction
334 : : */
335 : : static void
336 : 2 : page_preferences_action (GtkWidget *widget,
337 : : const char *action_name,
338 : : GVariant *parameter)
339 : : {
340 : 2 : ValentDevicePage *self = VALENT_DEVICE_PAGE (widget);
341 : 2 : GtkRoot *window = gtk_widget_get_root (widget);
342 : :
343 [ + - ]: 2 : if (self->preferences == NULL)
344 : : {
345 : :
346 : :
347 : 2 : self->preferences = g_object_new (VALENT_TYPE_DEVICE_PREFERENCES_DIALOG,
348 : : "device", self->device,
349 : : NULL);
350 : :
351 : 2 : g_object_add_weak_pointer (G_OBJECT (self->preferences),
352 : 2 : (gpointer)&self->preferences);
353 : : }
354 : :
355 : 2 : adw_dialog_present (ADW_DIALOG (self->preferences), GTK_WIDGET (window));
356 : 2 : }
357 : :
358 : : static void
359 : 1 : page_pair_action (GtkWidget *widget,
360 : : const char *action_name,
361 : : GVariant *parameter)
362 : : {
363 : 1 : ValentDevicePage *self = VALENT_DEVICE_PAGE (widget);
364 : :
365 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE (self->device));
366 : :
367 : 1 : g_action_group_activate_action (G_ACTION_GROUP (self->device), "pair", NULL);
368 : 1 : }
369 : :
370 : : static void
371 : 1 : page_unpair_action (GtkWidget *widget,
372 : : const char *action_name,
373 : : GVariant *parameter)
374 : : {
375 : 1 : ValentDevicePage *self = VALENT_DEVICE_PAGE (widget);
376 : :
377 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE (self->device));
378 : :
379 : 1 : g_action_group_activate_action (G_ACTION_GROUP (self->device), "unpair", NULL);
380 : 1 : }
381 : :
382 : : /*
383 : : * GObject
384 : : */
385 : : static void
386 : 3 : valent_device_page_constructed (GObject *object)
387 : : {
388 : 3 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
389 : 3 : GActionGroup *action_group = G_ACTION_GROUP (self->device);
390 : 3 : GMenuModel *menu;
391 : 3 : gboolean enabled;
392 : :
393 : 3 : G_OBJECT_CLASS (valent_device_page_parent_class)->constructed (object);
394 : :
395 : 3 : g_object_bind_property (self->device, "id",
396 : : self, "tag",
397 : : G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
398 : 3 : g_object_bind_property (self->device, "name",
399 : : self, "title",
400 : : G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
401 : :
402 : : /* Actions & Menu
403 : : */
404 : 3 : gtk_widget_insert_action_group (GTK_WIDGET (self), "device", action_group);
405 : 3 : menu = valent_device_get_menu (self->device);
406 : 3 : valent_menu_stack_set_menu_model (self->menu_actions, menu);
407 : :
408 : : /* Pair Section
409 : : */
410 : 3 : g_signal_connect_object (self->device,
411 : : "notify::state",
412 : : G_CALLBACK (on_state_changed),
413 : : self,
414 : : G_CONNECT_DEFAULT);
415 : 3 : on_state_changed (self->device, NULL, self);
416 : :
417 : : /*
418 : : * Battery Status
419 : : */
420 : 3 : g_signal_connect_object (action_group,
421 : : "action-state-changed::battery.state",
422 : : G_CALLBACK (on_battery_state_changed),
423 : : self,
424 : : G_CONNECT_DEFAULT);
425 : 3 : g_signal_connect_object (action_group,
426 : : "action-enabled-changed::battery.state",
427 : : G_CALLBACK (on_battery_enabled_changed),
428 : : self,
429 : : G_CONNECT_DEFAULT);
430 : :
431 : 3 : enabled = g_action_group_get_action_enabled (action_group, "battery.state");
432 : 3 : on_battery_enabled_changed (action_group, "battery.state", enabled, self);
433 : :
434 : : /*
435 : : * Connectivity Status
436 : : */
437 : 3 : g_signal_connect_object (action_group,
438 : : "action-state-changed::connectivity_report.state",
439 : : G_CALLBACK (on_connectivity_state_changed),
440 : : self, 0);
441 : :
442 : 3 : g_signal_connect_object (action_group,
443 : : "action-enabled-changed::connectivity_report.state",
444 : : G_CALLBACK (on_connectivity_enabled_changed),
445 : : self,
446 : : G_CONNECT_DEFAULT);
447 : :
448 : 3 : enabled = g_action_group_get_action_enabled (action_group, "connectivity_report.state");
449 : 3 : on_connectivity_enabled_changed (action_group, "connectivity_report.state", enabled, self);
450 : 3 : }
451 : :
452 : : static void
453 : 3 : valent_device_page_dispose (GObject *object)
454 : : {
455 : 3 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
456 : :
457 [ + - ]: 3 : g_clear_object (&self->device);
458 [ - + ]: 3 : g_clear_pointer (&self->preferences, adw_dialog_force_close);
459 : :
460 : 3 : gtk_widget_dispose_template (GTK_WIDGET (object), VALENT_TYPE_DEVICE_PAGE);
461 : :
462 : 3 : G_OBJECT_CLASS (valent_device_page_parent_class)->dispose (object);
463 : 3 : }
464 : :
465 : : static void
466 : 1 : valent_device_page_get_property (GObject *object,
467 : : guint prop_id,
468 : : GValue *value,
469 : : GParamSpec *pspec)
470 : : {
471 : 1 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
472 : :
473 [ + - ]: 1 : switch ((ValentDevicePageProperty)prop_id)
474 : : {
475 : 1 : case PROP_DEVICE:
476 : 1 : g_value_set_object (value, self->device);
477 : 1 : break;
478 : :
479 : 0 : default:
480 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
481 : : }
482 : 1 : }
483 : :
484 : : static void
485 : 3 : valent_device_page_set_property (GObject *object,
486 : : guint prop_id,
487 : : const GValue *value,
488 : : GParamSpec *pspec)
489 : : {
490 : 3 : ValentDevicePage *self = VALENT_DEVICE_PAGE (object);
491 : :
492 [ + - ]: 3 : switch ((ValentDevicePageProperty)prop_id)
493 : : {
494 : 3 : case PROP_DEVICE:
495 : 3 : self->device = g_value_dup_object (value);
496 : 3 : break;
497 : :
498 : 0 : default:
499 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
500 : : }
501 : 3 : }
502 : :
503 : : static void
504 : 2 : valent_device_page_class_init (ValentDevicePageClass *klass)
505 : : {
506 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
507 : 2 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
508 : :
509 : 2 : object_class->constructed = valent_device_page_constructed;
510 : 2 : object_class->dispose = valent_device_page_dispose;
511 : 2 : object_class->get_property = valent_device_page_get_property;
512 : 2 : object_class->set_property = valent_device_page_set_property;
513 : :
514 : : /* template */
515 : 2 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-device-page.ui");
516 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, gadgets);
517 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, battery_status);
518 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, battery_status_label);
519 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, battery_status_level);
520 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, connectivity_status);
521 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, connectivity_status_box);
522 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, stack);
523 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, pair_page);
524 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, pair_request);
525 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, pair_spinner);
526 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, verification_key);
527 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentDevicePage, menu_actions);
528 : :
529 : 2 : gtk_widget_class_install_action (widget_class, "page.preferences", NULL, page_preferences_action);
530 : 2 : gtk_widget_class_install_action (widget_class, "page.pair", NULL, page_pair_action);
531 : 2 : gtk_widget_class_install_action (widget_class, "page.unpair", NULL, page_unpair_action);
532 : :
533 : : /**
534 : : * ValentDevicePage:device:
535 : : *
536 : : * The device this panel controls and represents.
537 : : */
538 : 4 : properties [PROP_DEVICE] =
539 : 2 : g_param_spec_object ("device", NULL, NULL,
540 : : VALENT_TYPE_DEVICE,
541 : : (G_PARAM_READWRITE |
542 : : G_PARAM_CONSTRUCT_ONLY |
543 : : G_PARAM_EXPLICIT_NOTIFY |
544 : : G_PARAM_STATIC_STRINGS));
545 : :
546 : 2 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
547 : :
548 : : /* Ensure the private types we need are ready */
549 : 2 : g_type_ensure (VALENT_TYPE_MENU_LIST);
550 : 2 : g_type_ensure (VALENT_TYPE_MENU_STACK);
551 : 2 : }
552 : :
553 : : static void
554 : 3 : valent_device_page_init (ValentDevicePage *self)
555 : : {
556 : 3 : gtk_widget_init_template (GTK_WIDGET (self));
557 : 3 : }
558 : :
|