4 * Copyright (C) 2008 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
17 #include "filefilter.h"
19 #include "thumb_standard.h"
20 #include "ui_fileops.h"
25 static GHashTable *file_data_pool = NULL;
26 static GHashTable *file_data_planned_change_hash = NULL;
28 static gint sidecar_file_priority(const gchar *path);
32 *-----------------------------------------------------------------------------
33 * text conversion utils
34 *-----------------------------------------------------------------------------
37 gchar *text_from_size(gint64 size)
43 /* what I would like to use is printf("%'d", size)
44 * BUT: not supported on every libc :(
48 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
49 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
53 a = g_strdup_printf("%d", (guint)size);
59 b = g_new(gchar, l + n + 1);
84 gchar *text_from_size_abrev(gint64 size)
86 if (size < (gint64)1024)
88 return g_strdup_printf(_("%d bytes"), (gint)size);
90 if (size < (gint64)1048576)
92 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
94 if (size < (gint64)1073741824)
96 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
99 /* to avoid overflowing the gdouble, do division in two steps */
101 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
104 /* note: returned string is valid until next call to text_from_time() */
105 const gchar *text_from_time(time_t t)
107 static gchar *ret = NULL;
111 GError *error = NULL;
113 btime = localtime(&t);
115 /* the %x warning about 2 digit years is not an error */
116 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
117 if (buflen < 1) return "";
120 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
123 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
132 *-----------------------------------------------------------------------------
134 *-----------------------------------------------------------------------------
137 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
138 static void file_data_check_sidecars(FileData *fd);
139 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
142 void file_data_increment_version(FileData *fd)
148 fd->parent->version++;
149 fd->parent->valid_marks = 0;
153 static void file_data_set_collate_keys(FileData *fd)
155 gchar *caseless_name;
157 caseless_name = g_utf8_casefold(fd->name, -1);
159 g_free(fd->collate_key_name);
160 g_free(fd->collate_key_name_nocase);
162 #if GLIB_CHECK_VERSION(2, 8, 0)
163 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
164 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
166 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
167 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
169 g_free(caseless_name);
172 static void file_data_set_path(FileData *fd, const gchar *path)
174 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
175 g_assert(file_data_pool);
179 if (fd->original_path)
181 g_hash_table_remove(file_data_pool, fd->original_path);
182 g_free(fd->original_path);
185 g_assert(!g_hash_table_lookup(file_data_pool, path));
187 fd->original_path = g_strdup(path);
188 g_hash_table_insert(file_data_pool, fd->original_path, fd);
190 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
192 fd->path = g_strdup(path);
194 fd->extension = fd->name + 1;
195 file_data_set_collate_keys(fd);
199 fd->path = g_strdup(path);
200 fd->name = filename_from_path(fd->path);
202 if (strcmp(fd->name, "..") == 0)
204 gchar *dir = remove_level_from_path(path);
206 fd->path = remove_level_from_path(dir);
209 fd->extension = fd->name + 2;
210 file_data_set_collate_keys(fd);
213 else if (strcmp(fd->name, ".") == 0)
216 fd->path = remove_level_from_path(path);
218 fd->extension = fd->name + 1;
219 file_data_set_collate_keys(fd);
223 fd->extension = extension_from_path(fd->path);
224 if (fd->extension == NULL)
225 fd->extension = fd->name + strlen(fd->name);
227 file_data_set_collate_keys(fd);
230 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
232 gboolean ret = FALSE;
235 if (fd->size != st->st_size ||
236 fd->date != st->st_mtime)
238 fd->size = st->st_size;
239 fd->date = st->st_mtime;
240 fd->mode = st->st_mode;
241 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
242 fd->thumb_pixbuf = NULL;
243 file_data_increment_version(fd);
244 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
248 work = fd->sidecar_files;
251 FileData *sfd = work->data;
255 if (!stat_utf8(sfd->path, &st))
259 file_data_disconnect_sidecar_file(fd, sfd);
264 ret |= file_data_check_changed_files_recursive(sfd, &st);
270 gboolean file_data_check_changed_files(FileData *fd)
272 gboolean ret = FALSE;
275 if (fd->parent) fd = fd->parent;
277 if (!stat_utf8(fd->path, &st))
280 FileData *sfd = NULL;
282 /* parent is missing, we have to rebuild whole group */
287 work = fd->sidecar_files;
293 file_data_disconnect_sidecar_file(fd, sfd);
295 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
296 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
300 ret |= file_data_check_changed_files_recursive(fd, &st);
306 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
310 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
313 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
315 fd = g_hash_table_lookup(file_data_pool, path_utf8);
321 if (!fd && file_data_planned_change_hash)
323 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
326 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
328 file_data_apply_ci(fd);
337 changed = file_data_check_changed_files(fd);
339 changed = file_data_check_changed_files_recursive(fd, st);
340 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
341 file_data_check_sidecars(fd);
342 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
347 fd = g_new0(FileData, 1);
351 fd->collate_key_name = NULL;
352 fd->collate_key_name_nocase = NULL;
353 fd->original_path = NULL;
355 fd->size = st->st_size;
356 fd->date = st->st_mtime;
357 fd->mode = st->st_mode;
358 fd->thumb_pixbuf = NULL;
359 fd->sidecar_files = NULL;
361 fd->magick = 0x12345678;
363 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
366 file_data_check_sidecars(fd);
371 static void file_data_check_sidecars(FileData *fd)
375 FileData *parent_fd = NULL;
378 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
381 base_len = fd->extension - fd->path;
382 fname = g_string_new_len(fd->path, base_len);
383 work = sidecar_ext_get_list();
387 /* check for possible sidecar files;
388 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
389 they have fd->ref set to 0 and file_data unref must chack and free them all together
390 (using fd->ref would cause loops and leaks)
394 gchar *ext = work->data;
398 if (strcmp(ext, fd->extension) == 0)
400 new_fd = fd; /* processing the original file */
405 g_string_truncate(fname, base_len);
406 g_string_append(fname, ext);
408 if (!stat_utf8(fname->str, &nst))
411 new_fd = file_data_new(fname->str, &nst, FALSE);
413 if (new_fd->disable_grouping)
415 file_data_unref(new_fd);
419 new_fd->ref--; /* do not use ref here */
423 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
425 file_data_merge_sidecar_files(parent_fd, new_fd);
427 g_string_free(fname, TRUE);
431 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
433 gchar *path_utf8 = path_to_utf8(path);
434 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
440 FileData *file_data_new_simple(const gchar *path_utf8)
444 if (!stat_utf8(path_utf8, &st))
450 return file_data_new(path_utf8, &st, TRUE);
453 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
455 sfd->parent = target;
456 if (!g_list_find(target->sidecar_files, sfd))
457 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
458 file_data_increment_version(sfd); /* increments both sfd and target */
463 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
467 file_data_add_sidecar_file(target, source);
469 work = source->sidecar_files;
472 FileData *sfd = work->data;
473 file_data_add_sidecar_file(target, sfd);
477 g_list_free(source->sidecar_files);
478 source->sidecar_files = NULL;
480 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
485 #ifdef DEBUG_FILEDATA
486 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
488 FileData *file_data_ref(FileData *fd)
491 if (fd == NULL) return NULL;
492 #ifdef DEBUG_FILEDATA
493 if (fd->magick != 0x12345678)
494 DEBUG_0("fd magick mismatch at %s:%d", file, line);
496 g_assert(fd->magick == 0x12345678);
499 #ifdef DEBUG_FILEDATA
500 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
502 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
507 static void file_data_free(FileData *fd)
509 g_assert(fd->magick == 0x12345678);
510 g_assert(fd->ref == 0);
512 g_hash_table_remove(file_data_pool, fd->original_path);
515 g_free(fd->original_path);
516 g_free(fd->collate_key_name);
517 g_free(fd->collate_key_name_nocase);
518 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
520 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
522 file_data_change_info_free(NULL, fd);
526 #ifdef DEBUG_FILEDATA
527 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
529 void file_data_unref(FileData *fd)
532 if (fd == NULL) return;
533 #ifdef DEBUG_FILEDATA
534 if (fd->magick != 0x12345678)
535 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
537 g_assert(fd->magick == 0x12345678);
540 #ifdef DEBUG_FILEDATA
541 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
544 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
549 FileData *parent = fd->parent ? fd->parent : fd;
554 work = parent->sidecar_files;
557 FileData *sfd = work->data;
563 /* none of parent/children is referenced, we can free everything */
565 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
567 work = parent->sidecar_files;
570 FileData *sfd = work->data;
575 g_list_free(parent->sidecar_files);
576 parent->sidecar_files = NULL;
578 file_data_free(parent);
582 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
584 sfd->parent = target;
585 g_assert(g_list_find(target->sidecar_files, sfd));
587 file_data_increment_version(sfd); /* increments both sfd and target */
589 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
601 /* disables / enables grouping for particular file, sends UPDATE notification */
602 void file_data_disable_grouping(FileData *fd, gboolean disable)
604 if (!fd->disable_grouping == !disable) return;
605 fd->disable_grouping = !!disable;
611 FileData *parent = file_data_ref(fd->parent);
612 file_data_disconnect_sidecar_file(parent, fd);
613 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
614 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
615 file_data_unref(parent);
617 else if (fd->sidecar_files)
619 GList *sidecar_files = filelist_copy(fd->sidecar_files);
620 GList *work = sidecar_files;
623 FileData *sfd = work->data;
625 file_data_disconnect_sidecar_file(fd, sfd);
626 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
628 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
629 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
630 filelist_free(sidecar_files);
635 file_data_check_sidecars(fd);
636 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
640 /* compare name without extension */
641 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
643 size_t len1 = fd1->extension - fd1->name;
644 size_t len2 = fd2->extension - fd2->name;
646 if (len1 < len2) return -1;
647 if (len1 > len2) return 1;
649 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
652 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
660 g_free(fdci->source);
669 static gboolean file_data_can_write_directly(FileData *fd)
671 return (filter_file_class(fd->extension, FORMAT_CLASS_IMAGE));
672 /* FIXME: detect what exiv2 really supports */
675 static gboolean file_data_can_write_sidecar(FileData *fd)
677 return (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE));
678 /* FIXME: detect what exiv2 really supports */
681 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
683 gchar *sidecar_path = NULL;
685 if (!file_data_can_write_sidecar(fd)) return NULL;
687 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
690 FileData *sfd = work->data;
692 if (strcasecmp(sfd->extension, ".xmp") == 0)
694 sidecar_path = g_strdup(sfd->path);
699 if (!existing_only && !sidecar_path)
701 gchar *base = remove_extension_from_path(fd->path);
702 sidecar_path = g_strconcat(base, ".xmp", NULL);
711 *-----------------------------------------------------------------------------
712 * sidecar file info struct
713 *-----------------------------------------------------------------------------
718 static gint sidecar_file_priority(const gchar *path)
720 const gchar *extension = extension_from_path(path);
724 if (extension == NULL)
727 work = sidecar_ext_get_list();
730 gchar *ext = work->data;
733 if (strcmp(extension, ext) == 0) return i;
741 *-----------------------------------------------------------------------------
743 *-----------------------------------------------------------------------------
746 static SortType filelist_sort_method = SORT_NONE;
747 static gint filelist_sort_ascend = TRUE;
750 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
752 if (!filelist_sort_ascend)
759 switch (filelist_sort_method)
764 if (fa->size < fb->size) return -1;
765 if (fa->size > fb->size) return 1;
766 /* fall back to name */
769 if (fa->date < fb->date) return -1;
770 if (fa->date > fb->date) return 1;
771 /* fall back to name */
773 #ifdef HAVE_STRVERSCMP
775 return strverscmp(fa->name, fb->name);
782 if (options->file_sort.case_sensitive)
783 return strcmp(fa->collate_key_name, fb->collate_key_name);
785 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
788 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
790 filelist_sort_method = method;
791 filelist_sort_ascend = ascend;
792 return filelist_sort_compare_filedata(fa, fb);
795 static gint filelist_sort_file_cb(gpointer a, gpointer b)
797 return filelist_sort_compare_filedata(a, b);
800 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
802 filelist_sort_method = method;
803 filelist_sort_ascend = ascend;
804 return g_list_sort(list, cb);
807 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
809 filelist_sort_method = method;
810 filelist_sort_ascend = ascend;
811 return g_list_insert_sorted(list, data, cb);
814 GList *filelist_sort(GList *list, SortType method, gint ascend)
816 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
819 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
821 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
825 static GList *filelist_filter_out_sidecars(GList *flist)
828 GList *flist_filtered = NULL;
832 FileData *fd = work->data;
835 if (fd->parent) /* remove fd's that are children */
838 flist_filtered = g_list_prepend(flist_filtered, fd);
842 return flist_filtered;
845 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
852 gint (*stat_func)(const gchar *path, struct stat *buf);
854 g_assert(files || dirs);
856 if (files) *files = NULL;
857 if (dirs) *dirs = NULL;
859 pathl = path_from_utf8(dir_fd->path);
860 if (!pathl) return FALSE;
874 while ((dir = readdir(dp)) != NULL)
876 struct stat ent_sbuf;
877 const gchar *name = dir->d_name;
880 if (!options->file_filter.show_hidden_files && ishidden(name))
883 filepath = g_build_filename(pathl, name, NULL);
884 if (stat_func(filepath, &ent_sbuf) >= 0)
886 if (S_ISDIR(ent_sbuf.st_mode))
888 /* we ignore the .thumbnails dir for cleanliness */
890 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
891 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
892 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
893 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
895 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
900 if (files && filter_name_exists(name))
902 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
913 if (dirs) *dirs = dlist;
914 if (files) *files = filelist_filter_out_sidecars(flist);
919 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
921 return filelist_read_real(dir_fd, files, dirs, TRUE);
924 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
926 return filelist_read_real(dir_fd, files, dirs, FALSE);
929 void filelist_free(GList *list)
936 file_data_unref((FileData *)work->data);
944 GList *filelist_copy(GList *list)
946 GList *new_list = NULL;
957 new_list = g_list_prepend(new_list, file_data_ref(fd));
960 return g_list_reverse(new_list);
963 GList *filelist_from_path_list(GList *list)
965 GList *new_list = NULL;
976 new_list = g_list_prepend(new_list, file_data_new_simple(path));
979 return g_list_reverse(new_list);
982 GList *filelist_to_path_list(GList *list)
984 GList *new_list = NULL;
995 new_list = g_list_prepend(new_list, g_strdup(fd->path));
998 return g_list_reverse(new_list);
1001 GList *filelist_filter(GList *list, gint is_dir_list)
1005 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1010 FileData *fd = (FileData *)(work->data);
1011 const gchar *name = fd->name;
1013 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1014 (!is_dir_list && !filter_name_exists(name)) ||
1015 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1016 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1020 list = g_list_remove_link(list, link);
1021 file_data_unref(fd);
1032 *-----------------------------------------------------------------------------
1033 * filelist recursive
1034 *-----------------------------------------------------------------------------
1037 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1039 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1042 GList *filelist_sort_path(GList *list)
1044 return g_list_sort(list, filelist_sort_path_cb);
1047 static void filelist_recursive_append(GList **list, GList *dirs)
1054 FileData *fd = (FileData *)(work->data);
1058 if (filelist_read(fd, &f, &d))
1060 f = filelist_filter(f, FALSE);
1061 f = filelist_sort_path(f);
1062 *list = g_list_concat(*list, f);
1064 d = filelist_filter(d, TRUE);
1065 d = filelist_sort_path(d);
1066 filelist_recursive_append(list, d);
1074 GList *filelist_recursive(FileData *dir_fd)
1079 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1080 list = filelist_filter(list, FALSE);
1081 list = filelist_sort_path(list);
1083 d = filelist_filter(d, TRUE);
1084 d = filelist_sort_path(d);
1085 filelist_recursive_append(&list, d);
1093 * marks and orientation
1096 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1097 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1098 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1100 gboolean file_data_get_mark(FileData *fd, gint n)
1102 gboolean valid = (fd->valid_marks & (1 << n));
1103 if (file_data_get_mark_func[n] && !valid)
1105 guint old = fd->marks;
1106 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1107 if (!value != !(fd->marks & (1 << n)))
1109 fd->marks = fd->marks ^ (1 << n);
1111 fd->valid_marks |= (1 << n);
1112 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1114 file_data_unref(fd);
1116 else if (!old && fd->marks)
1122 return !!(fd->marks & (1 << n));
1125 guint file_data_get_marks(FileData *fd)
1128 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1132 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1135 if (!value == !file_data_get_mark(fd, n)) return;
1137 if (file_data_set_mark_func[n])
1139 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1144 fd->marks = fd->marks ^ (1 << n);
1146 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1148 file_data_unref(fd);
1150 else if (!old && fd->marks)
1155 file_data_increment_version(fd);
1156 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1159 gboolean file_data_filter_marks(FileData *fd, guint filter)
1162 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1163 return ((fd->marks & filter) == filter);
1166 GList *file_data_filter_marks_list(GList *list, guint filter)
1173 FileData *fd = work->data;
1177 if (!file_data_filter_marks(fd, filter))
1179 list = g_list_remove_link(list, link);
1180 file_data_unref(fd);
1188 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1190 FileData *fd = value;
1191 file_data_increment_version(fd);
1192 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1195 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1197 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1199 file_data_get_mark_func[n] = get_mark_func;
1200 file_data_set_mark_func[n] = set_mark_func;
1201 file_data_mark_func_data[n] = data;
1205 /* this effectively changes all known files */
1206 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1212 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1214 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1215 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1216 if (data) *data = file_data_mark_func_data[n];
1219 gint file_data_get_user_orientation(FileData *fd)
1221 return fd->user_orientation;
1224 void file_data_set_user_orientation(FileData *fd, gint value)
1226 if (fd->user_orientation == value) return;
1228 fd->user_orientation = value;
1229 file_data_increment_version(fd);
1230 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1235 * file_data - operates on the given fd
1236 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1240 /* return list of sidecar file extensions in a string */
1241 gchar *file_data_sc_list_to_string(FileData *fd)
1244 GString *result = g_string_new("");
1246 work = fd->sidecar_files;
1249 FileData *sfd = work->data;
1251 result = g_string_append(result, "+ ");
1252 result = g_string_append(result, sfd->extension);
1254 if (work) result = g_string_append_c(result, ' ');
1257 return g_string_free(result, FALSE);
1263 * add FileDataChangeInfo (see typedefs.h) for the given operation
1264 * uses file_data_add_change_info
1266 * fails if the fd->change already exists - change operations can't run in parallel
1267 * fd->change_info works as a lock
1269 * dest can be NULL - in this case the current name is used for now, it will
1274 FileDataChangeInfo types:
1276 MOVE - path is changed, name may be changed too
1277 RENAME - path remains unchanged, name is changed
1278 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1279 sidecar names are changed too, extensions are not changed
1281 UPDATE - file size, date or grouping has been changed
1284 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1286 FileDataChangeInfo *fdci;
1288 if (fd->change) return FALSE;
1290 fdci = g_new0(FileDataChangeInfo, 1);
1295 fdci->source = g_strdup(src);
1297 fdci->source = g_strdup(fd->path);
1300 fdci->dest = g_strdup(dest);
1307 static void file_data_planned_change_remove(FileData *fd)
1309 if (file_data_planned_change_hash &&
1310 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1312 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1314 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1315 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1316 file_data_unref(fd);
1317 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1319 g_hash_table_destroy(file_data_planned_change_hash);
1320 file_data_planned_change_hash = NULL;
1321 DEBUG_1("planned change: empty");
1328 void file_data_free_ci(FileData *fd)
1330 FileDataChangeInfo *fdci = fd->change;
1335 file_data_planned_change_remove(fd);
1337 g_free(fdci->source);
1346 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1350 if (fd->parent) fd = fd->parent;
1352 if (fd->change) return FALSE;
1354 work = fd->sidecar_files;
1357 FileData *sfd = work->data;
1359 if (sfd->change) return FALSE;
1363 file_data_add_ci(fd, type, NULL, NULL);
1365 work = fd->sidecar_files;
1368 FileData *sfd = work->data;
1370 file_data_add_ci(sfd, type, NULL, NULL);
1377 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1381 if (fd->parent) fd = fd->parent;
1383 if (!fd->change || fd->change->type != type) return FALSE;
1385 work = fd->sidecar_files;
1388 FileData *sfd = work->data;
1390 if (!sfd->change || sfd->change->type != type) return FALSE;
1398 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1400 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1401 file_data_sc_update_ci_copy(fd, dest_path);
1405 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1407 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1408 file_data_sc_update_ci_move(fd, dest_path);
1412 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1414 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1415 file_data_sc_update_ci_rename(fd, dest_path);
1419 gboolean file_data_sc_add_ci_delete(FileData *fd)
1421 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1424 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1426 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1427 file_data_sc_update_ci_unspecified(fd, dest_path);
1431 gboolean file_data_add_ci_write_metadata(FileData *fd)
1433 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1436 void file_data_sc_free_ci(FileData *fd)
1440 if (fd->parent) fd = fd->parent;
1442 file_data_free_ci(fd);
1444 work = fd->sidecar_files;
1447 FileData *sfd = work->data;
1449 file_data_free_ci(sfd);
1454 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1457 gboolean ret = TRUE;
1462 FileData *fd = work->data;
1464 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1471 static void file_data_sc_revert_ci_list(GList *fd_list)
1478 FileData *fd = work->data;
1480 file_data_sc_free_ci(fd);
1485 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1492 FileData *fd = work->data;
1494 if (!func(fd, dest))
1496 file_data_sc_revert_ci_list(work->prev);
1505 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1507 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1510 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1512 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1515 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1517 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1520 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1522 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1525 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1528 gboolean ret = TRUE;
1533 FileData *fd = work->data;
1535 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1542 void file_data_free_ci_list(GList *fd_list)
1549 FileData *fd = work->data;
1551 file_data_free_ci(fd);
1556 void file_data_sc_free_ci_list(GList *fd_list)
1563 FileData *fd = work->data;
1565 file_data_sc_free_ci(fd);
1571 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1572 * fails if fd->change does not exist or the change type does not match
1575 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1577 FileDataChangeType type = fd->change->type;
1579 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1583 if (!file_data_planned_change_hash)
1584 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1586 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1588 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1589 g_hash_table_remove(file_data_planned_change_hash, old_path);
1590 file_data_unref(fd);
1593 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1598 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1599 g_hash_table_remove(file_data_planned_change_hash, new_path);
1600 file_data_unref(ofd);
1603 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1605 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1610 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1612 gchar *old_path = fd->change->dest;
1614 fd->change->dest = g_strdup(dest_path);
1615 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1619 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1621 const gchar *extension = extension_from_path(fd->change->source);
1622 gchar *base = remove_extension_from_path(dest_path);
1623 gchar *old_path = fd->change->dest;
1625 fd->change->dest = g_strconcat(base, extension, NULL);
1626 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1632 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1635 gchar *dest_path_full = NULL;
1637 if (fd->parent) fd = fd->parent;
1641 dest_path = fd->path;
1643 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1645 gchar *dir = remove_level_from_path(fd->path);
1647 dest_path_full = g_build_filename(dir, dest_path, NULL);
1649 dest_path = dest_path_full;
1651 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1653 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1654 dest_path = dest_path_full;
1657 file_data_update_ci_dest(fd, dest_path);
1659 work = fd->sidecar_files;
1662 FileData *sfd = work->data;
1664 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1668 g_free(dest_path_full);
1671 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1673 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1674 file_data_sc_update_ci(fd, dest_path);
1678 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1680 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1683 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1685 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1688 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1690 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1693 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1695 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1698 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1700 gboolean (*func)(FileData *, const gchar *))
1703 gboolean ret = TRUE;
1708 FileData *fd = work->data;
1710 if (!func(fd, dest)) ret = FALSE;
1717 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1719 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1722 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1724 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1727 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1729 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1734 * verify source and dest paths - dest image exists, etc.
1735 * it should detect all possible problems with the planned operation
1738 gint file_data_verify_ci(FileData *fd)
1740 gint ret = CHANGE_OK;
1745 DEBUG_1("Change checked: no change info: %s", fd->path);
1749 if (!isname(fd->path))
1751 /* this probably should not happen */
1752 ret |= CHANGE_NO_SRC;
1753 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1757 dir = remove_level_from_path(fd->path);
1759 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1760 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1761 !access_file(fd->path, R_OK))
1763 ret |= CHANGE_NO_READ_PERM;
1764 DEBUG_1("Change checked: no read permission: %s", fd->path);
1766 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1767 !access_file(dir, W_OK))
1769 ret |= CHANGE_NO_WRITE_PERM_DIR;
1770 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1772 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1773 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1774 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1775 !access_file(fd->path, W_OK))
1777 ret |= CHANGE_WARN_NO_WRITE_PERM;
1778 DEBUG_1("Change checked: no write permission: %s", fd->path);
1780 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1781 - that means that there are no hard errors and warnings can be disabled
1782 - the destination is determined during the check
1784 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1786 /* determine destination file */
1787 gboolean have_dest = FALSE;
1788 gchar *dest_dir = NULL;
1790 if (options->metadata.save_in_image_file)
1792 if (file_data_can_write_directly(fd))
1794 /* we can write the file directly */
1795 if (access_file(fd->path, W_OK))
1801 if (options->metadata.warn_on_write_problems)
1803 ret |= CHANGE_WARN_NO_WRITE_PERM;
1804 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1808 else if (file_data_can_write_sidecar(fd))
1810 /* we can write sidecar */
1811 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1812 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1814 file_data_update_ci_dest(fd, sidecar);
1819 if (options->metadata.warn_on_write_problems)
1821 ret |= CHANGE_WARN_NO_WRITE_PERM;
1822 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1831 /* write private metadata file under ~/.geeqie */
1833 /* If an existing metadata file exists, we will try writing to
1834 * it's location regardless of the user's preference.
1836 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1837 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1839 if (metadata_path && !access_file(metadata_path, W_OK))
1841 g_free(metadata_path);
1842 metadata_path = NULL;
1849 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1850 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1852 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1854 metadata_path = g_build_filename(dest_dir, filename, NULL);
1858 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1860 file_data_update_ci_dest(fd, metadata_path);
1865 ret |= CHANGE_NO_WRITE_PERM_DEST;
1866 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1868 g_free(metadata_path);
1873 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1878 same = (strcmp(fd->path, fd->change->dest) == 0);
1882 const gchar *dest_ext = extension_from_path(fd->change->dest);
1883 if (!dest_ext) dest_ext = "";
1885 if (strcasecmp(fd->extension, dest_ext) != 0)
1887 ret |= CHANGE_WARN_CHANGED_EXT;
1888 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1893 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1895 ret |= CHANGE_WARN_SAME;
1896 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1900 dest_dir = remove_level_from_path(fd->change->dest);
1902 if (!isdir(dest_dir))
1904 ret |= CHANGE_NO_DEST_DIR;
1905 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1907 else if (!access_file(dest_dir, W_OK))
1909 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1910 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1914 if (isfile(fd->change->dest))
1916 if (!access_file(fd->change->dest, W_OK))
1918 ret |= CHANGE_NO_WRITE_PERM_DEST;
1919 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1923 ret |= CHANGE_WARN_DEST_EXISTS;
1924 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1927 else if (isdir(fd->change->dest))
1929 ret |= CHANGE_DEST_EXISTS;
1930 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1937 fd->change->error = ret;
1938 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1945 gint file_data_sc_verify_ci(FileData *fd)
1950 ret = file_data_verify_ci(fd);
1952 work = fd->sidecar_files;
1955 FileData *sfd = work->data;
1957 ret |= file_data_verify_ci(sfd);
1964 gchar *file_data_get_error_string(gint error)
1966 GString *result = g_string_new("");
1968 if (error & CHANGE_NO_SRC)
1970 if (result->len > 0) g_string_append(result, ", ");
1971 g_string_append(result, _("file or directory does not exist"));
1974 if (error & CHANGE_DEST_EXISTS)
1976 if (result->len > 0) g_string_append(result, ", ");
1977 g_string_append(result, _("destination already exists"));
1980 if (error & CHANGE_NO_WRITE_PERM_DEST)
1982 if (result->len > 0) g_string_append(result, ", ");
1983 g_string_append(result, _("destination can't be overwritten"));
1986 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1988 if (result->len > 0) g_string_append(result, ", ");
1989 g_string_append(result, _("destination directory is not writable"));
1992 if (error & CHANGE_NO_DEST_DIR)
1994 if (result->len > 0) g_string_append(result, ", ");
1995 g_string_append(result, _("destination directory does not exist"));
1998 if (error & CHANGE_NO_WRITE_PERM_DIR)
2000 if (result->len > 0) g_string_append(result, ", ");
2001 g_string_append(result, _("source directory is not writable"));
2004 if (error & CHANGE_NO_READ_PERM)
2006 if (result->len > 0) g_string_append(result, ", ");
2007 g_string_append(result, _("no read permission"));
2010 if (error & CHANGE_WARN_NO_WRITE_PERM)
2012 if (result->len > 0) g_string_append(result, ", ");
2013 g_string_append(result, _("file is readonly"));
2016 if (error & CHANGE_WARN_DEST_EXISTS)
2018 if (result->len > 0) g_string_append(result, ", ");
2019 g_string_append(result, _("destination already exists and will be overwritten"));
2022 if (error & CHANGE_WARN_SAME)
2024 if (result->len > 0) g_string_append(result, ", ");
2025 g_string_append(result, _("source and destination are the same"));
2028 if (error & CHANGE_WARN_CHANGED_EXT)
2030 if (result->len > 0) g_string_append(result, ", ");
2031 g_string_append(result, _("source and destination have different extension"));
2034 return g_string_free(result, FALSE);
2037 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2040 gint all_errors = 0;
2041 gint common_errors = ~0;
2046 if (!list) return 0;
2048 num = g_list_length(list);
2049 errors = g_new(int, num);
2060 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2061 all_errors |= error;
2062 common_errors &= error;
2069 if (desc && all_errors)
2072 GString *result = g_string_new("");
2076 gchar *str = file_data_get_error_string(common_errors);
2077 g_string_append(result, str);
2078 g_string_append(result, "\n");
2092 error = errors[i] & ~common_errors;
2096 gchar *str = file_data_get_error_string(error);
2097 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2102 *desc = g_string_free(result, FALSE);
2111 * perform the change described by FileFataChangeInfo
2112 * it is used for internal operations,
2113 * this function actually operates with files on the filesystem
2114 * it should implement safe delete
2117 static gboolean file_data_perform_move(FileData *fd)
2119 g_assert(!strcmp(fd->change->source, fd->path));
2120 return move_file(fd->change->source, fd->change->dest);
2123 static gboolean file_data_perform_copy(FileData *fd)
2125 g_assert(!strcmp(fd->change->source, fd->path));
2126 return copy_file(fd->change->source, fd->change->dest);
2129 static gboolean file_data_perform_delete(FileData *fd)
2131 if (isdir(fd->path) && !islink(fd->path))
2132 return rmdir_utf8(fd->path);
2134 if (options->file_ops.safe_delete_enable)
2135 return file_util_safe_unlink(fd->path);
2137 return unlink_file(fd->path);
2140 gboolean file_data_perform_ci(FileData *fd)
2142 FileDataChangeType type = fd->change->type;
2145 case FILEDATA_CHANGE_MOVE:
2146 return file_data_perform_move(fd);
2147 case FILEDATA_CHANGE_COPY:
2148 return file_data_perform_copy(fd);
2149 case FILEDATA_CHANGE_RENAME:
2150 return file_data_perform_move(fd); /* the same as move */
2151 case FILEDATA_CHANGE_DELETE:
2152 return file_data_perform_delete(fd);
2153 case FILEDATA_CHANGE_WRITE_METADATA:
2154 return metadata_write_perform(fd);
2155 case FILEDATA_CHANGE_UNSPECIFIED:
2156 /* nothing to do here */
2164 gboolean file_data_sc_perform_ci(FileData *fd)
2167 gboolean ret = TRUE;
2168 FileDataChangeType type = fd->change->type;
2170 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2172 work = fd->sidecar_files;
2175 FileData *sfd = work->data;
2177 if (!file_data_perform_ci(sfd)) ret = FALSE;
2181 if (!file_data_perform_ci(fd)) ret = FALSE;
2187 * updates FileData structure according to FileDataChangeInfo
2190 gint file_data_apply_ci(FileData *fd)
2192 FileDataChangeType type = fd->change->type;
2195 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2197 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2198 file_data_planned_change_remove(fd);
2200 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2202 /* this change overwrites another file which is already known to other modules
2203 renaming fd would create duplicate FileData structure
2204 the best thing we can do is nothing
2205 FIXME: maybe we could copy stuff like marks
2207 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2211 file_data_set_path(fd, fd->change->dest);
2214 file_data_increment_version(fd);
2215 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2220 gint file_data_sc_apply_ci(FileData *fd)
2223 FileDataChangeType type = fd->change->type;
2225 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2227 work = fd->sidecar_files;
2230 FileData *sfd = work->data;
2232 file_data_apply_ci(sfd);
2236 file_data_apply_ci(fd);
2242 * notify other modules about the change described by FileFataChangeInfo
2245 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2246 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2247 implementation in view_file_list.c */
2252 typedef struct _NotifyData NotifyData;
2254 struct _NotifyData {
2255 FileDataNotifyFunc func;
2257 NotifyPriority priority;
2260 static GList *notify_func_list = NULL;
2262 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2264 NotifyData *nda = (NotifyData *)a;
2265 NotifyData *ndb = (NotifyData *)b;
2267 if (nda->priority < ndb->priority) return -1;
2268 if (nda->priority > ndb->priority) return 1;
2272 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2276 nd = g_new(NotifyData, 1);
2279 nd->priority = priority;
2281 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2282 DEBUG_1("Notify func registered: %p", nd);
2287 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2289 GList *work = notify_func_list;
2293 NotifyData *nd = (NotifyData *)work->data;
2295 if (nd->func == func && nd->data == data)
2297 notify_func_list = g_list_delete_link(notify_func_list, work);
2299 DEBUG_1("Notify func unregistered: %p", nd);
2309 void file_data_send_notification(FileData *fd, NotifyType type)
2311 GList *work = notify_func_list;
2315 NotifyData *nd = (NotifyData *)work->data;
2317 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2318 nd->func(fd, type, nd->data);
2323 static GHashTable *file_data_monitor_pool = NULL;
2324 static gint realtime_monitor_id = -1;
2326 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2330 file_data_check_changed_files(fd);
2332 DEBUG_1("monitor %s", fd->path);
2335 static gboolean realtime_monitor_cb(gpointer data)
2337 if (!options->update_on_time_change) return TRUE;
2338 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2342 gint file_data_register_real_time_monitor(FileData *fd)
2348 if (!file_data_monitor_pool)
2349 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2351 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2353 DEBUG_1("Register realtime %d %s", count, fd->path);
2356 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2358 if (realtime_monitor_id == -1)
2360 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2366 gint file_data_unregister_real_time_monitor(FileData *fd)
2370 g_assert(file_data_monitor_pool);
2372 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2374 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2376 g_assert(count > 0);
2381 g_hash_table_remove(file_data_monitor_pool, fd);
2383 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2385 file_data_unref(fd);
2387 if (g_hash_table_size(file_data_monitor_pool) == 0)
2389 g_source_remove(realtime_monitor_id);
2390 realtime_monitor_id = -1;
2396 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */