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