1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2008 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <string.h>
23 #include <glib.h>
24 #include <glib/gi18n.h>
25 #include <gio/gio.h>
26 #include "gth-file-data.h"
27 #include "gth-file-source.h"
28 #include "gth-file-source-vfs.h"
29 #include "gth-hook.h"
30 #include "gth-main.h"
31 #include "gth-metadata-provider.h"
32 #include "gth-overwrite-dialog.h"
33 #include "glib-utils.h"
34 #include "gio-utils.h"
35
36
37 #define N_FILES_PER_REQUEST 128
38
39
40 /* -- _g_directory_foreach_child -- */
41
42
43 typedef struct {
44 GFile *file;
45 GFileInfo *info;
46 } ChildData;
47
48
49 static ChildData *
child_data_new(GFile * file,GFileInfo * info)50 child_data_new (GFile *file,
51 GFileInfo *info)
52 {
53 ChildData *data;
54
55 data = g_new0 (ChildData, 1);
56 data->file = g_file_dup (file);
57 data->info = g_file_info_dup (info);
58
59 return data;
60 }
61
62
63 static void
child_data_free(ChildData * data)64 child_data_free (ChildData *data)
65 {
66 if (data == NULL)
67 return;
68 _g_object_unref (data->file);
69 _g_object_unref (data->info);
70 g_free (data);
71 }
72
73
74 static void
clear_child_data(ChildData ** data)75 clear_child_data (ChildData **data)
76 {
77 if (*data != NULL)
78 child_data_free (*data);
79 *data = NULL;
80 }
81
82
83 typedef struct {
84 GFile *base_directory;
85 gboolean recursive;
86 gboolean follow_links;
87 StartDirCallback start_dir_func;
88 ForEachChildCallback for_each_file_func;
89 ReadyFunc done_func;
90 gpointer user_data;
91
92 /* private */
93
94 ChildData *current;
95 GHashTable *already_visited;
96 GList *to_visit;
97 char *attributes;
98 GCancellable *cancellable;
99 GFileEnumerator *enumerator;
100 GError *error;
101 guint source_id;
102 gboolean metadata_attributes;
103 GList *children;
104 GList *current_child;
105 } ForEachChildData;
106
107
108 static void
for_each_child_data_free(ForEachChildData * fec)109 for_each_child_data_free (ForEachChildData *fec)
110 {
111 if (fec == NULL)
112 return;
113
114 g_object_unref (fec->base_directory);
115 if (fec->already_visited != NULL)
116 g_hash_table_destroy (fec->already_visited);
117 clear_child_data (&(fec->current));
118 g_free (fec->attributes);
119 if (fec->to_visit != NULL) {
120 g_list_foreach (fec->to_visit, (GFunc) child_data_free, NULL);
121 g_list_free (fec->to_visit);
122 }
123 _g_object_unref (fec->cancellable);
124 g_free (fec);
125 }
126
127
128 static gboolean
for_each_child_done_cb(gpointer user_data)129 for_each_child_done_cb (gpointer user_data)
130 {
131 ForEachChildData *fec = user_data;
132
133 g_source_remove (fec->source_id);
134 clear_child_data (&(fec->current));
135 if (fec->done_func)
136 fec->done_func (fec->error, fec->user_data);
137 for_each_child_data_free (fec);
138
139 return FALSE;
140 }
141
142
143 static void
for_each_child_done(ForEachChildData * fec)144 for_each_child_done (ForEachChildData *fec)
145 {
146 fec->source_id = g_idle_add (for_each_child_done_cb, fec);
147 }
148
149
150 static void for_each_child_start_current (ForEachChildData *fec);
151
152
153 static void
for_each_child_start(ForEachChildData * fec)154 for_each_child_start (ForEachChildData *fec)
155 {
156 for_each_child_start_current (fec);
157 }
158
159
160 static void
for_each_child_set_current(ForEachChildData * fec,ChildData * data)161 for_each_child_set_current (ForEachChildData *fec,
162 ChildData *data)
163 {
164 clear_child_data (&(fec->current));
165 fec->current = data;
166 }
167
168
169 static void
for_each_child_start_next_sub_directory(ForEachChildData * fec)170 for_each_child_start_next_sub_directory (ForEachChildData *fec)
171 {
172 ChildData *child = NULL;
173
174 if (fec->to_visit != NULL) {
175 GList *tmp;
176
177 child = (ChildData *) fec->to_visit->data;
178 tmp = fec->to_visit;
179 fec->to_visit = g_list_remove_link (fec->to_visit, tmp);
180 g_list_free (tmp);
181 }
182
183 if (child != NULL) {
184 for_each_child_set_current (fec, child);
185 for_each_child_start (fec);
186 }
187 else
188 for_each_child_done (fec);
189 }
190
191
192 static void
for_each_child_close_enumerator(GObject * source_object,GAsyncResult * result,gpointer user_data)193 for_each_child_close_enumerator (GObject *source_object,
194 GAsyncResult *result,
195 gpointer user_data)
196 {
197 ForEachChildData *fec = user_data;
198 GError *error = NULL;
199
200 if (! g_file_enumerator_close_finish (fec->enumerator,
201 result,
202 &error))
203 {
204 if (fec->error == NULL)
205 fec->error = g_error_copy (error);
206 else
207 g_clear_error (&error);
208 }
209
210 if ((fec->error == NULL) && fec->recursive)
211 for_each_child_start_next_sub_directory (fec);
212 else
213 for_each_child_done (fec);
214 }
215
216
217 static void for_each_child_next_files_ready (GObject *source_object,
218 GAsyncResult *result,
219 gpointer user_data);
220
221
222 static void
for_each_child_read_next_files(ForEachChildData * fec)223 for_each_child_read_next_files (ForEachChildData *fec)
224 {
225 _g_object_list_unref (fec->children);
226 fec->children = NULL;
227 g_file_enumerator_next_files_async (fec->enumerator,
228 N_FILES_PER_REQUEST,
229 G_PRIORITY_DEFAULT,
230 fec->cancellable,
231 for_each_child_next_files_ready,
232 fec);
233 }
234
235
236 static void for_each_child_read_current_child_metadata (ForEachChildData *fec);
237
238
239 static void
for_each_child_read_next_child_metadata(ForEachChildData * fec)240 for_each_child_read_next_child_metadata (ForEachChildData *fec)
241 {
242 fec->current_child = fec->current_child->next;
243 if (fec->current_child != NULL)
244 for_each_child_read_current_child_metadata (fec);
245 else
246 for_each_child_read_next_files (fec);
247 }
248
249
250 static void
for_each_child_compute_child(ForEachChildData * fec,GFile * file,GFileInfo * info)251 for_each_child_compute_child (ForEachChildData *fec,
252 GFile *file,
253 GFileInfo *info)
254 {
255 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
256 char *id;
257
258 /* avoid to visit a directory more than ones */
259
260 id = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE));
261 if (id == NULL)
262 id = g_file_get_uri (file);
263
264 if (g_hash_table_lookup (fec->already_visited, id) == NULL) {
265 g_hash_table_insert (fec->already_visited, g_strdup (id), GINT_TO_POINTER (1));
266 fec->to_visit = g_list_append (fec->to_visit, child_data_new (file, info));
267 }
268
269 g_free (id);
270 }
271
272 fec->for_each_file_func (file, info, fec->user_data);
273 }
274
275
276 static void
for_each_child_metadata_ready_func(GObject * source_object,GAsyncResult * result,gpointer user_data)277 for_each_child_metadata_ready_func (GObject *source_object,
278 GAsyncResult *result,
279 gpointer user_data)
280 {
281 ForEachChildData *fec = user_data;
282 GList *files;
283 GError *error = NULL;
284
285 files = _g_query_metadata_finish (result, &error);
286 if (files != NULL) {
287 GthFileData *child_data = files->data;
288 for_each_child_compute_child (fec, child_data->file, child_data->info);
289 }
290
291 for_each_child_read_next_child_metadata (fec);
292 }
293
294
295 static void
for_each_child_read_current_child_metadata(ForEachChildData * fec)296 for_each_child_read_current_child_metadata (ForEachChildData *fec)
297 {
298 GFileInfo *child_info = fec->current_child->data;
299 GFile *child_file;
300 GList *file_list;
301 GthFileData *child_data;
302
303 child_file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
304 child_data = gth_file_data_new (child_file, child_info);
305 file_list = g_list_append (NULL, child_data);
306 _g_query_metadata_async (file_list,
307 fec->attributes,
308 NULL, /* FIXME: cannot use fec->cancellable here */
309 for_each_child_metadata_ready_func,
310 fec);
311
312 _g_object_list_unref (file_list);
313 g_object_unref (child_file);
314 }
315
316
317 static void
for_each_child_next_files_ready(GObject * source_object,GAsyncResult * result,gpointer user_data)318 for_each_child_next_files_ready (GObject *source_object,
319 GAsyncResult *result,
320 gpointer user_data)
321 {
322 ForEachChildData *fec = user_data;
323
324 fec->children = g_file_enumerator_next_files_finish (fec->enumerator,
325 result,
326 &(fec->error));
327
328 if (fec->children == NULL) {
329 g_file_enumerator_close_async (fec->enumerator,
330 G_PRIORITY_DEFAULT,
331 fec->cancellable,
332 for_each_child_close_enumerator,
333 fec);
334 return;
335 }
336
337 if (fec->metadata_attributes) {
338 fec->current_child = fec->children;
339 for_each_child_read_current_child_metadata (fec);
340 }
341 else {
342 GList *scan;
343
344 for (scan = fec->children; scan; scan = scan->next) {
345 GFileInfo *child_info = scan->data;
346 GFile *child_file;
347
348 child_file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
349 for_each_child_compute_child (fec, child_file, child_info);
350
351 g_object_unref (child_file);
352 }
353
354 for_each_child_read_next_files (fec);
355 }
356 }
357
358
359 static void
for_each_child_ready(GObject * source_object,GAsyncResult * result,gpointer user_data)360 for_each_child_ready (GObject *source_object,
361 GAsyncResult *result,
362 gpointer user_data)
363 {
364 ForEachChildData *fec = user_data;
365
366 fec->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), result, &(fec->error));
367 if (fec->enumerator == NULL) {
368 for_each_child_done (fec);
369 return;
370 }
371
372 g_file_enumerator_next_files_async (fec->enumerator,
373 N_FILES_PER_REQUEST,
374 G_PRIORITY_DEFAULT,
375 fec->cancellable,
376 for_each_child_next_files_ready,
377 fec);
378 }
379
380
381 static void
for_each_child_start_current(ForEachChildData * fec)382 for_each_child_start_current (ForEachChildData *fec)
383 {
384 if (fec->start_dir_func != NULL) {
385 DirOp op;
386
387 op = fec->start_dir_func (fec->current->file, fec->current->info, &(fec->error), fec->user_data);
388 switch (op) {
389 case DIR_OP_SKIP:
390 for_each_child_start_next_sub_directory (fec);
391 return;
392 case DIR_OP_STOP:
393 for_each_child_done (fec);
394 return;
395 case DIR_OP_CONTINUE:
396 break;
397 }
398 }
399
400 g_file_enumerate_children_async (fec->current->file,
401 fec->attributes,
402 fec->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
403 G_PRIORITY_DEFAULT,
404 fec->cancellable,
405 for_each_child_ready,
406 fec);
407 }
408
409
410 static void
directory_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)411 directory_info_ready_cb (GObject *source_object,
412 GAsyncResult *result,
413 gpointer user_data)
414 {
415 ForEachChildData *fec = user_data;
416 GFileInfo *info;
417 ChildData *child;
418
419 info = g_file_query_info_finish (G_FILE (source_object), result, &(fec->error));
420 if (info == NULL) {
421 for_each_child_done (fec);
422 return;
423 }
424
425 child = child_data_new (fec->base_directory, info);
426 g_object_unref (info);
427
428 for_each_child_set_current (fec, child);
429 for_each_child_start_current (fec);
430 }
431
432
433 /**
434 * _g_directory_foreach_child:
435 * @directory: The directory to visit.
436 * @recursive: Whether to traverse the @directory recursively.
437 * @follow_links: Whether to dereference the symbolic links.
438 * @attributes: The GFileInfo attributes to read.
439 * @cancellable: An optional @GCancellable object, used to cancel the process.
440 * @start_dir_func: the function called for each sub-directory, or %NULL if
441 * not needed.
442 * @for_each_file_func: the function called for each file. Can't be %NULL.
443 * @done_func: the function called at the end of the traversing process.
444 * Can't be %NULL.
445 * @user_data: data to pass to @done_func
446 *
447 * Traverse the @directory's filesystem structure calling the
448 * @for_each_file_func function for each file in the directory; the
449 * @start_dir_func function on each directory before it's going to be
450 * traversed, this includes @directory too; the @done_func function is
451 * called at the end of the process.
452 * Some traversing options are available: if @recursive is TRUE the
453 * directory is traversed recursively; if @follow_links is TRUE, symbolic
454 * links are dereferenced, otherwise they are returned as links.
455 * Each callback uses the same @user_data additional parameter.
456 */
457 void
_g_directory_foreach_child(GFile * directory,gboolean recursive,gboolean follow_links,const char * attributes,GCancellable * cancellable,StartDirCallback start_dir_func,ForEachChildCallback for_each_file_func,ReadyFunc done_func,gpointer user_data)458 _g_directory_foreach_child (GFile *directory,
459 gboolean recursive,
460 gboolean follow_links,
461 const char *attributes,
462 GCancellable *cancellable,
463 StartDirCallback start_dir_func,
464 ForEachChildCallback for_each_file_func,
465 ReadyFunc done_func,
466 gpointer user_data)
467 {
468 ForEachChildData *fec;
469
470 g_return_if_fail (for_each_file_func != NULL);
471
472 fec = g_new0 (ForEachChildData, 1);
473
474 fec->base_directory = g_file_dup (directory);
475 fec->recursive = recursive;
476 fec->follow_links = follow_links;
477 fec->attributes = g_strconcat (attributes, ",standard::name,standard::type,id::file", NULL);
478 fec->cancellable = _g_object_ref (cancellable);
479 fec->start_dir_func = start_dir_func;
480 fec->for_each_file_func = for_each_file_func;
481 fec->done_func = done_func;
482 fec->user_data = user_data;
483 fec->already_visited = g_hash_table_new_full (g_str_hash,
484 g_str_equal,
485 g_free,
486 NULL);
487 fec->metadata_attributes = ! _g_file_attributes_matches_all (fec->attributes, GIO_ATTRIBUTES);
488
489 g_file_query_info_async (fec->base_directory,
490 fec->attributes,
491 G_FILE_QUERY_INFO_NONE,
492 G_PRIORITY_DEFAULT,
493 fec->cancellable,
494 directory_info_ready_cb,
495 fec);
496 }
497
498 /* -- _g_file_list_query_info_async -- */
499
500
501 typedef struct {
502 GList *file_list;
503 GthListFlags flags;
504 char *attributes;
505 GCancellable *cancellable;
506 InfoReadyCallback callback;
507 gpointer user_data;
508 GList *current;
509 GList *files;
510 GList *sidecars;
511 GList *current_sidecar;
512 } QueryInfoData;
513
514
515 static void
query_data_free(QueryInfoData * query_data)516 query_data_free (QueryInfoData *query_data)
517 {
518 _g_object_list_unref (query_data->sidecars);
519 _g_object_list_unref (query_data->file_list);
520 _g_object_list_unref (query_data->files);
521 _g_object_unref (query_data->cancellable);
522 g_free (query_data->attributes);
523 g_free (query_data);
524 }
525
526
527 static void query_info__query_current (QueryInfoData *query_data);
528
529
530 static void
query_info__query_next(QueryInfoData * query_data)531 query_info__query_next (QueryInfoData *query_data)
532 {
533 query_data->current = query_data->current->next;
534 query_info__query_current (query_data);
535 }
536
537
538 static void
539 query_info__query_current_sidecar (QueryInfoData *query_data);
540
541
542 static void
query_info__query_next_sidecar(QueryInfoData * query_data)543 query_info__query_next_sidecar (QueryInfoData *query_data)
544 {
545 query_data->current_sidecar = query_data->current_sidecar->next;
546 query_info__query_current_sidecar (query_data);
547 }
548
549
550 static void
query_data_sidecar_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)551 query_data_sidecar_info_ready_cb (GObject *source_object,
552 GAsyncResult *result,
553 gpointer user_data)
554 {
555 QueryInfoData *query_data = user_data;
556 GFileInfo *info;
557 GFile *file;
558
559 info = g_file_query_info_finish ((GFile *) source_object, result, NULL);
560 if (info == NULL) {
561 query_info__query_next_sidecar (query_data);
562 return;
563 }
564
565 file = G_FILE (query_data->current_sidecar->data);
566 query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
567
568 query_info__query_next_sidecar (query_data);
569 }
570
571
572 static void
query_info__query_current_sidecar(QueryInfoData * query_data)573 query_info__query_current_sidecar (QueryInfoData *query_data)
574 {
575 GFileQueryInfoFlags flags;
576
577 if (query_data->current_sidecar == NULL) {
578 query_info__query_next (query_data);
579 return;
580 }
581
582 flags = G_FILE_QUERY_INFO_NONE;
583 if (query_data->flags & GTH_LIST_NO_FOLLOW_LINKS)
584 flags |= G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS;
585
586 g_file_query_info_async (G_FILE (query_data->current_sidecar->data),
587 query_data->attributes,
588 flags,
589 G_PRIORITY_DEFAULT,
590 query_data->cancellable,
591 query_data_sidecar_info_ready_cb,
592 query_data);
593 }
594
595
596 static void
query_info__query_sidecars(QueryInfoData * query_data,GFile * file)597 query_info__query_sidecars (QueryInfoData *query_data,
598 GFile *file)
599 {
600 GList *sidecars = NULL;
601
602 gth_hook_invoke ("add-sidecars", file, &sidecars);
603 if (sidecars == NULL) {
604 query_info__query_next (query_data);
605 return;
606 }
607
608 _g_object_list_unref (query_data->sidecars);
609 query_data->sidecars = sidecars;
610 query_data->current_sidecar = query_data->sidecars;
611 query_info__query_current_sidecar (query_data);
612 }
613
614
615 static void
query_data__done_cb(GError * error,gpointer user_data)616 query_data__done_cb (GError *error,
617 gpointer user_data)
618 {
619 QueryInfoData *query_data = user_data;
620
621 if (error != NULL) {
622 query_data->callback (NULL, error, query_data->user_data);
623 query_data_free (query_data);
624 return;
625 }
626
627 query_info__query_next (query_data);
628 }
629
630
631 static void
query_data__for_each_file_cb(GFile * file,GFileInfo * info,gpointer user_data)632 query_data__for_each_file_cb (GFile *file,
633 GFileInfo *info,
634 gpointer user_data)
635 {
636 QueryInfoData *query_data = user_data;
637
638 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
639 return;
640 if ((query_data->flags & GTH_LIST_NO_BACKUP_FILES) && g_file_info_get_is_backup (info))
641 return;
642 if ((query_data->flags & GTH_LIST_NO_HIDDEN_FILES) && g_file_info_get_is_hidden (info))
643 return;
644
645 query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
646 }
647
648
649 static DirOp
query_data__start_dir_cb(GFile * directory,GFileInfo * info,GError ** error,gpointer user_data)650 query_data__start_dir_cb (GFile *directory,
651 GFileInfo *info,
652 GError **error,
653 gpointer user_data)
654 {
655 QueryInfoData *query_data = user_data;
656
657 if ((query_data->flags & GTH_LIST_NO_BACKUP_FILES) && g_file_info_get_is_backup (info))
658 return DIR_OP_SKIP;
659 if ((query_data->flags & GTH_LIST_NO_HIDDEN_FILES) && g_file_info_get_is_hidden (info))
660 return DIR_OP_SKIP;
661
662 query_data->files = g_list_prepend (query_data->files, gth_file_data_new (directory, info));
663
664 return DIR_OP_CONTINUE;
665 }
666
667
668 static void
query_data_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)669 query_data_info_ready_cb (GObject *source_object,
670 GAsyncResult *result,
671 gpointer user_data)
672 {
673 QueryInfoData *query_data = user_data;
674 GError *error = NULL;
675 GFileInfo *info;
676
677 info = g_file_query_info_finish ((GFile *) source_object, result, &error);
678 if (info == NULL) {
679 query_data->callback (NULL, error, query_data->user_data);
680 query_data_free (query_data);
681 return;
682 }
683
684 if ((query_data->flags & GTH_LIST_RECURSIVE)
685 && (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY))
686 {
687 _g_directory_foreach_child ((GFile *) query_data->current->data,
688 TRUE,
689 (query_data->flags & GTH_LIST_NO_FOLLOW_LINKS) == 0,
690 query_data->attributes,
691 query_data->cancellable,
692 query_data__start_dir_cb,
693 query_data__for_each_file_cb,
694 query_data__done_cb,
695 query_data);
696 }
697 else {
698 GFile *file = G_FILE (query_data->current->data);
699
700 query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
701
702 if ((query_data->flags & GTH_LIST_INCLUDE_SIDECARS)
703 && (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR))
704 {
705 query_info__query_sidecars (query_data, file);
706 }
707 else
708 query_info__query_next (query_data);
709 }
710
711 g_object_unref (info);
712 }
713
714
715 static void
query_info__query_current(QueryInfoData * query_data)716 query_info__query_current (QueryInfoData *query_data)
717 {
718 GFileQueryInfoFlags flags;
719
720 if (query_data->current == NULL) {
721 query_data->files = g_list_reverse (query_data->files);
722 query_data->callback (query_data->files, NULL, query_data->user_data);
723 query_data_free (query_data);
724 return;
725 }
726
727 flags = G_FILE_QUERY_INFO_NONE;
728 if (query_data->flags & GTH_LIST_NO_FOLLOW_LINKS)
729 flags |= G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS;
730
731 g_file_query_info_async ((GFile *) query_data->current->data,
732 query_data->attributes,
733 flags,
734 G_PRIORITY_DEFAULT,
735 query_data->cancellable,
736 query_data_info_ready_cb,
737 query_data);
738 }
739
740
741 void
_g_file_list_query_info_async(GList * file_list,GthListFlags flags,const char * attributes,GCancellable * cancellable,InfoReadyCallback ready_callback,gpointer user_data)742 _g_file_list_query_info_async (GList *file_list,
743 GthListFlags flags,
744 const char *attributes,
745 GCancellable *cancellable,
746 InfoReadyCallback ready_callback,
747 gpointer user_data)
748 {
749 QueryInfoData *query_data;
750
751 query_data = g_new0 (QueryInfoData, 1);
752 query_data->file_list = _g_object_list_ref (file_list);
753 query_data->flags = flags;
754 query_data->attributes = g_strconcat ("standard::name,standard::type,standard::is-hidden,standard::is-backup,id::file",
755 (((attributes != NULL) && (strcmp (attributes, "") != 0)) ? "," : NULL),
756 attributes,
757 NULL);
758 query_data->cancellable = _g_object_ref (cancellable);
759 query_data->callback = ready_callback;
760 query_data->user_data = user_data;
761 query_data->sidecars = NULL;
762
763 query_data->current = query_data->file_list;
764 query_info__query_current (query_data);
765 }
766
767
768 /* -- _g_copy_file_async -- */
769
770
771 typedef struct {
772 GthFileData *source;
773 GFile *destination;
774 gboolean move;
775 GthFileCopyFlags flags;
776 int io_priority;
777 goffset tot_size;
778 goffset copied_size;
779 gsize tot_files;
780 GCancellable *cancellable;
781 ProgressCallback progress_callback;
782 gpointer progress_callback_data;
783 DialogCallback dialog_callback;
784 gpointer dialog_callback_data;
785 CopyReadyCallback ready_callback;
786 gpointer user_data;
787 gboolean move_succeed;
788
789 GFile *current_destination;
790 char *message;
791 GthOverwriteResponse default_response;
792 gboolean duplicating_file;
793
794 GList *source_sidecars; /* GFile list */
795 GList *destination_sidecars; /* GFile list */
796 GList *current_source_sidecar;
797 GList *current_destination_sidecar;
798 } CopyFileData;
799
800
801 static void
copy_file_data_free(CopyFileData * copy_file_data)802 copy_file_data_free (CopyFileData *copy_file_data)
803 {
804 g_object_unref (copy_file_data->source);
805 g_object_unref (copy_file_data->destination);
806 _g_object_unref (copy_file_data->cancellable);
807 _g_object_unref (copy_file_data->current_destination);
808 g_free (copy_file_data->message);
809 _g_object_list_unref (copy_file_data->destination_sidecars);
810 _g_object_list_unref (copy_file_data->source_sidecars);
811 g_free (copy_file_data);
812 }
813
814
815 static void
copy_file__delete_source(CopyFileData * copy_file_data)816 copy_file__delete_source (CopyFileData *copy_file_data)
817 {
818 GError *error = NULL;
819
820 /* delete the source if we are moving the file but the move operation
821 * is not supported. */
822 if (copy_file_data->move && ! copy_file_data->move_succeed)
823 g_file_delete (copy_file_data->source->file, copy_file_data->cancellable, &error);
824
825 copy_file_data->ready_callback (copy_file_data->default_response, copy_file_data->source_sidecars, error, copy_file_data->user_data);
826 copy_file_data_free (copy_file_data);
827 }
828
829
830 static void copy_file__copy_current_sidecar (CopyFileData *copy_file_data);
831
832
833 static void
copy_file__copy_next_sidecar(CopyFileData * copy_file_data)834 copy_file__copy_next_sidecar (CopyFileData *copy_file_data)
835 {
836 copy_file_data->current_source_sidecar = copy_file_data->current_source_sidecar->next;
837 copy_file_data->current_destination_sidecar = copy_file_data->current_destination_sidecar->next;
838 copy_file__copy_current_sidecar (copy_file_data);
839 }
840
841
842 static void
copy_file__copy_current_sidecar_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)843 copy_file__copy_current_sidecar_ready_cb (GObject *source_object,
844 GAsyncResult *result,
845 gpointer user_data)
846 {
847 CopyFileData *copy_file_data = user_data;
848
849 if (g_file_copy_finish ((GFile *) source_object, result, NULL)) {
850 if (copy_file_data->move)
851 g_file_delete ((GFile *) copy_file_data->current_source_sidecar->data, copy_file_data->cancellable, NULL);
852 }
853
854 copy_file__copy_next_sidecar (copy_file_data);
855 }
856
857
858 static void
copy_file__copy_current_sidecar(CopyFileData * copy_file_data)859 copy_file__copy_current_sidecar (CopyFileData *copy_file_data)
860 {
861 GFile *source;
862 GFile *destination;
863 GFile *destination_parent;
864
865 if (copy_file_data->current_source_sidecar == NULL) {
866 copy_file__delete_source (copy_file_data);
867 return;
868 }
869
870 source = copy_file_data->current_source_sidecar->data;
871 if (! g_file_query_exists (source, copy_file_data->cancellable)) {
872 copy_file__copy_next_sidecar (copy_file_data);
873 return;
874 }
875
876 destination = copy_file_data->current_destination_sidecar->data;
877 destination_parent = g_file_get_parent (destination);
878 g_file_make_directory (destination_parent, copy_file_data->cancellable, NULL);
879
880 g_file_copy_async (source,
881 destination,
882 G_FILE_COPY_OVERWRITE,
883 copy_file_data->io_priority,
884 copy_file_data->cancellable,
885 NULL,
886 NULL,
887 copy_file__copy_current_sidecar_ready_cb,
888 copy_file_data);
889
890 g_object_unref (destination_parent);
891 }
892
893
894 static void
copy_file__copy_sidecars(CopyFileData * copy_file_data)895 copy_file__copy_sidecars (CopyFileData *copy_file_data)
896 {
897 /* copy the metadata sidecars if requested */
898
899 _g_object_list_unref (copy_file_data->source_sidecars);
900 _g_object_list_unref (copy_file_data->destination_sidecars);
901 copy_file_data->source_sidecars = NULL;
902 copy_file_data->destination_sidecars = NULL;
903 if (copy_file_data->flags & GTH_FILE_COPY_ALL_METADATA) {
904 gth_hook_invoke ("add-sidecars", copy_file_data->source->file, ©_file_data->source_sidecars);
905 gth_hook_invoke ("add-sidecars", copy_file_data->destination, ©_file_data->destination_sidecars);
906 copy_file_data->source_sidecars = g_list_reverse (copy_file_data->source_sidecars);
907 copy_file_data->destination_sidecars = g_list_reverse (copy_file_data->destination_sidecars);
908 }
909
910 copy_file_data->current_source_sidecar = copy_file_data->source_sidecars;
911 copy_file_data->current_destination_sidecar = copy_file_data->destination_sidecars;
912 copy_file__copy_current_sidecar (copy_file_data);
913 }
914
915
916 static void _g_copy_file_to_destination (CopyFileData *copy_file_data,
917 GFile *destination,
918 GFileCopyFlags flags);
919
920
921 static void
copy_file__overwrite_dialog_response_cb(GtkDialog * dialog,int response_id,gpointer user_data)922 copy_file__overwrite_dialog_response_cb (GtkDialog *dialog,
923 int response_id,
924 gpointer user_data)
925 {
926 CopyFileData *copy_file_data = user_data;
927
928 if (response_id != GTK_RESPONSE_OK)
929 copy_file_data->default_response = GTH_OVERWRITE_RESPONSE_CANCEL;
930 else
931 copy_file_data->default_response = gth_overwrite_dialog_get_response (GTH_OVERWRITE_DIALOG (dialog));
932
933 gtk_widget_hide (GTK_WIDGET (dialog));
934
935 if (copy_file_data->dialog_callback != NULL)
936 copy_file_data->dialog_callback (FALSE, NULL, copy_file_data->dialog_callback_data);
937
938 switch (copy_file_data->default_response) {
939 case GTH_OVERWRITE_RESPONSE_NO:
940 case GTH_OVERWRITE_RESPONSE_ALWAYS_NO:
941 case GTH_OVERWRITE_RESPONSE_UNSPECIFIED:
942 case GTH_OVERWRITE_RESPONSE_CANCEL:
943 {
944 GError *error = NULL;
945
946 if (copy_file_data->default_response == GTH_OVERWRITE_RESPONSE_CANCEL)
947 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "");
948 else
949 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_EXISTS, "");
950 copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
951 copy_file_data_free (copy_file_data);
952 return;
953 }
954
955 case GTH_OVERWRITE_RESPONSE_YES:
956 case GTH_OVERWRITE_RESPONSE_ALWAYS_YES:
957 _g_copy_file_to_destination (copy_file_data, copy_file_data->current_destination, G_FILE_COPY_OVERWRITE);
958 break;
959
960 case GTH_OVERWRITE_RESPONSE_RENAME:
961 {
962 GFile *parent;
963 GFile *new_destination;
964
965 parent = g_file_get_parent (copy_file_data->current_destination);
966 new_destination = g_file_get_child_for_display_name (parent, gth_overwrite_dialog_get_filename (GTH_OVERWRITE_DIALOG (dialog)), NULL);
967 _g_copy_file_to_destination (copy_file_data, new_destination, G_FILE_COPY_NONE);
968
969 g_object_unref (new_destination);
970 g_object_unref (parent);
971 }
972 break;
973 }
974
975 gtk_widget_destroy (GTK_WIDGET (dialog));
976 }
977
978
979 static void
copy_file__handle_error(CopyFileData * copy_file_data,GError * error)980 copy_file__handle_error (CopyFileData *copy_file_data,
981 GError *error)
982 {
983 g_return_if_fail (GTH_IS_FILE_DATA (copy_file_data->source));
984 g_return_if_fail (G_IS_FILE (copy_file_data->source->file));
985
986 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
987 if (copy_file_data->duplicating_file) {
988
989 /* the duplicated file already exists, try another one */
990
991 GFile *new_destination = _g_file_get_duplicated (copy_file_data->current_destination);
992 _g_copy_file_to_destination (copy_file_data, new_destination, G_FILE_COPY_NONE);
993
994 g_object_unref (new_destination);
995 }
996 else if (copy_file_data->default_response != GTH_OVERWRITE_RESPONSE_ALWAYS_NO) {
997 GtkWidget *dialog;
998
999 dialog = gth_overwrite_dialog_new (copy_file_data->source->file,
1000 NULL,
1001 copy_file_data->current_destination,
1002 copy_file_data->default_response,
1003 copy_file_data->tot_files == 1);
1004
1005 if (copy_file_data->dialog_callback != NULL)
1006 copy_file_data->dialog_callback (TRUE, dialog, copy_file_data->dialog_callback_data);
1007
1008 g_signal_connect (dialog,
1009 "response",
1010 G_CALLBACK (copy_file__overwrite_dialog_response_cb),
1011 copy_file_data);
1012 gtk_widget_show (dialog);
1013 }
1014 else {
1015 copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
1016 copy_file_data_free (copy_file_data);
1017 }
1018 }
1019 else {
1020 copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
1021 copy_file_data_free (copy_file_data);
1022 }
1023 }
1024
1025
1026 static void
copy_file_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1027 copy_file_ready_cb (GObject *source_object,
1028 GAsyncResult *result,
1029 gpointer user_data)
1030 {
1031 CopyFileData *copy_file_data = user_data;
1032 GError *error = NULL;
1033
1034 if (! g_file_copy_finish (G_FILE (source_object), result, &error)) {
1035 copy_file__handle_error (copy_file_data, error);
1036 return;
1037 }
1038
1039 copy_file__copy_sidecars (copy_file_data);
1040 }
1041
1042
1043 static void
copy_file_progress_cb(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)1044 copy_file_progress_cb (goffset current_num_bytes,
1045 goffset total_num_bytes,
1046 gpointer user_data)
1047 {
1048 CopyFileData *copy_file_data = user_data;
1049 char *s1;
1050 char *s2;
1051 char *details;
1052
1053 if (copy_file_data->progress_callback == NULL)
1054 return;
1055
1056 s1 = g_format_size (copy_file_data->copied_size + current_num_bytes);
1057 s2 = g_format_size (copy_file_data->tot_size);
1058 /* For translators: This is a progress size indicator, for example: 230.4 MB of 512.8 MB */
1059 details = g_strdup_printf (_("%s of %s"), s1, s2);
1060
1061 copy_file_data->progress_callback (NULL,
1062 copy_file_data->message,
1063 details,
1064 FALSE,
1065 (double) (copy_file_data->copied_size + current_num_bytes) / copy_file_data->tot_size,
1066 copy_file_data->progress_callback_data);
1067
1068 g_free (details);
1069 g_free (s2);
1070 g_free (s1);
1071 }
1072
1073
1074 static void
_g_copy_file_to_destination_call_ready_callback(gpointer user_data)1075 _g_copy_file_to_destination_call_ready_callback (gpointer user_data)
1076 {
1077 CopyFileData *copy_file_data = user_data;
1078
1079 copy_file_data->ready_callback (copy_file_data->default_response, NULL, NULL, copy_file_data->user_data);
1080 copy_file_data_free (copy_file_data);
1081 }
1082
1083
1084 static void
make_directory_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1085 make_directory_ready_cb (GObject *source_object,
1086 GAsyncResult *result,
1087 gpointer user_data)
1088 {
1089 CopyFileData *copy_file_data = user_data;
1090 GError *error = NULL;
1091
1092 if (! g_file_make_directory_finish (G_FILE (source_object), result, &error))
1093 /* ignore this kind of errors */
1094 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
1095 g_clear_error (&error);
1096
1097 copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
1098 copy_file_data_free (copy_file_data);
1099 }
1100
1101
1102 static void
_g_copy_file_to_destination(CopyFileData * copy_file_data,GFile * destination,GFileCopyFlags flags)1103 _g_copy_file_to_destination (CopyFileData *copy_file_data,
1104 GFile *destination,
1105 GFileCopyFlags flags)
1106 {
1107 if (copy_file_data->default_response == GTH_OVERWRITE_RESPONSE_ALWAYS_YES)
1108 flags |= G_FILE_COPY_OVERWRITE;
1109 if (copy_file_data->flags & GTH_FILE_COPY_ALL_METADATA)
1110 flags |= G_FILE_COPY_ALL_METADATA;
1111
1112 _g_object_unref (copy_file_data->current_destination);
1113 copy_file_data->current_destination = g_file_dup (destination);
1114
1115 if (g_file_equal (copy_file_data->source->file, copy_file_data->current_destination)) {
1116 GFile *old_destination;
1117
1118 if ((copy_file_data->move
1119 || (copy_file_data->default_response == GTH_OVERWRITE_RESPONSE_ALWAYS_YES)
1120 || ! (copy_file_data->flags & GTH_FILE_COPY_RENAME_SAME_FILE)))
1121 {
1122 call_when_idle (_g_copy_file_to_destination_call_ready_callback, copy_file_data);
1123 return;
1124 }
1125
1126 copy_file_data->duplicating_file = TRUE;
1127
1128 old_destination = copy_file_data->current_destination;
1129 copy_file_data->current_destination = _g_file_get_duplicated (old_destination);
1130 g_object_unref (old_destination);
1131
1132 /* duplicated files shouldn't be overwritten, if a duplicated
1133 * file already exists get another duplicated file and try
1134 * again. */
1135 if (flags & G_FILE_COPY_OVERWRITE)
1136 flags ^= G_FILE_COPY_OVERWRITE;
1137 }
1138
1139 if (copy_file_data->progress_callback != NULL) {
1140 GFile *destination_parent;
1141 char *destination_name;
1142
1143 g_free (copy_file_data->message);
1144
1145 destination_parent = g_file_get_parent (copy_file_data->current_destination);
1146 destination_name = g_file_get_parse_name (destination_parent);
1147 if (copy_file_data->move)
1148 copy_file_data->message = g_strdup_printf (_("Moving “%s” to “%s”"), g_file_info_get_display_name (copy_file_data->source->info), destination_name);
1149 else
1150 copy_file_data->message = g_strdup_printf (_("Copying “%s” to “%s”"), g_file_info_get_display_name (copy_file_data->source->info), destination_name);
1151
1152 copy_file_progress_cb (0, 0, copy_file_data);
1153
1154 g_free (destination_name);
1155 g_object_unref (destination_parent);
1156 }
1157
1158 switch (g_file_info_get_file_type (copy_file_data->source->info)) {
1159 case G_FILE_TYPE_DIRECTORY:
1160 /* FIXME: handle the GTH_FILE_COPY_RENAME_SAME_FILE flag for directories */
1161 g_file_make_directory_async (copy_file_data->current_destination,
1162 copy_file_data->io_priority,
1163 copy_file_data->cancellable,
1164 make_directory_ready_cb,
1165 copy_file_data);
1166 break;
1167
1168 default:
1169 if (copy_file_data->move) {
1170 GError *error = NULL;
1171
1172 flags |= G_FILE_COPY_NO_FALLBACK_FOR_MOVE;
1173 if (g_file_move (copy_file_data->source->file,
1174 copy_file_data->current_destination,
1175 flags,
1176 copy_file_data->cancellable,
1177 copy_file_progress_cb,
1178 copy_file_data,
1179 &error))
1180 {
1181 copy_file_data->move_succeed = TRUE;
1182 copy_file__copy_sidecars (copy_file_data);
1183 return;
1184 }
1185 else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
1186 /* fallback to copy and delete */
1187 }
1188 else {
1189 copy_file__handle_error (copy_file_data, error);
1190 return;
1191 }
1192 }
1193
1194 g_file_copy_async (copy_file_data->source->file,
1195 copy_file_data->current_destination,
1196 flags,
1197 copy_file_data->io_priority,
1198 copy_file_data->cancellable,
1199 copy_file_progress_cb,
1200 copy_file_data,
1201 copy_file_ready_cb,
1202 copy_file_data);
1203 break;
1204 }
1205 }
1206
1207
1208 static void
_g_copy_file_async_private(GthFileData * source,GFile * destination,gboolean move,GthFileCopyFlags flags,GthOverwriteResponse default_response,int io_priority,goffset tot_size,goffset copied_size,gsize tot_files,GCancellable * cancellable,ProgressCallback progress_callback,gpointer progress_callback_data,DialogCallback dialog_callback,gpointer dialog_callback_data,CopyReadyCallback ready_callback,gpointer user_data)1209 _g_copy_file_async_private (GthFileData *source,
1210 GFile *destination,
1211 gboolean move,
1212 GthFileCopyFlags flags,
1213 GthOverwriteResponse default_response,
1214 int io_priority,
1215 goffset tot_size,
1216 goffset copied_size,
1217 gsize tot_files,
1218 GCancellable *cancellable,
1219 ProgressCallback progress_callback,
1220 gpointer progress_callback_data,
1221 DialogCallback dialog_callback,
1222 gpointer dialog_callback_data,
1223 CopyReadyCallback ready_callback,
1224 gpointer user_data)
1225 {
1226 CopyFileData *copy_file_data;
1227
1228 g_return_if_fail (GTH_IS_FILE_DATA (source));
1229 g_return_if_fail (G_IS_FILE (destination));
1230
1231 copy_file_data = g_new0 (CopyFileData, 1);
1232 copy_file_data->source = g_object_ref (source);
1233 copy_file_data->destination = g_object_ref (destination);
1234 copy_file_data->move = move;
1235 copy_file_data->flags = flags;
1236 copy_file_data->io_priority = io_priority;
1237 copy_file_data->tot_size = tot_size;
1238 copy_file_data->copied_size = copied_size;
1239 copy_file_data->tot_files = tot_files;
1240 copy_file_data->cancellable = _g_object_ref (cancellable);
1241 copy_file_data->progress_callback = progress_callback;
1242 copy_file_data->progress_callback_data = progress_callback_data;
1243 copy_file_data->dialog_callback = dialog_callback;
1244 copy_file_data->dialog_callback_data = dialog_callback_data;
1245 copy_file_data->ready_callback = ready_callback;
1246 copy_file_data->user_data = user_data;
1247 copy_file_data->default_response = default_response;
1248 copy_file_data->move_succeed = FALSE;
1249
1250 _g_copy_file_to_destination (copy_file_data, copy_file_data->destination, G_FILE_COPY_NONE);
1251 }
1252
1253
1254 void
_gth_file_data_copy_async(GthFileData * source,GFile * destination,gboolean move,GthFileCopyFlags flags,GthOverwriteResponse default_response,int io_priority,GCancellable * cancellable,ProgressCallback progress_callback,gpointer progress_callback_data,DialogCallback dialog_callback,gpointer dialog_callback_data,CopyReadyCallback ready_callback,gpointer user_data)1255 _gth_file_data_copy_async (GthFileData *source,
1256 GFile *destination,
1257 gboolean move,
1258 GthFileCopyFlags flags,
1259 GthOverwriteResponse default_response,
1260 int io_priority,
1261 GCancellable *cancellable,
1262 ProgressCallback progress_callback,
1263 gpointer progress_callback_data,
1264 DialogCallback dialog_callback,
1265 gpointer dialog_callback_data,
1266 CopyReadyCallback ready_callback,
1267 gpointer user_data)
1268 {
1269 _g_copy_file_async_private (source,
1270 destination,
1271 move,
1272 flags,
1273 default_response,
1274 io_priority,
1275 g_file_info_get_size (source->info),
1276 0,
1277 1,
1278 cancellable,
1279 progress_callback,
1280 progress_callback_data,
1281 dialog_callback,
1282 dialog_callback_data,
1283 ready_callback,
1284 user_data);
1285 }
1286
1287
1288 /* -- _g_file_list_copy_async -- */
1289
1290
1291 typedef struct {
1292 GHashTable *source_hash;
1293 GFile *destination;
1294
1295 GList *files; /* GthFileData list */
1296 GList *current;
1297 GList *copied_directories; /* GFile list */
1298 GHashTable *copied_files;
1299 GFile *source_base;
1300 GFile *current_destination;
1301
1302 GList *source_sidecars; /* GFile list */
1303 GList *destination_sidecars; /* GFile list */
1304 GList *current_source_sidecar;
1305 GList *current_destination_sidecar;
1306
1307 goffset tot_size;
1308 goffset copied_size;
1309 gsize tot_files;
1310
1311 char *message;
1312 GthOverwriteResponse default_response;
1313
1314 gboolean move;
1315 GthFileCopyFlags flags;
1316 int io_priority;
1317 GCancellable *cancellable;
1318 ProgressCallback progress_callback;
1319 gpointer progress_callback_data;
1320 DialogCallback dialog_callback;
1321 gpointer dialog_callback_data;
1322 ReadyFunc done_callback;
1323 gpointer user_data;
1324 } CopyData;
1325
1326
1327 static void
copy_data_free(CopyData * copy_data)1328 copy_data_free (CopyData *copy_data)
1329 {
1330 g_free (copy_data->message);
1331 _g_object_list_unref (copy_data->destination_sidecars);
1332 _g_object_list_unref (copy_data->source_sidecars);
1333 _g_object_unref (copy_data->current_destination);
1334 _g_object_list_unref (copy_data->copied_directories);
1335 g_hash_table_destroy (copy_data->copied_files);
1336 _g_object_list_unref (copy_data->files);
1337 _g_object_unref (copy_data->source_base);
1338 g_hash_table_destroy (copy_data->source_hash);
1339 g_object_unref (copy_data->destination);
1340 _g_object_unref (copy_data->cancellable);
1341 g_free (copy_data);
1342 }
1343
1344
1345 static void
copy_data__done(CopyData * copy_data,GError * error)1346 copy_data__done (CopyData *copy_data,
1347 GError *error)
1348 {
1349 copy_data->done_callback (error, copy_data->user_data);
1350 copy_data_free (copy_data);
1351 }
1352
1353
1354 static void copy_data__copy_current_file (CopyData *copy_data);
1355
1356
1357 static void
copy_data__copy_next_file(CopyData * copy_data)1358 copy_data__copy_next_file (CopyData *copy_data)
1359 {
1360 GthFileData *source = (GthFileData *) copy_data->current->data;
1361
1362 copy_data->copied_size += g_file_info_get_size (source->info);
1363 copy_data->current = copy_data->current->next;
1364 copy_data__copy_current_file (copy_data);
1365 }
1366
1367
1368 static void
copy_data__copy_current_file_ready_cb(GthOverwriteResponse response,GList * other_files,GError * error,gpointer user_data)1369 copy_data__copy_current_file_ready_cb (GthOverwriteResponse response,
1370 GList *other_files,
1371 GError *error,
1372 gpointer user_data)
1373 {
1374 CopyData *copy_data = user_data;
1375 GthFileData *source = (GthFileData *) copy_data->current->data;
1376 GList *scan;
1377
1378 if (error == NULL) {
1379 /* save the correctly copied directories in order to delete
1380 * them after moving their content. */
1381 if (copy_data->move) {
1382 if (g_file_info_get_file_type (source->info) == G_FILE_TYPE_DIRECTORY)
1383 copy_data->copied_directories = g_list_prepend (copy_data->copied_directories, g_file_dup (source->file));
1384 }
1385 }
1386 else if ((response == GTH_OVERWRITE_RESPONSE_ALWAYS_NO) || ! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
1387 copy_data__done (copy_data, error);
1388 return;
1389 }
1390
1391 g_hash_table_insert (copy_data->copied_files, g_file_dup (source->file), GINT_TO_POINTER (1));
1392 for (scan = other_files; scan != NULL; scan = scan->next) {
1393 GFile *other_file = G_FILE (scan->data);
1394 g_hash_table_insert (copy_data->copied_files, g_file_dup (other_file), GINT_TO_POINTER (1));
1395 }
1396
1397 copy_data->default_response = response;
1398 copy_data__copy_next_file (copy_data);
1399 }
1400
1401
1402 static void
copy_data__delete_source_directories(CopyData * copy_data)1403 copy_data__delete_source_directories (CopyData *copy_data)
1404 {
1405 GError *error = NULL;
1406
1407 if (copy_data->move) {
1408 GList *scan;
1409
1410 for (scan = copy_data->copied_directories; scan; scan = scan->next) {
1411 GFile *file = scan->data;
1412
1413 if (! g_file_delete (file, copy_data->cancellable, &error)) {
1414 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) {
1415 /* ignore this kind of errors, because
1416 * it is caused by the user choice of
1417 * not move a file. */
1418 g_clear_error (&error);
1419 }
1420 else
1421 break;
1422 }
1423 }
1424 }
1425
1426 copy_data__done (copy_data, error);
1427 }
1428
1429
1430 static void
copy_data__copy_current_file(CopyData * copy_data)1431 copy_data__copy_current_file (CopyData *copy_data)
1432 {
1433 GthFileData *source;
1434 gboolean explicitly_requested;
1435 GFile *destination;
1436
1437 if (copy_data->current == NULL) {
1438 copy_data__delete_source_directories (copy_data);
1439 return;
1440 }
1441
1442 source = GTH_FILE_DATA (copy_data->current->data);
1443
1444 /* Ignore already copied files. These are sidecar files already copied
1445 * with _g_copy_file_async_private */
1446
1447 if (g_hash_table_lookup (copy_data->copied_files, source->file) != NULL) {
1448 call_when_idle ((DataFunc) copy_data__copy_next_file, copy_data);
1449 return;
1450 }
1451
1452 /* Ignore non-existent files that weren't explicitly requested,
1453 * they are children of some requested directory and if they don't
1454 * exist anymore they have been already moved to the destination
1455 * because they are metadata of other files. */
1456 explicitly_requested = (g_hash_table_lookup (copy_data->source_hash, source->file) != NULL);
1457 if (! explicitly_requested) {
1458 if (! g_file_query_exists (source->file, copy_data->cancellable)) {
1459 call_when_idle ((DataFunc) copy_data__copy_next_file, copy_data);
1460 return;
1461 }
1462 }
1463
1464 /* compute the destination */
1465 if (explicitly_requested) {
1466 _g_object_unref (copy_data->source_base);
1467 copy_data->source_base = g_file_get_parent (source->file);
1468 }
1469 destination = _g_file_get_destination (source->file, copy_data->source_base, copy_data->destination);
1470
1471 _g_copy_file_async_private (source,
1472 destination,
1473 copy_data->move,
1474 copy_data->flags,
1475 copy_data->default_response,
1476 copy_data->io_priority,
1477 copy_data->tot_size,
1478 copy_data->copied_size,
1479 copy_data->tot_files,
1480 copy_data->cancellable,
1481 copy_data->progress_callback,
1482 copy_data->progress_callback_data,
1483 copy_data->dialog_callback,
1484 copy_data->dialog_callback_data,
1485 copy_data__copy_current_file_ready_cb,
1486 copy_data);
1487
1488 g_object_unref (destination);
1489 }
1490
1491
1492 static void
copy_files__sources_info_ready_cb(GList * files,GError * error,gpointer user_data)1493 copy_files__sources_info_ready_cb (GList *files,
1494 GError *error,
1495 gpointer user_data)
1496 {
1497 CopyData *copy_data = user_data;
1498 GList *scan;
1499
1500 if (error != NULL) {
1501 copy_data__done (copy_data, error);
1502 return;
1503 }
1504
1505 copy_data->files = _g_object_list_ref (files);
1506 copy_data->tot_size = 0;
1507 copy_data->tot_files = 0;
1508 for (scan = copy_data->files; scan; scan = scan->next) {
1509 GthFileData *file_data = GTH_FILE_DATA (scan->data);
1510
1511 copy_data->tot_size += g_file_info_get_size (file_data->info);
1512 copy_data->tot_files += 1;
1513 }
1514
1515 copy_data->copied_size = 0;
1516 copy_data->current = copy_data->files;
1517 copy_data__copy_current_file (copy_data);
1518 }
1519
1520
1521 void
_g_file_list_copy_async(GList * sources,GFile * destination,gboolean move,GthFileCopyFlags flags,GthOverwriteResponse default_response,int io_priority,GCancellable * cancellable,ProgressCallback progress_callback,gpointer progress_callback_data,DialogCallback dialog_callback,gpointer dialog_callback_data,ReadyFunc done_callback,gpointer user_data)1522 _g_file_list_copy_async (GList *sources, /* GFile list */
1523 GFile *destination,
1524 gboolean move,
1525 GthFileCopyFlags flags,
1526 GthOverwriteResponse default_response,
1527 int io_priority,
1528 GCancellable *cancellable,
1529 ProgressCallback progress_callback,
1530 gpointer progress_callback_data,
1531 DialogCallback dialog_callback,
1532 gpointer dialog_callback_data,
1533 ReadyFunc done_callback,
1534 gpointer user_data)
1535 {
1536 CopyData *copy_data;
1537 GList *scan;
1538
1539 copy_data = g_new0 (CopyData, 1);
1540 copy_data->destination = g_object_ref (destination);
1541 copy_data->move = move;
1542 copy_data->flags = flags;
1543 copy_data->io_priority = io_priority;
1544 copy_data->cancellable = _g_object_ref (cancellable);
1545 copy_data->progress_callback = progress_callback;
1546 copy_data->progress_callback_data = progress_callback_data;
1547 copy_data->dialog_callback = dialog_callback;
1548 copy_data->dialog_callback_data = dialog_callback_data;
1549 copy_data->done_callback = done_callback;
1550 copy_data->user_data = user_data;
1551 copy_data->default_response = default_response;
1552 copy_data->copied_files = g_hash_table_new_full ((GHashFunc) g_file_hash, (GEqualFunc) g_file_equal, (GDestroyNotify) g_object_unref, NULL);
1553
1554 /* save the explicitly requested files */
1555 copy_data->source_hash = g_hash_table_new_full ((GHashFunc) g_file_hash, (GEqualFunc) g_file_equal, (GDestroyNotify) g_object_unref, NULL);
1556 for (scan = sources; scan; scan = scan->next)
1557 g_hash_table_insert (copy_data->source_hash, g_object_ref (scan->data), GINT_TO_POINTER (1));
1558
1559 if (copy_data->progress_callback != NULL)
1560 copy_data->progress_callback (NULL,
1561 copy_data->move ? _("Moving files") : _("Copying files"),
1562 _("Getting file information"),
1563 TRUE,
1564 0.0,
1565 copy_data->progress_callback_data);
1566
1567 /* for each directory in 'sources' this query will add all of its content
1568 * to the file list. */
1569 _g_file_list_query_info_async (sources,
1570 GTH_LIST_RECURSIVE,
1571 "standard::name,standard::display-name,standard::type,standard::size",
1572 copy_data->cancellable,
1573 copy_files__sources_info_ready_cb,
1574 copy_data);
1575 }
1576
1577
1578 gboolean
_g_file_move(GFile * source,GFile * destination,GFileCopyFlags flags,GCancellable * cancellable,GFileProgressCallback progress_callback,gpointer progress_callback_data,GError ** error)1579 _g_file_move (GFile *source,
1580 GFile *destination,
1581 GFileCopyFlags flags,
1582 GCancellable *cancellable,
1583 GFileProgressCallback progress_callback,
1584 gpointer progress_callback_data,
1585 GError **error)
1586 {
1587 GList *source_sidecars = NULL;
1588 GList *destination_sidecars = NULL;
1589 GList *scan1;
1590 GList *scan2;
1591
1592 if (! g_file_move (source,
1593 destination,
1594 flags,
1595 cancellable,
1596 progress_callback,
1597 progress_callback_data,
1598 error))
1599 {
1600 return FALSE;
1601 }
1602
1603 if ((flags & G_FILE_COPY_ALL_METADATA) == 0)
1604 return TRUE;
1605
1606 /* move the metadata sidecars if requested */
1607
1608 gth_hook_invoke ("add-sidecars", source, &source_sidecars);
1609 source_sidecars = g_list_reverse (source_sidecars);
1610
1611 gth_hook_invoke ("add-sidecars", destination, &destination_sidecars);
1612 destination_sidecars = g_list_reverse (destination_sidecars);
1613
1614 for (scan1 = source_sidecars, scan2 = destination_sidecars;
1615 scan1 && scan2;
1616 scan1 = scan1->next, scan2 = scan2->next)
1617 {
1618 source = scan1->data;
1619 destination = scan2->data;
1620
1621 g_file_move (source, destination, 0, cancellable, NULL, NULL, NULL);
1622 }
1623
1624 _g_object_list_unref (destination_sidecars);
1625 _g_object_list_unref (source_sidecars);
1626
1627 return TRUE;
1628 }
1629
1630
1631 gboolean
_g_file_list_delete(GList * file_list,gboolean include_metadata,GError ** error)1632 _g_file_list_delete (GList *file_list,
1633 gboolean include_metadata,
1634 GError **error)
1635 {
1636 GList *scan;
1637
1638 for (scan = file_list; scan; scan = scan->next) {
1639 GFile *file = scan->data;
1640
1641 if (! g_file_delete (file, NULL, error))
1642 return FALSE;
1643 }
1644
1645 if (include_metadata) {
1646 GList *sidecars;
1647 GList *scan;
1648
1649 sidecars = NULL;
1650 for (scan = file_list; scan; scan = scan->next)
1651 gth_hook_invoke ("add-sidecars", scan->data, &sidecars);
1652 sidecars = g_list_reverse (sidecars);
1653
1654 for (scan = sidecars; scan; scan = scan->next) {
1655 GFile *file = scan->data;
1656 g_file_delete (file, NULL, NULL);
1657 }
1658
1659 _g_object_list_unref (sidecars);
1660 }
1661
1662 return TRUE;
1663 }
1664
1665
1666 /* -- _g_delete_files_async -- */
1667
1668
1669 typedef struct {
1670 GList *file_list;
1671 GList *current;
1672 gboolean include_metadata;
1673 GCancellable *cancellable;
1674 ProgressCallback progress_callback;
1675 ReadyFunc callback;
1676 gpointer user_data;
1677 glong n_files;
1678 glong n_deleted;
1679 } DeleteData;
1680
1681
1682 static void
delete_data_free(DeleteData * delete_data)1683 delete_data_free (DeleteData *delete_data)
1684 {
1685 _g_object_list_unref (delete_data->file_list);
1686 _g_object_unref (delete_data->cancellable);
1687 g_free (delete_data);
1688 }
1689
1690
1691 static void delete_files__delete_current (DeleteData *delete_data);
1692
1693
1694 static void
delete_files__delete_current_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1695 delete_files__delete_current_cb (GObject *source_object,
1696 GAsyncResult *result,
1697 gpointer user_data)
1698 {
1699 DeleteData *delete_data = user_data;
1700 GError *error = NULL;
1701
1702 if (! g_file_delete_finish (G_FILE (source_object), result, &error)) {
1703 delete_data->callback (error, delete_data->user_data);
1704 delete_data_free (delete_data);
1705 g_error_free (error);
1706 return;
1707 }
1708
1709 delete_data->n_deleted++;
1710 delete_data->current = delete_data->current->next;
1711 delete_files__delete_current (delete_data);
1712 }
1713
1714
1715 static void
delete_files__delete_current(DeleteData * delete_data)1716 delete_files__delete_current (DeleteData *delete_data)
1717 {
1718 GthFileData *file_data;
1719
1720 if (delete_data->current == NULL) {
1721 delete_data->callback (NULL, delete_data->user_data);
1722 delete_data_free (delete_data);
1723 return;
1724 }
1725
1726 if (delete_data->progress_callback != NULL)
1727 delete_data->progress_callback (NULL,
1728 _("Deleting files"),
1729 NULL,
1730 FALSE,
1731 (double) (delete_data->n_deleted + 1) / (delete_data->n_files + 1),
1732 delete_data->user_data);
1733
1734 file_data = delete_data->current->data;
1735 g_file_delete_async (file_data->file,
1736 G_PRIORITY_DEFAULT,
1737 delete_data->cancellable,
1738 delete_files__delete_current_cb,
1739 delete_data);
1740 }
1741
1742
1743 static void
delete_files__info_ready_cb(GList * files,GError * error,gpointer user_data)1744 delete_files__info_ready_cb (GList *files,
1745 GError *error,
1746 gpointer user_data)
1747 {
1748 DeleteData *delete_data = user_data;
1749
1750 if (error != NULL) {
1751 delete_data->callback (error, delete_data->user_data);
1752 delete_data_free (delete_data);
1753 return;
1754 }
1755
1756 delete_data->file_list = _g_object_list_ref (files);
1757 delete_data->file_list = g_list_reverse (delete_data->file_list);
1758 delete_data->current = delete_data->file_list;
1759 delete_data->n_files = g_list_length (delete_data->file_list);
1760 delete_data->n_deleted = 0;
1761 delete_files__delete_current (delete_data);
1762 }
1763
1764
1765 void
_g_file_list_delete_async(GList * file_list,gboolean recursive,gboolean include_metadata,GCancellable * cancellable,ProgressCallback progress_callback,ReadyFunc callback,gpointer user_data)1766 _g_file_list_delete_async (GList *file_list,
1767 gboolean recursive,
1768 gboolean include_metadata,
1769 GCancellable *cancellable,
1770 ProgressCallback progress_callback,
1771 ReadyFunc callback,
1772 gpointer user_data)
1773 {
1774 DeleteData *delete_data;
1775 GthListFlags flags;
1776
1777 delete_data = g_new0 (DeleteData, 1);
1778 delete_data->file_list = NULL;
1779 delete_data->include_metadata = include_metadata;
1780 delete_data->cancellable = _g_object_ref (cancellable);
1781 delete_data->progress_callback = progress_callback;
1782 delete_data->callback = callback;
1783 delete_data->user_data = user_data;
1784
1785 flags = GTH_LIST_NO_FOLLOW_LINKS;
1786 if (recursive)
1787 flags |= GTH_LIST_RECURSIVE;
1788 if (include_metadata)
1789 flags |= GTH_LIST_INCLUDE_SIDECARS;
1790
1791 if (delete_data->progress_callback != NULL)
1792 delete_data->progress_callback (NULL,
1793 _("Getting file information"),
1794 NULL,
1795 TRUE,
1796 0.0,
1797 delete_data->user_data);
1798
1799 _g_file_list_query_info_async (file_list,
1800 flags,
1801 GFILE_NAME_TYPE_ATTRIBUTES,
1802 delete_data->cancellable,
1803 delete_files__info_ready_cb,
1804 delete_data);
1805 }
1806
1807
1808 /* -- _g_trash_files_async -- */
1809
1810
1811 typedef struct {
1812 GList *file_list;
1813 GList *current;
1814 GCancellable *cancellable;
1815 ProgressCallback progress_callback;
1816 ReadyFunc callback;
1817 gpointer user_data;
1818 glong n_files;
1819 glong n_deleted;
1820 } TrashData;
1821
1822
1823 static void
trash_data_free(TrashData * tdata)1824 trash_data_free (TrashData *tdata)
1825 {
1826 _g_object_list_unref (tdata->file_list);
1827 _g_object_unref (tdata->cancellable);
1828 g_free (tdata);
1829 }
1830
1831
1832 static void trash_files__delete_current (TrashData *tdata);
1833
1834
1835 static void
trash_files__delete_current_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1836 trash_files__delete_current_cb (GObject *source_object,
1837 GAsyncResult *result,
1838 gpointer user_data)
1839 {
1840 TrashData *tdata = user_data;
1841 GError *error = NULL;
1842
1843 if (! g_file_trash_finish (G_FILE (source_object), result, &error)) {
1844 tdata->callback (error, tdata->user_data);
1845 trash_data_free (tdata);
1846 g_error_free (error);
1847 return;
1848 }
1849
1850 tdata->n_deleted++;
1851 tdata->current = tdata->current->next;
1852 trash_files__delete_current (tdata);
1853 }
1854
1855
1856 static void
trash_files__delete_current(TrashData * tdata)1857 trash_files__delete_current (TrashData *tdata)
1858 {
1859 GthFileData *file_data;
1860
1861 if (tdata->current == NULL) {
1862 tdata->callback (NULL, tdata->user_data);
1863 trash_data_free (tdata);
1864 return;
1865 }
1866
1867 if (tdata->progress_callback != NULL)
1868 tdata->progress_callback (NULL,
1869 _("Moving files to trash"),
1870 NULL,
1871 FALSE,
1872 (double) (tdata->n_deleted + 1) / (tdata->n_files + 1),
1873 tdata->user_data);
1874
1875 file_data = GTH_FILE_DATA (tdata->current->data);
1876 g_file_trash_async (file_data->file,
1877 G_PRIORITY_DEFAULT,
1878 tdata->cancellable,
1879 trash_files__delete_current_cb,
1880 tdata);
1881 }
1882
1883
1884 static void
trash_files__info_ready_cb(GList * files,GError * error,gpointer user_data)1885 trash_files__info_ready_cb (GList *files,
1886 GError *error,
1887 gpointer user_data)
1888 {
1889 TrashData *tdata = user_data;
1890
1891 if (error != NULL) {
1892 tdata->callback (error, tdata->user_data);
1893 trash_data_free (tdata);
1894 return;
1895 }
1896
1897 tdata->file_list = _g_object_list_ref (files);
1898 tdata->n_files = g_list_length (tdata->file_list);
1899 tdata->n_deleted = 0;
1900 tdata->current = tdata->file_list;
1901 trash_files__delete_current (tdata);
1902 }
1903
1904
1905 void
_g_file_list_trash_async(GList * file_list,GCancellable * cancellable,ProgressCallback progress_callback,ReadyFunc callback,gpointer user_data)1906 _g_file_list_trash_async (GList *file_list, /* GFile list */
1907 GCancellable *cancellable,
1908 ProgressCallback progress_callback,
1909 ReadyFunc callback,
1910 gpointer user_data)
1911 {
1912 TrashData *tdata;
1913
1914 tdata = g_new0 (TrashData, 1);
1915 tdata->file_list = NULL;
1916 tdata->cancellable = _g_object_ref (cancellable);
1917 tdata->progress_callback = progress_callback;
1918 tdata->callback = callback;
1919 tdata->user_data = user_data;
1920
1921 if (tdata->progress_callback != NULL)
1922 tdata->progress_callback (NULL,
1923 _("Getting file information"),
1924 NULL,
1925 TRUE,
1926 0.0,
1927 tdata->user_data);
1928
1929 _g_file_list_query_info_async (file_list,
1930 GTH_LIST_INCLUDE_SIDECARS,
1931 GFILE_NAME_TYPE_ATTRIBUTES,
1932 tdata->cancellable,
1933 trash_files__info_ready_cb,
1934 tdata);
1935 }
1936
1937
1938 #define BUFFER_SIZE (64 * 1024)
1939
1940
1941 gboolean
_g_input_stream_read_all(GInputStream * istream,void ** buffer,gsize * size,GCancellable * cancellable,GError ** error)1942 _g_input_stream_read_all (GInputStream *istream,
1943 void **buffer,
1944 gsize *size,
1945 GCancellable *cancellable,
1946 GError **error)
1947 {
1948 gboolean retval = FALSE;
1949 guchar *local_buffer = NULL;
1950 char *tmp_buffer;
1951 gsize count;
1952 gssize n;
1953
1954 tmp_buffer = g_new (char, BUFFER_SIZE);
1955 count = 0;
1956 for (;;) {
1957 n = g_input_stream_read (istream, tmp_buffer, BUFFER_SIZE, cancellable, error);
1958 if (n < 0) {
1959 g_free (local_buffer);
1960 retval = FALSE;
1961 break;
1962 }
1963 else if (n == 0) {
1964 *buffer = local_buffer;
1965 *size = count;
1966 retval = TRUE;
1967 break;
1968 }
1969
1970 /* the '+ 1' here is used to allow to add a NULL character at the
1971 * end of the buffer. */
1972 local_buffer = g_realloc (local_buffer, count + n + 1);
1973 memcpy (local_buffer + count, tmp_buffer, n);
1974 count += n;
1975 }
1976
1977 g_free (tmp_buffer);
1978
1979 return retval;
1980 }
1981
1982
1983 gboolean
_g_file_load_in_buffer(GFile * file,void ** buffer,gsize * size,GCancellable * cancellable,GError ** error)1984 _g_file_load_in_buffer (GFile *file,
1985 void **buffer,
1986 gsize *size,
1987 GCancellable *cancellable,
1988 GError **error)
1989 {
1990 GInputStream *istream;
1991 gboolean retval = FALSE;
1992
1993 istream = (GInputStream *) g_file_read (file, cancellable, error);
1994 if (istream != NULL) {
1995 retval = _g_input_stream_read_all (istream, buffer, size, cancellable, error);
1996 g_object_unref (istream);
1997 }
1998
1999 return retval;
2000 }
2001
2002
2003 typedef struct {
2004 int io_priority;
2005 GCancellable *cancellable;
2006 BufferReadyCallback callback;
2007 gpointer user_data;
2008 GInputStream *stream;
2009 guchar tmp_buffer[BUFFER_SIZE];
2010 guchar *buffer;
2011 gsize count;
2012 } LoadData;
2013
2014
2015 static void
load_data_free(LoadData * load_data)2016 load_data_free (LoadData *load_data)
2017 {
2018 if (load_data->stream != NULL)
2019 g_object_unref (load_data->stream);
2020 g_free (load_data->buffer);
2021 g_free (load_data);
2022 }
2023
2024
2025 static void
load_file__stream_read_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2026 load_file__stream_read_cb (GObject *source_object,
2027 GAsyncResult *result,
2028 gpointer user_data)
2029 {
2030 LoadData *load_data = user_data;
2031 GError *error = NULL;
2032 gssize count;
2033
2034 count = g_input_stream_read_finish (load_data->stream, result, &error);
2035 if (count < 0) {
2036 load_data->callback ((gpointer *) &load_data->buffer, -1, error, load_data->user_data);
2037 load_data_free (load_data);
2038 return;
2039 }
2040 else if (count == 0) {
2041 if (load_data->buffer != NULL)
2042 ((char *)load_data->buffer)[load_data->count] = 0;
2043 load_data->callback ((gpointer *) &load_data->buffer, load_data->count, NULL, load_data->user_data);
2044 load_data_free (load_data);
2045 return;
2046 }
2047
2048 load_data->buffer = g_realloc (load_data->buffer, load_data->count + count + 1);
2049 memcpy (load_data->buffer + load_data->count, load_data->tmp_buffer, count);
2050 load_data->count += count;
2051
2052 g_input_stream_read_async (load_data->stream,
2053 load_data->tmp_buffer,
2054 BUFFER_SIZE,
2055 load_data->io_priority,
2056 load_data->cancellable,
2057 load_file__stream_read_cb,
2058 load_data);
2059 }
2060
2061
2062 static void
load_file__file_read_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2063 load_file__file_read_cb (GObject *source_object,
2064 GAsyncResult *result,
2065 gpointer user_data)
2066 {
2067 LoadData *load_data = user_data;
2068 GError *error = NULL;
2069
2070 load_data->stream = (GInputStream *) g_file_read_finish (G_FILE (source_object), result, &error);
2071 if (load_data->stream == NULL) {
2072 load_data->callback ((gpointer *) &load_data->buffer, -1, error, load_data->user_data);
2073 load_data_free (load_data);
2074 return;
2075 }
2076
2077 load_data->count = 0;
2078 g_input_stream_read_async (load_data->stream,
2079 load_data->tmp_buffer,
2080 BUFFER_SIZE,
2081 load_data->io_priority,
2082 load_data->cancellable,
2083 load_file__stream_read_cb,
2084 load_data);
2085 }
2086
2087
2088 void
_g_file_load_async(GFile * file,int io_priority,GCancellable * cancellable,BufferReadyCallback callback,gpointer user_data)2089 _g_file_load_async (GFile *file,
2090 int io_priority,
2091 GCancellable *cancellable,
2092 BufferReadyCallback callback,
2093 gpointer user_data)
2094 {
2095 LoadData *load_data;
2096
2097 load_data = g_new0 (LoadData, 1);
2098 load_data->io_priority = io_priority;
2099 load_data->cancellable = cancellable;
2100 load_data->callback = callback;
2101 load_data->user_data = user_data;
2102
2103 g_file_read_async (file, io_priority, cancellable, load_file__file_read_cb, load_data);
2104 }
2105
2106
2107 /* -- _g_file_write_async -- */
2108
2109
2110 typedef struct {
2111 int io_priority;
2112 GCancellable *cancellable;
2113 BufferReadyCallback callback;
2114 gpointer user_data;
2115 guchar *buffer;
2116 gsize count;
2117 gsize written;
2118 GError *error;
2119 } WriteData;
2120
2121
2122 static void
write_data_free(WriteData * write_data)2123 write_data_free (WriteData *write_data)
2124 {
2125 g_free (write_data->buffer);
2126 g_free (write_data);
2127 }
2128
2129
2130 static void
write_file__notify(gpointer user_data)2131 write_file__notify (gpointer user_data)
2132 {
2133 WriteData *write_data = user_data;
2134
2135 write_data->callback ((gpointer *) &write_data->buffer, write_data->count, write_data->error, write_data->user_data);
2136 write_data_free (write_data);
2137 }
2138
2139
2140 static void
write_file__stream_flush_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2141 write_file__stream_flush_cb (GObject *source_object,
2142 GAsyncResult *result,
2143 gpointer user_data)
2144 {
2145 GOutputStream *stream = (GOutputStream *) source_object;
2146 WriteData *write_data = user_data;
2147 GError *error = NULL;
2148
2149 g_output_stream_flush_finish (stream, result, &error);
2150 write_data->error = error;
2151 g_object_unref (stream);
2152
2153 call_when_idle (write_file__notify, write_data);
2154 }
2155
2156
2157 static void
write_file__stream_write_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2158 write_file__stream_write_ready_cb (GObject *source_object,
2159 GAsyncResult *result,
2160 gpointer user_data)
2161 {
2162 GOutputStream *stream = (GOutputStream *) source_object;
2163 WriteData *write_data = user_data;
2164 GError *error = NULL;
2165 gssize count;
2166
2167 count = g_output_stream_write_finish (stream, result, &error);
2168 write_data->written += count;
2169
2170 if ((count == 0) || (write_data->written == write_data->count)) {
2171 g_output_stream_flush_async (stream,
2172 write_data->io_priority,
2173 write_data->cancellable,
2174 write_file__stream_flush_cb,
2175 user_data);
2176 return;
2177 }
2178
2179 g_output_stream_write_async (stream,
2180 write_data->buffer + write_data->written,
2181 write_data->count - write_data->written,
2182 write_data->io_priority,
2183 write_data->cancellable,
2184 write_file__stream_write_ready_cb,
2185 write_data);
2186 }
2187
2188
2189 static void
write_file__replace_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2190 write_file__replace_ready_cb (GObject *source_object,
2191 GAsyncResult *result,
2192 gpointer user_data)
2193 {
2194 WriteData *write_data = user_data;
2195 GOutputStream *stream;
2196 GError *error = NULL;
2197
2198 stream = (GOutputStream*) g_file_replace_finish ((GFile*) source_object, result, &error);
2199 if (stream == NULL) {
2200 write_data->callback ((gpointer *) &write_data->buffer, write_data->count, error, write_data->user_data);
2201 write_data_free (write_data);
2202 return;
2203 }
2204
2205 write_data->written = 0;
2206 g_output_stream_write_async (stream,
2207 write_data->buffer,
2208 write_data->count,
2209 write_data->io_priority,
2210 write_data->cancellable,
2211 write_file__stream_write_ready_cb,
2212 write_data);
2213 }
2214
2215
2216 static void
write_file__create_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2217 write_file__create_ready_cb (GObject *source_object,
2218 GAsyncResult *result,
2219 gpointer user_data)
2220 {
2221 WriteData *write_data = user_data;
2222 GOutputStream *stream;
2223 GError *error = NULL;
2224
2225 stream = (GOutputStream*) g_file_create_finish ((GFile*) source_object, result, &error);
2226 if (stream == NULL) {
2227 write_data->callback ((gpointer *) &write_data->buffer, write_data->count, error, write_data->user_data);
2228 write_data_free (write_data);
2229 return;
2230 }
2231
2232 write_data->written = 0;
2233 g_output_stream_write_async (stream,
2234 write_data->buffer,
2235 write_data->count,
2236 write_data->io_priority,
2237 write_data->cancellable,
2238 write_file__stream_write_ready_cb,
2239 write_data);
2240 }
2241
2242
2243 void
_g_file_write_async(GFile * file,void * buffer,gsize count,gboolean replace,int io_priority,GCancellable * cancellable,BufferReadyCallback callback,gpointer user_data)2244 _g_file_write_async (GFile *file,
2245 void *buffer,
2246 gsize count,
2247 gboolean replace,
2248 int io_priority,
2249 GCancellable *cancellable,
2250 BufferReadyCallback callback,
2251 gpointer user_data)
2252 {
2253 WriteData *write_data;
2254
2255 write_data = g_new0 (WriteData, 1);
2256 write_data->buffer = buffer;
2257 write_data->count = count;
2258 write_data->io_priority = io_priority;
2259 write_data->cancellable = cancellable;
2260 write_data->callback = callback;
2261 write_data->user_data = user_data;
2262
2263 if (replace)
2264 g_file_replace_async (file,
2265 NULL,
2266 FALSE,
2267 0,
2268 io_priority,
2269 cancellable,
2270 write_file__replace_ready_cb,
2271 write_data);
2272 else
2273 g_file_create_async (file,
2274 0,
2275 io_priority,
2276 cancellable,
2277 write_file__create_ready_cb,
2278 write_data);
2279 }
2280
2281
2282 GFile *
_g_file_create_unique(GFile * parent,const char * display_name,const char * suffix,GError ** error)2283 _g_file_create_unique (GFile *parent,
2284 const char *display_name,
2285 const char *suffix,
2286 GError **error)
2287 {
2288 GFile *file = NULL;
2289 GError *local_error = NULL;
2290 int n;
2291 GFileOutputStream *stream = NULL;
2292
2293 n = 0;
2294 do {
2295 char *new_display_name;
2296
2297 if (file != NULL)
2298 g_object_unref (file);
2299
2300 n++;
2301 if (n == 1)
2302 new_display_name = g_strdup_printf ("%s%s", display_name, suffix);
2303 else
2304 new_display_name = g_strdup_printf ("%s %d%s", display_name, n, suffix);
2305
2306 file = g_file_get_child_for_display_name (parent, new_display_name, &local_error);
2307 if (local_error == NULL)
2308 stream = g_file_create (file, 0, NULL, &local_error);
2309
2310 if ((stream == NULL) && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
2311 g_clear_error (&local_error);
2312
2313 g_free (new_display_name);
2314 }
2315 while ((stream == NULL) && (local_error == NULL));
2316
2317 if (stream == NULL) {
2318 if (error != NULL)
2319 *error = local_error;
2320 g_object_unref (file);
2321 file = NULL;
2322 }
2323 else
2324 g_object_unref (stream);
2325
2326 return file;
2327 }
2328
2329
2330 GFile *
_g_directory_create_tmp(void)2331 _g_directory_create_tmp (void)
2332 {
2333 const int max_attemps = 10;
2334 const int name_len = 12;
2335 GFile *tmp_dir;
2336 GFile *dir = NULL;
2337 int n;
2338
2339 tmp_dir = g_file_new_for_path (g_get_tmp_dir ());
2340 if (tmp_dir == NULL)
2341 return NULL;
2342
2343 for (n = 0; n < max_attemps; n++) {
2344 char *name;
2345
2346 name = _g_str_random (name_len);
2347 dir = g_file_get_child (tmp_dir, name);
2348 g_free (name);
2349
2350 if (g_file_make_directory (dir, NULL, NULL))
2351 break;
2352
2353 g_object_unref (dir);
2354 }
2355
2356 g_object_unref (tmp_dir);
2357
2358 return dir;
2359 }
2360
2361
2362 /* -- _g_file_write -- */
2363
2364
2365 gboolean
_g_file_write(GFile * file,gboolean make_backup,GFileCreateFlags flags,void * buffer,gsize count,GCancellable * cancellable,GError ** error)2366 _g_file_write (GFile *file,
2367 gboolean make_backup,
2368 GFileCreateFlags flags,
2369 void *buffer,
2370 gsize count,
2371 GCancellable *cancellable,
2372 GError **error)
2373 {
2374 gboolean success;
2375 GOutputStream *stream;
2376
2377 stream = (GOutputStream *) g_file_replace (file, NULL, make_backup, flags, cancellable, error);
2378 if (stream != NULL)
2379 success = g_output_stream_write_all (stream, buffer, count, NULL, cancellable, error);
2380 else
2381 success = FALSE;
2382
2383 _g_object_unref (stream);
2384
2385 return success;
2386 }
2387
2388
2389 gboolean
_g_file_set_modification_time(GFile * file,GTimeVal * timeval,GCancellable * cancellable,GError ** error)2390 _g_file_set_modification_time (GFile *file,
2391 GTimeVal *timeval,
2392 GCancellable *cancellable,
2393 GError **error)
2394 {
2395 GFileInfo *info;
2396 gboolean result;
2397
2398 info = g_file_info_new ();
2399 g_file_info_set_modification_time (info, timeval);
2400 result = g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, cancellable, error);
2401
2402 g_object_unref (info);
2403
2404 return result;
2405 }
2406
2407
2408 GFileInfo *
_g_file_get_info_for_display(GFile * file)2409 _g_file_get_info_for_display (GFile *file)
2410 {
2411 GFileInfo *file_info;
2412 GthFileSource *file_source;
2413
2414 file_info = NULL;
2415 file_source = gth_main_get_file_source (file);
2416 if ((file_source != NULL) && ! GTH_IS_FILE_SOURCE_VFS (file_source))
2417 file_info = gth_file_source_get_file_info (file_source, file, GFILE_DISPLAY_ATTRIBUTES);
2418
2419 if (file_info == NULL) {
2420 char *name;
2421 char *uri;
2422 GIcon *icon;
2423
2424 file_info = g_file_info_new ();
2425
2426 name = _g_file_get_display_name (file);
2427 g_file_info_set_display_name (file_info, name);
2428
2429 uri = g_file_get_uri (file);
2430 icon = g_themed_icon_new (g_str_has_prefix (uri, "file://") ? "folder-symbolic" : "folder-remote-symbolic");
2431 g_file_info_set_symbolic_icon (file_info, icon);
2432
2433 g_object_unref (icon);
2434 g_free (uri);
2435 g_free (name);
2436 }
2437
2438 return file_info;
2439 }
2440
2441
2442 GMenuItem *
_g_menu_item_new_for_file(GFile * file,const char * custom_label)2443 _g_menu_item_new_for_file (GFile *file,
2444 const char *custom_label)
2445 {
2446 GMenuItem *item;
2447 GFileInfo *info;
2448
2449 item = g_menu_item_new (NULL, NULL);
2450 info = _g_file_get_info_for_display (file);
2451 if (info != NULL) {
2452 g_menu_item_set_label (item, (custom_label != NULL) ? custom_label : g_file_info_get_display_name (info));
2453 g_menu_item_set_icon (item, g_file_info_get_symbolic_icon (info));
2454
2455 g_object_unref (info);
2456 }
2457
2458 return item;
2459 }
2460
2461
2462 GMenuItem *
_g_menu_item_new_for_file_data(GthFileData * file_data)2463 _g_menu_item_new_for_file_data (GthFileData *file_data)
2464 {
2465 GMenuItem *item;
2466
2467 item = g_menu_item_new (NULL, NULL);
2468 g_menu_item_set_label (item, g_file_info_get_display_name (file_data->info));
2469 g_menu_item_set_icon (item, g_file_info_get_symbolic_icon (file_data->info));
2470
2471 return item;
2472 }
2473