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 : : 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 : 0 : on_action_removed (GActionGroup *action_group,
329 : : const char *action_name,
330 : : GtkWidget *widget)
331 : : {
332 : 0 : gtk_widget_set_visible (widget, FALSE);
333 : 0 : }
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 : 3 : on_selected_changed (ValentShareDialog *self)
368 : : {
369 : 3 : GtkWidget *child;
370 : 3 : gboolean enabled = FALSE;
371 : :
372 [ + - ]: 3 : if (!self->selection_mode)
373 : : {
374 : 3 : gtk_widget_action_set_enabled (GTK_WIDGET (self),
375 : : "chooser.share",
376 : : enabled);
377 : 3 : 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, G_CONNECT_SWAPPED);
418 : 1 : g_signal_connect_object (device,
419 : : "action-added::share.uris",
420 : : G_CALLBACK (on_action_added),
421 : : row, 0);
422 : 1 : g_signal_connect_object (device,
423 : : "action-removed::share.uris",
424 : : G_CALLBACK (on_action_removed),
425 : : row, 0);
426 : 1 : g_signal_connect_object (device,
427 : : "action-enabled-changed::share.uris",
428 : : G_CALLBACK (on_action_enabled_changed),
429 : : row, 0);
430 : 1 : on_action_added (G_ACTION_GROUP (device), "share.uris", row);
431 : :
432 : 1 : return row;
433 : : }
434 : :
435 : : static void
436 : 3 : on_items_changed (GListModel *list,
437 : : unsigned int position,
438 : : unsigned int removed,
439 : : unsigned int added,
440 : : ValentShareDialog *self)
441 : : {
442 [ + - ]: 3 : g_assert (VALENT_IS_SHARE_DIALOG (self));
443 : :
444 [ + + ]: 4 : while (removed--)
445 : : {
446 : 1 : GtkListBoxRow *row;
447 : :
448 : 1 : row = gtk_list_box_get_row_at_index (self->device_list, position);
449 : 1 : gtk_list_box_remove (self->device_list, GTK_WIDGET (row));
450 : : }
451 : :
452 [ + + ]: 4 : for (unsigned int i = 0; i < added; i++)
453 : : {
454 : 1 : g_autoptr (GObject) item = NULL;
455 [ + - ]: 1 : g_autoptr (GtkWidget) widget = NULL;
456 : :
457 : 1 : item = g_list_model_get_item (list, position + i);
458 : 1 : widget = valent_share_dialog_create_row (item, self);
459 : :
460 [ + - ]: 1 : if (g_object_is_floating (widget))
461 : 1 : g_object_ref_sink (widget);
462 : :
463 [ + - ]: 1 : gtk_list_box_insert (self->device_list, widget, position + i);
464 : : }
465 : :
466 : 3 : on_selected_changed (self);
467 : 3 : }
468 : :
469 : : static gboolean
470 : 0 : valent_share_dialog_refresh (gpointer data)
471 : : {
472 : 0 : ValentDeviceManager *manager = VALENT_DEVICE_MANAGER (data);
473 : :
474 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
475 : :
476 : 0 : valent_device_manager_refresh (manager);
477 : :
478 : 0 : return G_SOURCE_CONTINUE;
479 : : }
480 : :
481 : : /*
482 : : * URI Selector
483 : : */
484 : : static void
485 : 0 : on_uri_activated (GtkEditable *editable,
486 : : ValentShareDialog *self)
487 : : {
488 : 0 : const char *text = NULL;
489 : :
490 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
491 : :
492 : 0 : text = gtk_editable_get_text (editable);
493 [ # # # # ]: 0 : if (text == NULL || *text == '\0')
494 : : return;
495 : :
496 [ # # ]: 0 : if (!gtk_widget_has_css_class (GTK_WIDGET (self->uri_entry), "error"))
497 : : {
498 : 0 : g_autoptr (GListStore) files = NULL;
499 [ # # ]: 0 : g_autoptr (GFile) file = NULL;
500 : :
501 : 0 : file = g_file_new_for_uri (text);
502 : 0 : files = g_list_store_new (G_TYPE_FILE);
503 : 0 : g_list_store_append (files, file);
504 : :
505 [ # # ]: 0 : valent_share_dialog_set_files (self, G_LIST_MODEL (files));
506 : : }
507 : : }
508 : :
509 : : static void
510 : 0 : on_uri_changed (GtkEditable *editable,
511 : : ValentShareDialog *self)
512 : : {
513 : 0 : const char *text = NULL;
514 : 0 : const char *scheme = NULL;
515 : :
516 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
517 : :
518 : 0 : text = gtk_editable_get_text (editable);
519 [ # # # # ]: 0 : if (text == NULL || *text == '\0')
520 : : {
521 : 0 : gtk_widget_remove_css_class (GTK_WIDGET (self->uri_entry), "error");
522 : 0 : gtk_accessible_reset_state (GTK_ACCESSIBLE (self->uri_entry),
523 : : GTK_ACCESSIBLE_STATE_INVALID);
524 : 0 : return;
525 : : }
526 : :
527 : 0 : scheme = g_uri_peek_scheme (text);
528 [ # # # # ]: 0 : if (scheme != NULL && !g_str_equal (scheme, "file"))
529 : : {
530 : 0 : gtk_widget_remove_css_class (GTK_WIDGET (self->uri_entry), "error");
531 : 0 : gtk_accessible_reset_state (GTK_ACCESSIBLE (self->uri_entry),
532 : : GTK_ACCESSIBLE_STATE_INVALID);
533 : : }
534 : : else
535 : : {
536 : 0 : gtk_widget_add_css_class (GTK_WIDGET (self->uri_entry), "error");
537 : 0 : gtk_accessible_update_state (GTK_ACCESSIBLE (self->uri_entry),
538 : : GTK_ACCESSIBLE_STATE_INVALID, TRUE,
539 : : -1);
540 : : }
541 : : }
542 : :
543 : : /*
544 : : * GAction
545 : : */
546 : : static void
547 : 0 : gtk_file_dialog_open_multiple_cb (GtkFileDialog *dialog,
548 : : GAsyncResult *result,
549 : : ValentShareDialog *self)
550 : : {
551 : 0 : g_autoptr (GListModel) files = NULL;
552 : 0 : g_autoptr (GError) error = NULL;
553 : :
554 : 0 : files = gtk_file_dialog_open_multiple_finish (dialog, result, &error);
555 : :
556 [ # # ]: 0 : if (files == NULL)
557 : : {
558 [ # # # # ]: 0 : if (!g_error_matches (error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED) &&
559 : 0 : !g_error_matches (error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED))
560 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
561 : :
562 [ # # ]: 0 : return;
563 : : }
564 : :
565 [ # # ]: 0 : valent_share_dialog_set_files (self, files);
566 : : }
567 : :
568 : : static void
569 : 0 : chooser_select_files_action (GtkWidget *widget,
570 : : const char *action_name,
571 : : GVariant *parameter)
572 : : {
573 : 0 : ValentShareDialog *self = VALENT_SHARE_DIALOG (widget);
574 : 0 : g_autoptr (GtkFileDialog) dialog = NULL;
575 : :
576 [ # # ]: 0 : g_assert (VALENT_IS_SHARE_DIALOG (self));
577 : :
578 : 0 : dialog = gtk_file_dialog_new ();
579 [ # # ]: 0 : gtk_file_dialog_open_multiple (dialog,
580 : : GTK_WINDOW (self),
581 : : NULL,
582 : : (GAsyncReadyCallback)gtk_file_dialog_open_multiple_cb,
583 : : self);
584 : 0 : }
585 : :
586 : : static void
587 : 0 : chooser_share_action (GtkWidget *widget,
588 : : const char *action_name,
589 : : GVariant *parameter)
590 : : {
591 : 0 : ValentShareDialog *self = VALENT_SHARE_DIALOG (widget);
592 : 0 : GtkWidget *child;
593 : :
594 : 0 : for (child = gtk_widget_get_first_child (GTK_WIDGET (self->device_list));
595 [ # # ]: 0 : child != NULL;
596 : 0 : child = gtk_widget_get_next_sibling (child))
597 : : {
598 [ # # ]: 0 : if (!VALENT_IS_DEVICE_ROW (child))
599 : 0 : continue;
600 : :
601 [ # # ]: 0 : if (!valent_device_row_get_selected (VALENT_DEVICE_ROW (child)))
602 : 0 : continue;
603 : :
604 : 0 : valent_share_dialog_share (self, VALENT_DEVICE_ROW (child));
605 : : }
606 : 0 : }
607 : :
608 : : /*
609 : : * ValentShareDialog
610 : : */
611 : : static void
612 : 1 : valent_share_dialog_set_files (ValentShareDialog *self,
613 : : GListModel *files)
614 : : {
615 : 1 : unsigned int n_items = 0;
616 : :
617 [ + - ]: 1 : g_assert (VALENT_IS_SHARE_DIALOG (self));
618 [ + - - + ]: 1 : g_assert (files == NULL || G_IS_LIST_MODEL (files));
619 : :
620 [ + - ]: 1 : if (!g_set_object (&self->files, files))
621 : : return;
622 : :
623 : 1 : valent_share_dialog_reset (self);
624 : :
625 [ + - ]: 1 : if (self->files != NULL)
626 : : {
627 : 1 : n_items = g_list_model_get_n_items (files);
628 : 1 : g_signal_connect_object (self->files,
629 : : "items-changed",
630 : : G_CALLBACK (on_files_changed),
631 : : self,
632 : : G_CONNECT_DEFAULT);
633 : 1 : on_files_changed (self->files,
634 : : 0,
635 : 1 : self->n_links + self->n_files,
636 : : n_items,
637 : : self);
638 : : }
639 : :
640 [ + - ]: 1 : if (n_items > 0)
641 : 1 : adw_navigation_view_push_by_tag (self->view, "device");
642 : : else
643 : 0 : adw_navigation_view_pop (self->view);
644 : :
645 : 1 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILES]);
646 : : }
647 : :
648 : : static void
649 : 1 : valent_share_dialog_set_selection_mode (ValentShareDialog *self,
650 : : gboolean selection_mode)
651 : : {
652 [ + - ]: 1 : g_assert (VALENT_IS_SHARE_DIALOG (self));
653 : :
654 : 1 : selection_mode = !!selection_mode;
655 [ - + ]: 1 : if (self->selection_mode == selection_mode)
656 : : return;
657 : :
658 [ # # ]: 0 : gtk_list_box_set_selection_mode (self->device_list,
659 : : selection_mode
660 : : ? GTK_SELECTION_MULTIPLE
661 : : : GTK_SELECTION_NONE);
662 : :
663 : 0 : self->selection_mode = selection_mode;
664 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTION_MODE]);
665 : : }
666 : :
667 : : static void
668 : 0 : valent_share_dialog_share (ValentShareDialog *self,
669 : : ValentDeviceRow *row)
670 : : {
671 : 0 : ValentDevice *device = NULL;
672 : 0 : GVariantBuilder builder;
673 : 0 : unsigned int n_files = 0;
674 : :
675 : 0 : g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
676 : 0 : n_files = g_list_model_get_n_items (self->files);
677 : :
678 [ # # ]: 0 : for (unsigned int i = 0; i < n_files; i++)
679 : : {
680 : 0 : g_autoptr (GFile) file = g_list_model_get_item (self->files, i);
681 : 0 : GVariant *uri = g_variant_new_take_string (g_file_get_uri (file));
682 : :
683 [ # # ]: 0 : g_variant_builder_add_value (&builder, uri);
684 : : }
685 : :
686 : 0 : device = valent_device_row_get_device (row);
687 : 0 : g_action_group_activate_action (G_ACTION_GROUP (device),
688 : : "share.uris",
689 : : g_variant_builder_end (&builder));
690 : :
691 : 0 : gtk_window_close (GTK_WINDOW (self));
692 : 0 : }
693 : :
694 : : /*
695 : : * GObject
696 : : */
697 : : static void
698 : 1 : valent_share_dialog_constructed (GObject *object)
699 : : {
700 : 1 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
701 : :
702 : 1 : G_OBJECT_CLASS (valent_share_dialog_parent_class)->constructed (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 : 1 : }
723 : :
724 : : static void
725 : 1 : valent_share_dialog_dispose (GObject *object)
726 : : {
727 : 1 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
728 : :
729 [ + - ]: 1 : g_clear_handle_id (&self->refresh_id, g_source_remove);
730 : :
731 [ + - ]: 1 : if (self->manager != NULL)
732 : : {
733 : 1 : g_signal_handlers_disconnect_by_data (self->manager, self);
734 : 1 : self->manager = NULL;
735 : : }
736 : :
737 [ + - ]: 1 : if (self->cancellable != NULL)
738 : : {
739 : 1 : g_cancellable_cancel (self->cancellable);
740 [ + - ]: 1 : g_clear_object (&self->cancellable);
741 : : }
742 : :
743 [ + - ]: 1 : g_clear_object (&self->files);
744 [ - + ]: 1 : g_clear_pointer (&self->rows, g_ptr_array_unref);
745 : :
746 : 1 : gtk_widget_dispose_template (GTK_WIDGET (object),
747 : : VALENT_TYPE_SHARE_DIALOG);
748 : :
749 : 1 : G_OBJECT_CLASS (valent_share_dialog_parent_class)->dispose (object);
750 : 1 : }
751 : :
752 : : static void
753 : 4 : valent_share_dialog_get_property (GObject *object,
754 : : guint prop_id,
755 : : GValue *value,
756 : : GParamSpec *pspec)
757 : : {
758 : 4 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
759 : :
760 [ + + - ]: 4 : switch ((ValentShareDialogProperty)prop_id)
761 : : {
762 : 1 : case PROP_FILES:
763 : 1 : g_value_set_object (value, self->files);
764 : 1 : break;
765 : :
766 : 3 : case PROP_SELECTION_MODE:
767 : 3 : g_value_set_boolean (value, self->selection_mode);
768 : 3 : break;
769 : :
770 : 0 : default:
771 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
772 : : }
773 : 4 : }
774 : :
775 : : static void
776 : 2 : valent_share_dialog_set_property (GObject *object,
777 : : guint prop_id,
778 : : const GValue *value,
779 : : GParamSpec *pspec)
780 : : {
781 : 2 : ValentShareDialog *self = VALENT_SHARE_DIALOG (object);
782 : :
783 [ + + - ]: 2 : switch ((ValentShareDialogProperty)prop_id)
784 : : {
785 : 1 : case PROP_FILES:
786 : 1 : valent_share_dialog_set_files (self, g_value_get_object (value));
787 : 1 : break;
788 : :
789 : 1 : case PROP_SELECTION_MODE:
790 : 1 : valent_share_dialog_set_selection_mode (self, g_value_get_boolean (value));
791 : 1 : break;
792 : :
793 : 0 : default:
794 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
795 : : }
796 : 2 : }
797 : :
798 : : static void
799 : 1 : valent_share_dialog_class_init (ValentShareDialogClass *klass)
800 : : {
801 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
802 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
803 : :
804 : 1 : object_class->constructed = valent_share_dialog_constructed;
805 : 1 : object_class->dispose = valent_share_dialog_dispose;
806 : 1 : object_class->get_property = valent_share_dialog_get_property;
807 : 1 : object_class->set_property = valent_share_dialog_set_property;
808 : :
809 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-share-dialog.ui");
810 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, view);
811 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, device_list);
812 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, single_row);
813 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, single_icon);
814 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, multiple_row);
815 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, multiple_icon);
816 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentShareDialog, uri_entry);
817 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_device_activated);
818 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_uri_activated);
819 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_uri_changed);
820 : :
821 : 1 : gtk_widget_class_install_action (widget_class, "chooser.share", NULL, chooser_share_action);
822 : 1 : gtk_widget_class_install_action (widget_class, "chooser.select-files", NULL, chooser_select_files_action);
823 : :
824 : : /**
825 : : * ValentShareDialog:files:
826 : : *
827 : : * The URIs to share.
828 : : */
829 : 2 : properties [PROP_FILES] =
830 : 1 : g_param_spec_object ("files", NULL, NULL,
831 : : G_TYPE_LIST_MODEL,
832 : : (G_PARAM_READWRITE |
833 : : G_PARAM_CONSTRUCT_ONLY |
834 : : G_PARAM_EXPLICIT_NOTIFY |
835 : : G_PARAM_STATIC_STRINGS));
836 : :
837 : : /**
838 : : * ValentShareDialog:selection-mode:
839 : : *
840 : : * Whether multiple devices can be selected.
841 : : */
842 : 2 : properties [PROP_SELECTION_MODE] =
843 : 1 : g_param_spec_boolean ("selection-mode", NULL, NULL,
844 : : FALSE,
845 : : (G_PARAM_READWRITE |
846 : : G_PARAM_CONSTRUCT |
847 : : G_PARAM_EXPLICIT_NOTIFY |
848 : : G_PARAM_STATIC_STRINGS));
849 : :
850 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
851 : 1 : }
852 : :
853 : : static void
854 : 1 : valent_share_dialog_init (ValentShareDialog *self)
855 : : {
856 : 1 : gtk_widget_init_template (GTK_WIDGET (self));
857 : 1 : }
858 : :
|