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-share-target-chooser"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <adwaita.h>
9 : : #include <glib/gi18n.h>
10 : : #include <gtk/gtk.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-device-row.h"
14 : :
15 : : #include "valent-share-dialog.h"
16 : :
17 : :
18 : : struct _ValentShareDialog
19 : : {
20 : : AdwWindow parent_instance;
21 : :
22 : : ValentDeviceManager *manager;
23 : : GListModel *files;
24 : : unsigned int refresh_id;
25 : : unsigned int selection_mode : 1;
26 : :
27 : : GPtrArray *rows;
28 : : GCancellable *cancellable;
29 : : guint64 total_size;
30 : : unsigned int n_files;
31 : : unsigned int n_links;
32 : :
33 : : /* template */
34 : : AdwNavigationView *view;
35 : : AdwActionRow *single_row;
36 : : GtkImage *single_icon;
37 : : AdwExpanderRow *multiple_row;
38 : : GtkImage *multiple_icon;
39 : : GtkListBox *device_list;
40 : : AdwEntryRow *uri_entry;
41 : : };
42 : :
43 [ + + + - ]: 13 : G_DEFINE_FINAL_TYPE (ValentShareDialog, valent_share_dialog, ADW_TYPE_WINDOW)
44 : :
45 : : typedef enum {
46 : : PROP_FILES = 1,
47 : : PROP_SELECTION_MODE,
48 : : } ValentShareDialogProperty;
49 : :
50 : : static GParamSpec *properties[PROP_SELECTION_MODE + 1] = { NULL, };
51 : :
52 : : static void valent_share_dialog_set_files (ValentShareDialog *self,
53 : : GListModel *files);
54 : : static void valent_share_dialog_share (ValentShareDialog *self,
55 : : ValentDeviceRow *row);
56 : :
57 : :
58 : : /*
59 : : * Summary
60 : : */
61 : : typedef struct
62 : : {
63 : : ValentShareDialog *self;
64 : : GtkWidget *row;
65 : : GtkImage *icon;
66 : : } EntryData;
67 : :
68 : : static void
69 : 0 : g_file_query_info_cb (GFile *file,
70 : : GAsyncResult *result,
71 : : gpointer user_data)
72 : : {
73 : 0 : g_autofree EntryData *data = (EntryData *)user_data;
74 : 0 : GIcon *icon = NULL;
75 : 0 : g_autoptr (GFileInfo) info = NULL;
76 : 0 : g_autoptr (GError) error = NULL;
77 [ # # # # ]: 0 : g_autofree char *size_str = NULL;
78 : 0 : g_autofree char *size_label = NULL;
79 : 0 : gsize size = 0;
80 : :
81 [ # # ]: 0 : if ((info = g_file_query_info_finish (file, result, &error)) == NULL)
82 : : {
83 [ # # ]: 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
84 : : return;
85 : :
86 [ # # ]: 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
87 : : {
88 : 0 : gtk_widget_add_css_class (GTK_WIDGET (data->row), "error");
89 : : }
90 : :
91 : 0 : return;
92 : : }
93 : :
94 [ # # ]: 0 : if ((icon = g_file_info_get_icon (info)) != NULL)
95 : : {
96 : 0 : gtk_image_set_from_gicon (data->icon, icon);
97 : 0 : gtk_widget_add_css_class (GTK_WIDGET (data->icon), "lowres-icon");
98 : : }
99 : : else
100 : : {
101 : 0 : gtk_image_set_from_icon_name (data->icon, "share-file-symbolic");
102 : 0 : gtk_widget_remove_css_class (GTK_WIDGET (data->icon), "lowres-icon");
103 : : }
104 : :
105 : 0 : size = g_file_info_get_size (info);
106 : 0 : size_str = g_format_size (size);
107 : 0 : size_label = g_strdup_printf (_("Size: %s"), size_str);
108 : 0 : g_object_set (data->row, "subtitle", size_label, NULL);
109 : :
110 [ # # ]: 0 : if (data->self->n_files > 1)
111 : : {
112 : 0 : g_autofree char *total_str = NULL;
113 : 0 : g_autofree char *total_label = NULL;
114 : :
115 [ # # ]: 0 : if ((G_MAXUINT64 - data->self->total_size) < size)
116 : 0 : data->self->total_size = G_MAXUINT64;
117 : : else
118 : 0 : data->self->total_size += size;
119 : :
120 : 0 : total_str = g_format_size (data->self->total_size);
121 : 0 : total_label = g_strdup_printf (_("Total size: %s"), total_str);
122 : 0 : g_object_set (data->self->multiple_row, "subtitle", total_label, NULL);
123 : : }
124 : : }
125 : :
126 : : static void
127 : 0 : valent_share_dialog_add_entry (ValentShareDialog *self,
128 : : GFile *file,
129 : : GCancellable *cancellable)
130 : : {
131 : 0 : g_autofree char *title = NULL;
132 : 0 : const char *icon_name = NULL;
133 : 0 : gboolean is_file = FALSE;
134 : 0 : GtkWidget *row;
135 : 0 : GtkWidget *icon;
136 : :
137 : 0 : is_file = g_file_has_uri_scheme (file, "file");
138 : :
139 [ # # ]: 0 : if (is_file)
140 : : {
141 : 0 : self->n_files += 1;
142 : 0 : title = g_file_get_basename (file);
143 : 0 : icon_name = "share-file-symbolic";
144 : : }
145 : : else
146 : : {
147 : 0 : g_autofree char *uri = NULL;
148 : :
149 : 0 : uri = g_file_get_uri (file);
150 : :
151 : 0 : self->n_links += 1;
152 : 0 : title = g_strdup_printf ("<a href=\"%s\">%s</a>", uri, uri);
153 : 0 : icon_name = "share-link-symbolic";
154 : : }
155 : :
156 : 0 : row = g_object_new (ADW_TYPE_ACTION_ROW,
157 : : "title", title,
158 : : "title-lines", 1,
159 : : NULL);
160 : 0 : icon = g_object_new (GTK_TYPE_IMAGE,
161 : : "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
162 : : "icon-name", icon_name,
163 : : NULL);
164 : 0 : adw_action_row_add_prefix (ADW_ACTION_ROW (row), icon);
165 : 0 : adw_expander_row_add_row (self->multiple_row, row);
166 : 0 : g_ptr_array_add (self->rows, row);
167 : :
168 [ # # ]: 0 : if (is_file)
169 : : {
170 : 0 : EntryData *data = NULL;
171 : :
172 : 0 : data = g_new0 (EntryData, 1);
173 : 0 : data->self = self;
174 : 0 : data->row = (GtkWidget *)row;
175 : 0 : data->icon = (GtkImage *)icon;
176 : :
177 : 0 : g_file_query_info_async (file,
178 : : G_FILE_ATTRIBUTE_STANDARD_SIZE","
179 : : G_FILE_ATTRIBUTE_STANDARD_ICON,
180 : : G_FILE_QUERY_INFO_NONE,
181 : : G_PRIORITY_DEFAULT_IDLE,
182 : : cancellable,
183 : : (GAsyncReadyCallback)g_file_query_info_cb,
184 : : g_steal_pointer (&data));
185 : : }
186 : 0 : }
187 : :
188 : : static void
189 : 2 : valent_share_dialog_reset (ValentShareDialog *self)
190 : : {
191 [ - + ]: 2 : g_assert (VALENT_IS_SHARE_DIALOG (self));
192 : :
193 : 2 : g_cancellable_cancel (self->cancellable);
194 [ + + ]: 2 : g_clear_object (&self->cancellable);
195 : 2 : self->cancellable = g_cancellable_new ();
196 : 2 : self->n_files = 0;
197 : 2 : self->n_links = 0;
198 : 2 : self->total_size = 0;
199 : :
200 : 2 : g_object_set (self->single_row,
201 : : "title", NULL,
202 : : "subtitle", NULL,
203 : : NULL);
204 : 2 : g_object_set (self->multiple_row,
205 : : "title", NULL,
206 : : "subtitle", NULL,
207 : : NULL);
208 : 2 : gtk_editable_set_text (GTK_EDITABLE (self->uri_entry), "");
209 : :
210 [ - + ]: 2 : if (self->rows != NULL)
211 : : {
212 [ # # ]: 0 : for (unsigned int i = 0; i < self->rows->len; i++)
213 : : {
214 : 0 : adw_expander_row_remove (self->multiple_row,
215 : 0 : g_ptr_array_index (self->rows, i));
216 : : }
217 : 0 : g_clear_pointer (&self->rows, g_ptr_array_unref);
218 : : }
219 : 2 : }
220 : :
221 : : static void
222 : 1 : on_files_changed (GListModel *list,
223 : : unsigned int position,
224 : : unsigned int removed,
225 : : unsigned int added,
226 : : ValentShareDialog *self)
227 : : {
228 : 1 : unsigned int n_items = 0;
229 : :
230 [ - + ]: 1 : g_assert (VALENT_IS_SHARE_DIALOG (self));
231 : :
232 : 1 : valent_share_dialog_reset (self);
233 : :
234 [ + - ]: 1 : if (self->files != NULL)
235 : 1 : n_items = g_list_model_get_n_items (self->files);
236 : :
237 [ - + ]: 1 : if (n_items > 1)
238 : : {
239 : 0 : g_autofree char *title = NULL;
240 : :
241 : 0 : self->rows = g_ptr_array_sized_new (n_items);
242 : :
243 [ # # ]: 0 : for (unsigned int i = 0; i < n_items; i++)
244 : : {
245 : 0 : g_autoptr (GFile) file = NULL;
246 : :
247 : 0 : file = g_list_model_get_item (self->files, i);
248 [ # # ]: 0 : valent_share_dialog_add_entry (self, file, self->cancellable);
249 : : }
250 : :
251 [ # # # # ]: 0 : if (self->n_files > 0 && self->n_links > 0)
252 : 0 : title = g_strdup_printf (_("%u files and links"), n_items);
253 [ # # ]: 0 : else if (self->n_files > 0)
254 : 0 : title = g_strdup_printf (_("%u files"), n_items);
255 [ # # ]: 0 : else if (self->n_links > 0)
256 : 0 : title = g_strdup_printf (_("%u links"), n_items);
257 : :
258 : 0 : g_object_set (self->multiple_row,
259 : : "title", title,
260 : : "visible", TRUE,
261 : : NULL);
262 : : }
263 [ + - ]: 1 : else if (n_items > 0)
264 : : {
265 : 1 : g_autofree char *title = NULL;
266 : 1 : g_autoptr (GFile) entry = NULL;
267 : 1 : const char *icon_name = NULL;
268 : :
269 : 1 : entry = g_list_model_get_item (self->files, 0);
270 : :
271 [ - + ]: 1 : if (g_file_has_uri_scheme (entry, "file"))
272 : : {
273 : 0 : EntryData *data;
274 : :
275 : 0 : data = g_new0 (EntryData, 1);
276 : 0 : data->self = self;
277 : 0 : data->row = (GtkWidget *)self->single_row;
278 : 0 : data->icon = self->single_icon;
279 : :
280 : 0 : g_file_query_info_async (entry,
281 : : G_FILE_ATTRIBUTE_STANDARD_SIZE","
282 : : G_FILE_ATTRIBUTE_STANDARD_ICON,
283 : : G_FILE_QUERY_INFO_NONE,
284 : : G_PRIORITY_DEFAULT_IDLE,
285 : : self->cancellable,
286 : : (GAsyncReadyCallback)g_file_query_info_cb,
287 : : g_steal_pointer (&data));
288 : :
289 : 0 : title = g_file_get_basename (entry);
290 : 0 : icon_name = "share-symbolic";
291 : : }
292 : : else
293 : : {
294 : 1 : g_autofree char *uri = NULL;
295 : :
296 : 1 : uri = g_file_get_uri (entry);
297 : :
298 : 1 : title = g_strdup_printf ("<a href=\"%s\">%s</a>", uri, uri);
299 : 1 : icon_name = "share-link-symbolic";
300 : : }
301 : :
302 : 1 : g_object_set (self->single_row,
303 : : "title", title,
304 : : "visible", TRUE,
305 : : NULL);
306 : 1 : gtk_image_set_from_icon_name (self->single_icon, icon_name);
307 [ + - ]: 1 : gtk_widget_remove_css_class (GTK_WIDGET (self->single_icon), "lowres-icon");
308 : : }
309 : 1 : }
310 : :
311 : : /*
312 : : * Devices
313 : : */
314 : : static void
315 : 1 : on_action_added (GActionGroup *action_group,
316 : : const char *action_name,
317 : : GtkWidget *widget)
318 : : {
319 : 1 : gboolean visible = FALSE;
320 : :
321 [ - + ]: 1 : if (g_action_group_get_action_enabled (action_group, action_name))
322 : 0 : visible = TRUE;
323 : :
324 : 1 : gtk_widget_set_visible (widget, visible);
325 : 1 : }
326 : :
327 : : static void
328 : 1 : on_action_removed (GActionGroup *action_group,
329 : : const char *action_name,
330 : : GtkWidget *widget)
331 : : {
332 : 1 : gtk_widget_set_visible (widget, FALSE);
333 : 1 : }
334 : :
335 : : static void
336 : 0 : on_action_enabled_changed (GActionGroup *action_group,
337 : : const char *action_name,
338 : : gboolean enabled,
339 : : GtkWidget *widget)
340 : : {
341 : 0 : gtk_widget_set_visible (widget, enabled);
342 : 0 : }
343 : :
344 : : static void
345 : 0 : on_device_activated (GtkListBox *box,
346 : : ValentDeviceRow *row,
347 : : ValentShareDialog *self)
348 : : {
349 [ # # # # : 0 : g_assert (GTK_IS_LIST_BOX (box));
# # # # ]
350 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_ROW (row));
351 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
352 : :
353 [ # # ]: 0 : if (self->selection_mode)
354 : : {
355 : 0 : gboolean selected;
356 : :
357 : 0 : selected = valent_device_row_get_selected (row);
358 : 0 : valent_device_row_set_selected (row, !selected);
359 : : }
360 : : else
361 : : {
362 : 0 : valent_share_dialog_share (self, row);
363 : : }
364 : 0 : }
365 : :
366 : : static void
367 : 4 : on_selected_changed (ValentShareDialog *self)
368 : : {
369 : 4 : GtkWidget *child;
370 : 4 : gboolean enabled = FALSE;
371 : :
372 [ + - ]: 4 : if (!self->selection_mode)
373 : : {
374 : 4 : gtk_widget_action_set_enabled (GTK_WIDGET (self),
375 : : "chooser.share",
376 : : enabled);
377 : 4 : return;
378 : : }
379 : :
380 : 0 : for (child = gtk_widget_get_first_child (GTK_WIDGET (self->device_list));
381 [ # # ]: 0 : child != NULL;
382 : 0 : child = gtk_widget_get_next_sibling (child))
383 : : {
384 [ # # ]: 0 : if (!VALENT_IS_DEVICE_ROW (child))
385 : 0 : continue;
386 : :
387 [ # # ]: 0 : if (valent_device_row_get_selected (VALENT_DEVICE_ROW (child)))
388 : : {
389 : : enabled = TRUE;
390 : : break;
391 : : }
392 : : }
393 : :
394 : 0 : gtk_widget_action_set_enabled (GTK_WIDGET (self), "chooser.share", enabled);
395 : : }
396 : :
397 : : static GtkWidget *
398 : 1 : valent_share_dialog_create_row (gpointer item,
399 : : gpointer user_data)
400 : : {
401 : 1 : ValentShareDialog *self = VALENT_SHARE_DIALOG (user_data);
402 : 1 : ValentDevice *device = VALENT_DEVICE (item);
403 : 1 : GtkWidget *row;
404 : :
405 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
406 : :
407 : 1 : row = g_object_new (VALENT_TYPE_DEVICE_ROW,
408 : : "device", device,
409 : 1 : "selection-mode", self->selection_mode,
410 : : NULL);
411 : 1 : g_object_bind_property (self, "selection-mode",
412 : : row, "selection-mode",
413 : : G_BINDING_SYNC_CREATE);
414 : 1 : g_signal_connect_object (row,
415 : : "notify::selected",
416 : : G_CALLBACK (on_selected_changed),
417 : : self,
418 : : G_CONNECT_SWAPPED);
419 : 1 : g_signal_connect_object (device,
420 : : "action-added::share.uris",
421 : : G_CALLBACK (on_action_added),
422 : : row,
423 : : G_CONNECT_DEFAULT);
424 : 1 : g_signal_connect_object (device,
425 : : "action-removed::share.uris",
426 : : G_CALLBACK (on_action_removed),
427 : : row,
428 : : G_CONNECT_DEFAULT);
429 : 1 : g_signal_connect_object (device,
430 : : "action-enabled-changed::share.uris",
431 : : G_CALLBACK (on_action_enabled_changed),
432 : : row,
433 : : G_CONNECT_DEFAULT);
434 : 1 : on_action_added (G_ACTION_GROUP (device), "share.uris", row);
435 : :
436 : 1 : return row;
437 : : }
438 : :
439 : : static void
440 : 4 : on_items_changed (GListModel *list,
441 : : unsigned int position,
442 : : unsigned int removed,
443 : : unsigned int added,
444 : : ValentShareDialog *self)
445 : : {
446 [ + - ]: 4 : g_assert (VALENT_IS_SHARE_DIALOG (self));
447 : :
448 [ + + ]: 5 : while (removed--)
449 : : {
450 : 1 : GtkListBoxRow *row;
451 : :
452 : 1 : row = gtk_list_box_get_row_at_index (self->device_list, position);
453 : 1 : gtk_list_box_remove (self->device_list, GTK_WIDGET (row));
454 : : }
455 : :
456 [ + + ]: 5 : for (unsigned int i = 0; i < added; i++)
457 : : {
458 : 1 : g_autoptr (GObject) item = NULL;
459 [ + - ]: 1 : g_autoptr (GtkWidget) widget = NULL;
460 : :
461 : 1 : item = g_list_model_get_item (list, position + i);
462 : 1 : widget = valent_share_dialog_create_row (item, self);
463 : :
464 [ + - ]: 1 : if (g_object_is_floating (widget))
465 : 1 : g_object_ref_sink (widget);
466 : :
467 [ + - ]: 1 : gtk_list_box_insert (self->device_list, widget, position + i);
468 : : }
469 : :
470 : 4 : on_selected_changed (self);
471 : 4 : }
472 : :
473 : : static gboolean
474 : 0 : valent_share_dialog_refresh (gpointer data)
475 : : {
476 : 0 : ValentDeviceManager *manager = VALENT_DEVICE_MANAGER (data);
477 : :
478 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
479 : :
480 : 0 : valent_device_manager_refresh (manager);
481 : :
482 : 0 : return G_SOURCE_CONTINUE;
483 : : }
484 : :
485 : : /*
486 : : * URI Selector
487 : : */
488 : : static void
489 : 0 : on_uri_activated (GtkEditable *editable,
490 : : ValentShareDialog *self)
491 : : {
492 : 0 : const char *text = NULL;
493 : :
494 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
495 : :
496 : 0 : text = gtk_editable_get_text (editable);
497 [ # # # # ]: 0 : if (text == NULL || *text == '\0')
498 : : return;
499 : :
500 [ # # ]: 0 : if (!gtk_widget_has_css_class (GTK_WIDGET (self->uri_entry), "error"))
501 : : {
502 : 0 : g_autoptr (GListStore) files = NULL;
503 [ # # ]: 0 : g_autoptr (GFile) file = NULL;
504 : :
505 : 0 : file = g_file_new_for_uri (text);
506 : 0 : files = g_list_store_new (G_TYPE_FILE);
507 : 0 : g_list_store_append (files, file);
508 : :
509 [ # # ]: 0 : valent_share_dialog_set_files (self, G_LIST_MODEL (files));
510 : : }
511 : : }
512 : :
513 : : static void
514 : 0 : on_uri_changed (GtkEditable *editable,
515 : : ValentShareDialog *self)
516 : : {
517 : 0 : const char *text = NULL;
518 : 0 : const char *scheme = NULL;
519 : :
520 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
521 : :
522 : 0 : text = gtk_editable_get_text (editable);
523 [ # # # # ]: 0 : if (text == NULL || *text == '\0')
524 : : {
525 : 0 : gtk_widget_remove_css_class (GTK_WIDGET (self->uri_entry), "error");
526 : 0 : gtk_accessible_reset_state (GTK_ACCESSIBLE (self->uri_entry),
527 : : GTK_ACCESSIBLE_STATE_INVALID);
528 : 0 : return;
529 : : }
530 : :
531 : 0 : scheme = g_uri_peek_scheme (text);
532 [ # # # # ]: 0 : if (scheme != NULL && !g_str_equal (scheme, "file"))
533 : : {
534 : 0 : gtk_widget_remove_css_class (GTK_WIDGET (self->uri_entry), "error");
535 : 0 : gtk_accessible_reset_state (GTK_ACCESSIBLE (self->uri_entry),
536 : : GTK_ACCESSIBLE_STATE_INVALID);
537 : : }
538 : : else
539 : : {
540 : 0 : gtk_widget_add_css_class (GTK_WIDGET (self->uri_entry), "error");
541 : 0 : gtk_accessible_update_state (GTK_ACCESSIBLE (self->uri_entry),
542 : : GTK_ACCESSIBLE_STATE_INVALID, TRUE,
543 : : -1);
544 : : }
545 : : }
546 : :
547 : : /*
548 : : * GAction
549 : : */
550 : : static void
551 : 0 : gtk_file_dialog_open_multiple_cb (GtkFileDialog *dialog,
552 : : GAsyncResult *result,
553 : : ValentShareDialog *self)
554 : : {
555 : 0 : g_autoptr (GListModel) files = NULL;
556 : 0 : g_autoptr (GError) error = NULL;
557 : :
558 : 0 : files = gtk_file_dialog_open_multiple_finish (dialog, result, &error);
559 : :
560 [ # # ]: 0 : if (files == NULL)
561 : : {
562 [ # # # # ]: 0 : if (!g_error_matches (error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED) &&
563 : 0 : !g_error_matches (error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED))
564 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
565 : :
566 [ # # ]: 0 : return;
567 : : }
568 : :
569 [ # # ]: 0 : valent_share_dialog_set_files (self, files);
570 : : }
571 : :
572 : : static void
573 : 0 : chooser_select_files_action (GtkWidget *widget,
574 : : const char *action_name,
575 : : GVariant *parameter)
576 : : {
577 : 0 : ValentShareDialog *self = VALENT_SHARE_DIALOG (widget);
578 : 0 : g_autoptr (GtkFileDialog) dialog = NULL;
579 : :
580 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
581 : :
582 : 0 : dialog = gtk_file_dialog_new ();
583 [ # # ]: 0 : gtk_file_dialog_open_multiple (dialog,
584 : : GTK_WINDOW (self),
585 : : NULL,
586 : : (GAsyncReadyCallback)gtk_file_dialog_open_multiple_cb,
587 : : self);
588 : 0 : }
589 : :
590 : : static void
591 : 0 : chooser_share_action (GtkWidget *widget,
592 : : const char *action_name,
593 : : GVariant *parameter)
594 : : {
595 : 0 : ValentShareDialog *self = VALENT_SHARE_DIALOG (widget);
596 : 0 : GtkWidget *child;
597 : :
598 : 0 : for (child = gtk_widget_get_first_child (GTK_WIDGET (self->device_list));
599 [ # # ]: 0 : child != NULL;
600 : 0 : child = gtk_widget_get_next_sibling (child))
601 : : {
602 [ # # ]: 0 : if (!VALENT_IS_DEVICE_ROW (child))
603 : 0 : continue;
604 : :
605 [ # # ]: 0 : if (!valent_device_row_get_selected (VALENT_DEVICE_ROW (child)))
606 : 0 : continue;
607 : :
608 : 0 : valent_share_dialog_share (self, VALENT_DEVICE_ROW (child));
609 : : }
610 : 0 : }
611 : :
612 : : /*
613 : : * ValentShareDialog
614 : : */
615 : : static void
616 : 1 : valent_share_dialog_set_files (ValentShareDialog *self,
617 : : GListModel *files)
618 : : {
619 : 1 : unsigned int n_items = 0;
620 : :
621 [ - + ]: 1 : g_assert (VALENT_IS_SHARE_DIALOG (self));
622 [ + - + - ]: 1 : g_assert (files == NULL || G_IS_LIST_MODEL (files));
623 : :
624 [ + - ]: 1 : if (!g_set_object (&self->files, files))
625 : : return;
626 : :
627 : 1 : valent_share_dialog_reset (self);
628 : :
629 [ + - ]: 1 : if (self->files != NULL)
630 : : {
631 : 1 : n_items = g_list_model_get_n_items (files);
632 : 1 : g_signal_connect_object (self->files,
633 : : "items-changed",
634 : : G_CALLBACK (on_files_changed),
635 : : self,
636 : : G_CONNECT_DEFAULT);
637 : 1 : on_files_changed (self->files,
638 : : 0,
639 : 1 : self->n_links + self->n_files,
640 : : n_items,
641 : : self);
642 : : }
643 : :
644 [ + - ]: 1 : if (n_items > 0)
645 : 1 : adw_navigation_view_push_by_tag (self->view, "device");
646 : : else
647 : 0 : adw_navigation_view_pop (self->view);
648 : :
649 : 1 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILES]);
650 : : }
651 : :
652 : : static void
653 : 1 : valent_share_dialog_set_selection_mode (ValentShareDialog *self,
654 : : gboolean selection_mode)
655 : : {
656 [ - + ]: 1 : g_assert (VALENT_IS_SHARE_DIALOG (self));
657 : :
658 : 1 : selection_mode = !!selection_mode;
659 [ - + ]: 1 : if (self->selection_mode == selection_mode)
660 : : return;
661 : :
662 [ # # ]: 0 : gtk_list_box_set_selection_mode (self->device_list,
663 : : selection_mode
664 : : ? GTK_SELECTION_MULTIPLE
665 : : : GTK_SELECTION_NONE);
666 : :
667 : 0 : self->selection_mode = selection_mode;
668 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTION_MODE]);
669 : : }
670 : :
671 : : static void
672 : 0 : valent_share_dialog_share (ValentShareDialog *self,
673 : : ValentDeviceRow *row)
674 : : {
675 : 0 : ValentDevice *device = NULL;
676 : 0 : GVariantBuilder builder;
677 : 0 : unsigned int n_files = 0;
678 : :
679 : 0 : g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
680 : 0 : n_files = g_list_model_get_n_items (self->files);
681 : :
682 [ # # ]: 0 : for (unsigned int i = 0; i < n_files; i++)
683 : : {
684 : 0 : g_autoptr (GFile) file = g_list_model_get_item (self->files, i);
685 : 0 : GVariant *uri = g_variant_new_take_string (g_file_get_uri (file));
686 : :
687 [ # # ]: 0 : g_variant_builder_add_value (&builder, uri);
688 : : }
689 : :
690 : 0 : device = valent_device_row_get_device (row);
691 : 0 : g_action_group_activate_action (G_ACTION_GROUP (device),
692 : : "share.uris",
693 : : g_variant_builder_end (&builder));
694 : :
695 : 0 : gtk_window_close (GTK_WINDOW (self));
696 : 0 : }
697 : :
698 : : /*
699 : : * GObject
700 : : */
701 : : static void
702 : 1 : valent_share_dialog_constructed (GObject *object)
703 : : {
704 : 1 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
705 : :
706 : 1 : G_OBJECT_CLASS (valent_share_dialog_parent_class)->constructed (object);
707 : :
708 : 1 : self->manager = valent_device_manager_get_default ();
709 : 1 : g_signal_connect_object (self->manager,
710 : : "items-changed",
711 : : G_CALLBACK (on_items_changed),
712 : : self,
713 : : G_CONNECT_DEFAULT);
714 : 1 : on_items_changed (G_LIST_MODEL (self->manager),
715 : : 0,
716 : : 0,
717 : 1 : g_list_model_get_n_items (G_LIST_MODEL (self->manager)),
718 : : self);
719 : :
720 : : /* Broadcast every 5 seconds to re-connect devices that may have gone idle */
721 : 1 : valent_device_manager_refresh (self->manager);
722 : 1 : self->refresh_id = g_timeout_add_seconds_full (G_PRIORITY_LOW,
723 : : 5,
724 : : valent_share_dialog_refresh,
725 : 1 : g_object_ref (self->manager),
726 : : g_object_unref);
727 : 1 : }
728 : :
729 : : static void
730 : 1 : valent_share_dialog_dispose (GObject *object)
731 : : {
732 : 1 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
733 : :
734 [ + - ]: 1 : g_clear_handle_id (&self->refresh_id, g_source_remove);
735 : :
736 [ + - ]: 1 : if (self->manager != NULL)
737 : : {
738 : 1 : g_signal_handlers_disconnect_by_data (self->manager, self);
739 : 1 : self->manager = NULL;
740 : : }
741 : :
742 [ + - ]: 1 : if (self->cancellable != NULL)
743 : : {
744 : 1 : g_cancellable_cancel (self->cancellable);
745 [ + - ]: 1 : g_clear_object (&self->cancellable);
746 : : }
747 : :
748 [ + - ]: 1 : g_clear_object (&self->files);
749 [ - + ]: 1 : g_clear_pointer (&self->rows, g_ptr_array_unref);
750 : :
751 : 1 : gtk_widget_dispose_template (GTK_WIDGET (object),
752 : : VALENT_TYPE_SHARE_DIALOG);
753 : :
754 : 1 : G_OBJECT_CLASS (valent_share_dialog_parent_class)->dispose (object);
755 : 1 : }
756 : :
757 : : static void
758 : 4 : valent_share_dialog_get_property (GObject *object,
759 : : guint prop_id,
760 : : GValue *value,
761 : : GParamSpec *pspec)
762 : : {
763 : 4 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
764 : :
765 [ + + - ]: 4 : switch ((ValentShareDialogProperty)prop_id)
766 : : {
767 : 1 : case PROP_FILES:
768 : 1 : g_value_set_object (value, self->files);
769 : 1 : break;
770 : :
771 : 3 : case PROP_SELECTION_MODE:
772 : 3 : g_value_set_boolean (value, self->selection_mode);
773 : 3 : break;
774 : :
775 : 0 : default:
776 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
777 : : }
778 : 4 : }
779 : :
780 : : static void
781 : 2 : valent_share_dialog_set_property (GObject *object,
782 : : guint prop_id,
783 : : const GValue *value,
784 : : GParamSpec *pspec)
785 : : {
786 : 2 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
787 : :
788 [ + + - ]: 2 : switch ((ValentShareDialogProperty)prop_id)
789 : : {
790 : 1 : case PROP_FILES:
791 : 1 : valent_share_dialog_set_files (self, g_value_get_object (value));
792 : 1 : break;
793 : :
794 : 1 : case PROP_SELECTION_MODE:
795 : 1 : valent_share_dialog_set_selection_mode (self, g_value_get_boolean (value));
796 : 1 : break;
797 : :
798 : 0 : default:
799 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
800 : : }
801 : 2 : }
802 : :
803 : : static void
804 : 1 : valent_share_dialog_class_init (ValentShareDialogClass *klass)
805 : : {
806 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
807 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
808 : :
809 : 1 : object_class->constructed = valent_share_dialog_constructed;
810 : 1 : object_class->dispose = valent_share_dialog_dispose;
811 : 1 : object_class->get_property = valent_share_dialog_get_property;
812 : 1 : object_class->set_property = valent_share_dialog_set_property;
813 : :
814 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-share-dialog.ui");
815 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, view);
816 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, device_list);
817 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, single_row);
818 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, single_icon);
819 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, multiple_row);
820 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, multiple_icon);
821 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, uri_entry);
822 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_device_activated);
823 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_uri_activated);
824 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_uri_changed);
825 : :
826 : 1 : gtk_widget_class_install_action (widget_class, "chooser.share", NULL, chooser_share_action);
827 : 1 : gtk_widget_class_install_action (widget_class, "chooser.select-files", NULL, chooser_select_files_action);
828 : :
829 : : /**
830 : : * ValentShareDialog:files:
831 : : *
832 : : * The URIs to share.
833 : : */
834 : 2 : properties [PROP_FILES] =
835 : 1 : g_param_spec_object ("files", NULL, NULL,
836 : : G_TYPE_LIST_MODEL,
837 : : (G_PARAM_READWRITE |
838 : : G_PARAM_CONSTRUCT_ONLY |
839 : : G_PARAM_EXPLICIT_NOTIFY |
840 : : G_PARAM_STATIC_STRINGS));
841 : :
842 : : /**
843 : : * ValentShareDialog:selection-mode:
844 : : *
845 : : * Whether multiple devices can be selected.
846 : : */
847 : 2 : properties [PROP_SELECTION_MODE] =
848 : 1 : g_param_spec_boolean ("selection-mode", NULL, NULL,
849 : : FALSE,
850 : : (G_PARAM_READWRITE |
851 : : G_PARAM_CONSTRUCT |
852 : : G_PARAM_EXPLICIT_NOTIFY |
853 : : G_PARAM_STATIC_STRINGS));
854 : :
855 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
856 : 1 : }
857 : :
858 : : static void
859 : 1 : valent_share_dialog_init (ValentShareDialog *self)
860 : : {
861 : 1 : gtk_widget_init_template (GTK_WIDGET (self));
862 : 1 : }
863 : :
|