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