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