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 : : #define G_LOG_DOMAIN "valent-window"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <adwaita.h>
9 : : #include <glib/gi18n-lib.h>
10 : : #include <gtk/gtk.h>
11 : : #include <libportal/portal.h>
12 : : #include <valent.h>
13 : :
14 : : #include "valent-application-credits.h"
15 : : #include "valent-device-page.h"
16 : : #include "valent-device-row.h"
17 : : #include "valent-preferences-dialog.h"
18 : : #include "valent-ui-utils-private.h"
19 : : #include "valent-version-vcs.h"
20 : :
21 : : #include "valent-window.h"
22 : :
23 : :
24 : : struct _ValentWindow
25 : : {
26 : : AdwApplicationWindow parent_instance;
27 : : ValentDeviceManager *manager;
28 : :
29 : : AdwAnimation *scan;
30 : : AdwAnimation *fade;
31 : :
32 : : /* template */
33 : : AdwNavigationView *view;
34 : : GtkProgressBar *progress_bar;
35 : : GtkListBox *device_list;
36 : : AdwDialog *preferences;
37 : : };
38 : :
39 [ + + + - ]: 39 : G_DEFINE_FINAL_TYPE (ValentWindow, valent_window, ADW_TYPE_APPLICATION_WINDOW)
40 : :
41 : : typedef enum {
42 : : PROP_DEVICE_MANAGER = 1,
43 : : } ValentWindowProperty;
44 : :
45 : : static GParamSpec *properties[PROP_DEVICE_MANAGER + 1] = { NULL, };
46 : :
47 : :
48 : : static JsonNode *
49 : 0 : valent_get_debug_info (void)
50 : : {
51 : 0 : g_autoptr (JsonBuilder) builder = NULL;
52 [ # # ]: 0 : g_autofree char *os_name = NULL;
53 : 0 : const char *desktop = NULL;
54 : 0 : const char *session = NULL;
55 : 0 : const char *environment = NULL;
56 : 0 : PeasEngine *engine = NULL;
57 : 0 : unsigned int n_plugins = 0;
58 : :
59 : 0 : os_name = g_get_os_info (G_OS_INFO_KEY_PRETTY_NAME);
60 : 0 : desktop = g_getenv ("XDG_CURRENT_DESKTOP");
61 : 0 : session = g_getenv ("XDG_SESSION_TYPE");
62 [ # # # # ]: 0 : environment = xdp_portal_running_under_flatpak () ? "flatpak" :
63 : 0 : (xdp_portal_running_under_snap (NULL) ? "snap" : "host");
64 : :
65 : 0 : builder = json_builder_new ();
66 : 0 : json_builder_begin_object (builder);
67 : :
68 : : /* Application */
69 : 0 : json_builder_set_member_name (builder, "application");
70 : 0 : json_builder_begin_object (builder);
71 : 0 : json_builder_set_member_name (builder, "id");
72 : 0 : json_builder_add_string_value (builder, APPLICATION_ID);
73 : 0 : json_builder_set_member_name (builder, "version");
74 : 0 : json_builder_add_string_value (builder, VALENT_VERSION);
75 : 0 : json_builder_set_member_name (builder, "commit");
76 : 0 : json_builder_add_string_value (builder, VALENT_VCS_TAG);
77 : 0 : json_builder_end_object (builder);
78 : :
79 : : /* Runtime */
80 : 0 : json_builder_set_member_name (builder, "runtime");
81 : 0 : json_builder_begin_object (builder);
82 : 0 : json_builder_set_member_name (builder, "os");
83 [ # # ]: 0 : json_builder_add_string_value (builder, os_name != NULL ? os_name : "unknown");
84 : 0 : json_builder_set_member_name (builder, "desktop");
85 [ # # ]: 0 : json_builder_add_string_value (builder, desktop != NULL ? desktop : "unknown");
86 : 0 : json_builder_set_member_name (builder, "session");
87 [ # # ]: 0 : json_builder_add_string_value (builder, session != NULL ? session : "unknown");
88 : 0 : json_builder_set_member_name (builder, "environment");
89 : 0 : json_builder_add_string_value (builder, environment);
90 : 0 : json_builder_end_object (builder);
91 : :
92 : : /* Plugins */
93 : 0 : json_builder_set_member_name (builder, "plugins");
94 : 0 : json_builder_begin_object (builder);
95 : :
96 : 0 : engine = valent_get_plugin_engine ();
97 : 0 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
98 : :
99 [ # # ]: 0 : for (unsigned int i = 0; i < n_plugins; i++)
100 : : {
101 : 0 : g_autoptr (PeasPluginInfo) info = NULL;
102 : 0 : const char *name = NULL;
103 : 0 : gboolean loaded = FALSE;
104 : :
105 : 0 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
106 : 0 : name = peas_plugin_info_get_module_name (info);
107 : 0 : loaded = peas_plugin_info_is_loaded (info);
108 : :
109 : 0 : json_builder_set_member_name (builder, name);
110 [ # # ]: 0 : json_builder_add_boolean_value (builder, loaded);
111 : : }
112 : 0 : json_builder_end_object (builder);
113 : :
114 : 0 : json_builder_end_object (builder);
115 : :
116 : 0 : return json_builder_get_root (builder);
117 : : }
118 : :
119 : : /*
120 : : * AdwAnimation Callbacks
121 : : */
122 : : static gboolean
123 : 0 : refresh_cb (gpointer data)
124 : : {
125 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (data));
126 : :
127 : 0 : valent_device_manager_refresh (VALENT_DEVICE_MANAGER (data));
128 : :
129 : 0 : return G_SOURCE_CONTINUE;
130 : : }
131 : :
132 : : static void
133 : 0 : on_animation_state_changed (AdwAnimation *animation,
134 : : GParamSpec *pspec,
135 : : ValentWindow *self)
136 : : {
137 : 0 : AdwAnimationState state = adw_animation_get_state (animation);
138 : 0 : static unsigned int refresh_id = 0;
139 : :
140 [ # # ]: 0 : g_clear_handle_id (&refresh_id, g_source_remove);
141 : :
142 [ # # # ]: 0 : switch (state)
143 : : {
144 : 0 : case ADW_ANIMATION_PLAYING:
145 [ # # ]: 0 : if (self->scan == animation)
146 : : {
147 : 0 : valent_device_manager_refresh (self->manager);
148 : 0 : refresh_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
149 : : 2,
150 : : refresh_cb,
151 : 0 : g_object_ref (self->manager),
152 : : g_object_unref);
153 : 0 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "win.refresh", FALSE);
154 : : }
155 : : break;
156 : :
157 : 0 : case ADW_ANIMATION_FINISHED:
158 [ # # ]: 0 : if (self->scan == animation)
159 : : {
160 : 0 : adw_animation_play (self->fade);
161 : 0 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "win.refresh", TRUE);
162 : : }
163 : : break;
164 : :
165 : 0 : case ADW_ANIMATION_IDLE:
166 : : case ADW_ANIMATION_PAUSED:
167 : : default:
168 : 0 : gtk_progress_bar_set_fraction (self->progress_bar, 0.0);
169 : 0 : gtk_widget_set_opacity (GTK_WIDGET (self->progress_bar), 1.0);
170 : 0 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "win.refresh", TRUE);
171 : 0 : break;
172 : : }
173 : 0 : }
174 : :
175 : : static GtkWidget *
176 : 1 : valent_window_create_row_func (gpointer item,
177 : : gpointer user_data)
178 : : {
179 : 1 : ValentWindow *self = VALENT_WINDOW (user_data);
180 : 1 : ValentDevice *device = VALENT_DEVICE (item);
181 : 1 : const char *device_id;
182 : :
183 [ + - ]: 1 : g_assert (VALENT_IS_WINDOW (self));
184 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (item));
185 : :
186 : : /* A device was added, so stop the refresh */
187 [ - + ]: 1 : if (self->scan != NULL)
188 : 0 : adw_animation_skip (self->scan);
189 : :
190 : 1 : device_id = valent_device_get_id (device);
191 : 1 : return g_object_new (VALENT_TYPE_DEVICE_ROW,
192 : : "device", device,
193 : : "action-name", "win.page",
194 : : "action-target", g_variant_new_string (device_id),
195 : : "activatable", TRUE,
196 : : "selectable", FALSE,
197 : : NULL);
198 : : }
199 : :
200 : : /*
201 : : * GActions
202 : : */
203 : : static void
204 : 0 : about_action (GtkWidget *widget,
205 : : const char *action_name,
206 : : GVariant *parameter)
207 : : {
208 : 0 : GtkWindow *window = GTK_WINDOW (widget);
209 : 0 : AdwDialog *dialog = NULL;
210 : 0 : g_autoptr (JsonNode) debug_json = NULL;
211 [ # # ]: 0 : g_autofree char *debug_info = NULL;
212 : :
213 [ # # # # : 0 : g_assert (GTK_IS_WINDOW (window));
# # # # ]
214 : :
215 : 0 : debug_json = valent_get_debug_info ();
216 : 0 : debug_info = json_to_string (debug_json, TRUE);
217 : :
218 : 0 : dialog = g_object_new (ADW_TYPE_ABOUT_DIALOG,
219 : : "application-icon", APPLICATION_ID,
220 : 0 : "application-name", _("Valent"),
221 : : "copyright", "© Andy Holmes",
222 : : "issue-url", PACKAGE_BUGREPORT,
223 : : "license-type", GTK_LICENSE_GPL_3_0,
224 : : "debug-info", debug_info,
225 : : "debug-info-filename", "valent-debug.json",
226 : : "artists", valent_application_credits_artists,
227 : : "designers", valent_application_credits_designers,
228 : : "developers", valent_application_credits_developers,
229 : : "documenters", valent_application_credits_documenters,
230 : 0 : "translator-credits", _("translator-credits"),
231 : : "version", PACKAGE_VERSION,
232 : : "website", PACKAGE_URL,
233 : : NULL);
234 : 0 : adw_about_dialog_add_acknowledgement_section (ADW_ABOUT_DIALOG (dialog),
235 : 0 : _("Sponsors"),
236 : : valent_application_credits_sponsors);
237 : :
238 : 0 : adw_dialog_present (dialog, GTK_WIDGET (window));
239 : 0 : }
240 : :
241 : : static void
242 : 0 : page_action (GtkWidget *widget,
243 : : const char *action_name,
244 : : GVariant *parameter)
245 : : {
246 : 0 : ValentWindow *self = VALENT_WINDOW (widget);
247 : 0 : unsigned int n_devices = 0;
248 : 0 : const char *tag;
249 : :
250 [ # # ]: 0 : g_assert (VALENT_IS_WINDOW (self));
251 : :
252 : 0 : tag = g_variant_get_string (parameter, NULL);
253 : :
254 [ # # # # ]: 0 : if (*tag == '\0' || g_strcmp0 (tag, "main") == 0)
255 : : {
256 : 0 : adw_navigation_view_pop (self->view);
257 : 0 : return;
258 : : }
259 : :
260 : :
261 [ # # ]: 0 : g_clear_pointer (&self->preferences, adw_dialog_force_close);
262 : :
263 : 0 : n_devices = g_list_model_get_n_items (G_LIST_MODEL (self->manager));
264 [ # # ]: 0 : for (unsigned int i = 0; i < n_devices; i++)
265 : : {
266 : 0 : g_autoptr (ValentDevice) device = NULL;
267 : 0 : AdwNavigationPage *page;
268 : :
269 : 0 : device = g_list_model_get_item (G_LIST_MODEL (self->manager), i);
270 : :
271 [ # # ]: 0 : if (g_strcmp0 (valent_device_get_id (device), tag) == 0)
272 : : {
273 : 0 : page = g_object_new (VALENT_TYPE_DEVICE_PAGE,
274 : : "device", device,
275 : : NULL);
276 : 0 : adw_navigation_view_push (self->view, page);
277 [ # # ]: 0 : break;
278 : : }
279 : : }
280 : : }
281 : :
282 : : static void
283 : 0 : preferences_action (GtkWidget *widget,
284 : : const char *action_name,
285 : : GVariant *parameter)
286 : : {
287 : 0 : ValentWindow *self = VALENT_WINDOW (widget);
288 : :
289 [ # # ]: 0 : g_assert (VALENT_IS_WINDOW (self));
290 : :
291 [ # # ]: 0 : if (self->preferences == NULL)
292 : : {
293 : 0 : self->preferences = g_object_new (VALENT_TYPE_PREFERENCES_DIALOG, NULL);
294 : 0 : g_object_add_weak_pointer (G_OBJECT (self->preferences),
295 : 0 : (gpointer)&self->preferences);
296 : : }
297 : :
298 : 0 : adw_dialog_present (ADW_DIALOG (self->preferences), GTK_WIDGET (self));
299 : 0 : }
300 : :
301 : : static void
302 : 1 : refresh_action (GtkWidget *widget,
303 : : const char *action_name,
304 : : GVariant *parameter)
305 : : {
306 : 1 : ValentWindow *self = VALENT_WINDOW (widget);
307 : :
308 [ + - ]: 1 : g_assert (VALENT_IS_WINDOW (self));
309 : :
310 [ + - ]: 1 : if (!adw_get_enable_animations (widget))
311 : : {
312 : 1 : valent_device_manager_refresh (self->manager);
313 : 1 : return;
314 : : }
315 : :
316 [ # # # # ]: 0 : if (self->scan == NULL && self->fade == NULL)
317 : 0 : {
318 : 0 : AdwAnimationTarget *target = NULL;
319 : :
320 : 0 : target = adw_property_animation_target_new (G_OBJECT (self->progress_bar),
321 : : "fraction");
322 : 0 : self->scan = adw_timed_animation_new (widget, 0.0, 1.0, 5000, target);
323 : 0 : g_signal_connect_object (self->scan,
324 : : "notify::state",
325 : : G_CALLBACK (on_animation_state_changed),
326 : : self, 0);
327 : :
328 : 0 : target = adw_property_animation_target_new (G_OBJECT (self->progress_bar),
329 : : "opacity");
330 : 0 : self->fade = adw_timed_animation_new (widget, 1.0, 0.0, 500, target);
331 : 0 : g_signal_connect_object (self->fade,
332 : : "notify::state",
333 : : G_CALLBACK (on_animation_state_changed),
334 : : self, 0);
335 : : }
336 : : else
337 : : {
338 : 0 : adw_animation_reset (self->fade);
339 : 0 : adw_animation_reset (self->scan);
340 : : }
341 : :
342 : 0 : adw_animation_play (self->scan);
343 : : }
344 : :
345 : : /*
346 : : * GObject
347 : : */
348 : : static void
349 : 2 : valent_window_constructed (GObject *object)
350 : : {
351 : 2 : ValentWindow *self = VALENT_WINDOW (object);
352 : :
353 [ + - ]: 2 : g_assert (self->manager != NULL);
354 : :
355 : 2 : G_OBJECT_CLASS (valent_window_parent_class)->constructed (object);
356 : :
357 : 2 : gtk_list_box_bind_model (self->device_list,
358 : 2 : G_LIST_MODEL (self->manager),
359 : : valent_window_create_row_func,
360 : : self, NULL);
361 : 2 : }
362 : :
363 : : static void
364 : 2 : valent_window_dispose (GObject *object)
365 : : {
366 : 2 : ValentWindow *self = VALENT_WINDOW (object);
367 : :
368 [ - + ]: 2 : if (self->scan != NULL)
369 : 0 : adw_animation_reset (self->scan);
370 [ - + ]: 2 : g_clear_object (&self->scan);
371 : :
372 [ - + ]: 2 : if (self->fade != NULL)
373 : 0 : adw_animation_reset (self->fade);
374 [ - + ]: 2 : g_clear_object (&self->fade);
375 : :
376 [ - + ]: 2 : g_clear_pointer (&self->preferences, adw_dialog_force_close);
377 : 2 : gtk_widget_dispose_template (GTK_WIDGET (object), VALENT_TYPE_WINDOW);
378 : :
379 : 2 : G_OBJECT_CLASS (valent_window_parent_class)->dispose (object);
380 : 2 : }
381 : :
382 : : static void
383 : 2 : valent_window_finalize (GObject *object)
384 : : {
385 : 2 : ValentWindow *self = VALENT_WINDOW (object);
386 : :
387 [ + - ]: 2 : g_clear_object (&self->manager);
388 : :
389 : 2 : G_OBJECT_CLASS (valent_window_parent_class)->finalize (object);
390 : 2 : }
391 : :
392 : : static void
393 : 1 : valent_window_get_property (GObject *object,
394 : : guint prop_id,
395 : : GValue *value,
396 : : GParamSpec *pspec)
397 : : {
398 : 1 : ValentWindow *self = VALENT_WINDOW (object);
399 : :
400 [ + - ]: 1 : switch ((ValentWindowProperty)prop_id)
401 : : {
402 : 1 : case PROP_DEVICE_MANAGER:
403 : 1 : g_value_set_object (value, self->manager);
404 : 1 : break;
405 : :
406 : 0 : default:
407 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 : : }
409 : 1 : }
410 : :
411 : : static void
412 : 2 : valent_window_set_property (GObject *object,
413 : : guint prop_id,
414 : : const GValue *value,
415 : : GParamSpec *pspec)
416 : : {
417 : 2 : ValentWindow *self = VALENT_WINDOW (object);
418 : :
419 [ + - ]: 2 : switch ((ValentWindowProperty)prop_id)
420 : : {
421 : 2 : case PROP_DEVICE_MANAGER:
422 : 2 : self->manager = g_value_dup_object (value);
423 : 2 : break;
424 : :
425 : 0 : default:
426 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
427 : : }
428 : 2 : }
429 : :
430 : : static void
431 : 1 : valent_window_class_init (ValentWindowClass *klass)
432 : : {
433 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
434 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
435 : :
436 : 1 : object_class->constructed = valent_window_constructed;
437 : 1 : object_class->dispose = valent_window_dispose;
438 : 1 : object_class->finalize = valent_window_finalize;
439 : 1 : object_class->get_property = valent_window_get_property;
440 : 1 : object_class->set_property = valent_window_set_property;
441 : :
442 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-window.ui");
443 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentWindow, view);
444 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentWindow, progress_bar);
445 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentWindow, device_list);
446 : :
447 : 1 : gtk_widget_class_install_action (widget_class, "win.about", NULL, about_action);
448 : 1 : gtk_widget_class_install_action (widget_class, "win.page", "s", page_action);
449 : 1 : gtk_widget_class_install_action (widget_class, "win.preferences", NULL, preferences_action);
450 : 1 : gtk_widget_class_install_action (widget_class, "win.refresh", NULL, refresh_action);
451 : :
452 : : /**
453 : : * ValentWindow:device-manager:
454 : : *
455 : : * The [class@Valent.DeviceManager] that the window represents.
456 : : */
457 : 2 : properties [PROP_DEVICE_MANAGER] =
458 : 1 : g_param_spec_object ("device-manager", NULL, NULL,
459 : : VALENT_TYPE_DEVICE_MANAGER,
460 : : (G_PARAM_READWRITE |
461 : : G_PARAM_CONSTRUCT_ONLY |
462 : : G_PARAM_EXPLICIT_NOTIFY |
463 : : G_PARAM_STATIC_STRINGS));
464 : :
465 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
466 : 1 : }
467 : :
468 : : static void
469 : 2 : valent_window_init (ValentWindow *self)
470 : : {
471 : 2 : gtk_widget_init_template (GTK_WIDGET (self));
472 : 2 : }
473 : :
|