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 (strcasecmp(ext, fd->extension) == 0)
400 new_fd = fd; /* processing the original file */
405 g_string_truncate(fname, base_len);
407 if (!stat_utf8_case_insensitive_ext(fname, ext, &nst))
410 new_fd = file_data_new(fname->str, &nst, FALSE);
412 if (new_fd->disable_grouping)
414 file_data_unref(new_fd);
418 new_fd->ref--; /* do not use ref here */
422 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
424 file_data_merge_sidecar_files(parent_fd, new_fd);
426 g_string_free(fname, TRUE);
430 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
432 gchar *path_utf8 = path_to_utf8(path);
433 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
439 FileData *file_data_new_simple(const gchar *path_utf8)
443 if (!stat_utf8(path_utf8, &st))
449 return file_data_new(path_utf8, &st, TRUE);
452 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
454 sfd->parent = target;
455 if (!g_list_find(target->sidecar_files, sfd))
456 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
457 file_data_increment_version(sfd); /* increments both sfd and target */
462 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
466 file_data_add_sidecar_file(target, source);
468 work = source->sidecar_files;
471 FileData *sfd = work->data;
472 file_data_add_sidecar_file(target, sfd);
476 g_list_free(source->sidecar_files);
477 source->sidecar_files = NULL;
479 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
484 #ifdef DEBUG_FILEDATA
485 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
487 FileData *file_data_ref(FileData *fd)
490 if (fd == NULL) return NULL;
491 #ifdef DEBUG_FILEDATA
492 if (fd->magick != 0x12345678)
493 DEBUG_0("fd magick mismatch at %s:%d", file, line);
495 g_assert(fd->magick == 0x12345678);
498 #ifdef DEBUG_FILEDATA
499 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
501 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
506 static void file_data_free(FileData *fd)
508 g_assert(fd->magick == 0x12345678);
509 g_assert(fd->ref == 0);
511 g_hash_table_remove(file_data_pool, fd->original_path);
514 g_free(fd->original_path);
515 g_free(fd->collate_key_name);
516 g_free(fd->collate_key_name_nocase);
517 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
519 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
521 file_data_change_info_free(NULL, fd);
525 #ifdef DEBUG_FILEDATA
526 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
528 void file_data_unref(FileData *fd)
531 if (fd == NULL) return;
532 #ifdef DEBUG_FILEDATA
533 if (fd->magick != 0x12345678)
534 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
536 g_assert(fd->magick == 0x12345678);
539 #ifdef DEBUG_FILEDATA
540 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
543 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
548 FileData *parent = fd->parent ? fd->parent : fd;
553 work = parent->sidecar_files;
556 FileData *sfd = work->data;
562 /* none of parent/children is referenced, we can free everything */
564 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
566 work = parent->sidecar_files;
569 FileData *sfd = work->data;
574 g_list_free(parent->sidecar_files);
575 parent->sidecar_files = NULL;
577 file_data_free(parent);
581 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
583 sfd->parent = target;
584 g_assert(g_list_find(target->sidecar_files, sfd));
586 file_data_increment_version(sfd); /* increments both sfd and target */
588 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
600 /* disables / enables grouping for particular file, sends UPDATE notification */
601 void file_data_disable_grouping(FileData *fd, gboolean disable)
603 if (!fd->disable_grouping == !disable) return;
604 fd->disable_grouping = !!disable;
610 FileData *parent = file_data_ref(fd->parent);
611 file_data_disconnect_sidecar_file(parent, fd);
612 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
613 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
614 file_data_unref(parent);
616 else if (fd->sidecar_files)
618 GList *sidecar_files = filelist_copy(fd->sidecar_files);
619 GList *work = sidecar_files;
622 FileData *sfd = work->data;
624 file_data_disconnect_sidecar_file(fd, sfd);
625 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
627 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
628 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
629 filelist_free(sidecar_files);
634 file_data_check_sidecars(fd);
635 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
639 /* compare name without extension */
640 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
642 size_t len1 = fd1->extension - fd1->name;
643 size_t len2 = fd2->extension - fd2->name;
645 if (len1 < len2) return -1;
646 if (len1 > len2) return 1;
648 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
651 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
659 g_free(fdci->source);
668 static gboolean file_data_can_write_directly(FileData *fd)
670 return (filter_file_class(fd->extension, FORMAT_CLASS_IMAGE));
671 /* FIXME: detect what exiv2 really supports */
674 static gboolean file_data_can_write_sidecar(FileData *fd)
676 return (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE));
677 /* FIXME: detect what exiv2 really supports */
680 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
682 gchar *sidecar_path = NULL;
684 if (!file_data_can_write_sidecar(fd)) return NULL;
686 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
689 FileData *sfd = work->data;
691 if (strcasecmp(sfd->extension, ".xmp") == 0)
693 sidecar_path = g_strdup(sfd->path);
698 if (!existing_only && !sidecar_path)
700 gchar *base = remove_extension_from_path(fd->path);
701 sidecar_path = g_strconcat(base, ".xmp", NULL);
710 *-----------------------------------------------------------------------------
711 * sidecar file info struct
712 *-----------------------------------------------------------------------------
717 static gint sidecar_file_priority(const gchar *path)
719 const gchar *extension = extension_from_path(path);
723 if (extension == NULL)
726 work = sidecar_ext_get_list();
729 gchar *ext = work->data;
732 if (strcasecmp(extension, ext) == 0) return i;
740 *-----------------------------------------------------------------------------
742 *-----------------------------------------------------------------------------
745 static SortType filelist_sort_method = SORT_NONE;
746 static gint filelist_sort_ascend = TRUE;
749 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
751 if (!filelist_sort_ascend)
758 switch (filelist_sort_method)
763 if (fa->size < fb->size) return -1;
764 if (fa->size > fb->size) return 1;
765 /* fall back to name */
768 if (fa->date < fb->date) return -1;
769 if (fa->date > fb->date) return 1;
770 /* fall back to name */
772 #ifdef HAVE_STRVERSCMP
774 return strverscmp(fa->name, fb->name);
781 if (options->file_sort.case_sensitive)
782 return strcmp(fa->collate_key_name, fb->collate_key_name);
784 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
787 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
789 filelist_sort_method = method;
790 filelist_sort_ascend = ascend;
791 return filelist_sort_compare_filedata(fa, fb);
794 static gint filelist_sort_file_cb(gpointer a, gpointer b)
796 return filelist_sort_compare_filedata(a, b);
799 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
801 filelist_sort_method = method;
802 filelist_sort_ascend = ascend;
803 return g_list_sort(list, cb);
806 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
808 filelist_sort_method = method;
809 filelist_sort_ascend = ascend;
810 return g_list_insert_sorted(list, data, cb);
813 GList *filelist_sort(GList *list, SortType method, gint ascend)
815 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
818 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
820 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
824 static GList *filelist_filter_out_sidecars(GList *flist)
827 GList *flist_filtered = NULL;
831 FileData *fd = work->data;
834 if (fd->parent) /* remove fd's that are children */
837 flist_filtered = g_list_prepend(flist_filtered, fd);
841 return flist_filtered;
844 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
851 gint (*stat_func)(const gchar *path, struct stat *buf);
853 g_assert(files || dirs);
855 if (files) *files = NULL;
856 if (dirs) *dirs = NULL;
858 pathl = path_from_utf8(dir_fd->path);
859 if (!pathl) return FALSE;
873 while ((dir = readdir(dp)) != NULL)
875 struct stat ent_sbuf;
876 const gchar *name = dir->d_name;
879 if (!options->file_filter.show_hidden_files && ishidden(name))
882 filepath = g_build_filename(pathl, name, NULL);
883 if (stat_func(filepath, &ent_sbuf) >= 0)
885 if (S_ISDIR(ent_sbuf.st_mode))
887 /* we ignore the .thumbnails dir for cleanliness */
889 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
890 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
891 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
892 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
894 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
899 if (files && filter_name_exists(name))
901 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
912 if (dirs) *dirs = dlist;
913 if (files) *files = filelist_filter_out_sidecars(flist);
918 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
920 return filelist_read_real(dir_fd, files, dirs, TRUE);
923 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
925 return filelist_read_real(dir_fd, files, dirs, FALSE);
928 void filelist_free(GList *list)
935 file_data_unref((FileData *)work->data);
943 GList *filelist_copy(GList *list)
945 GList *new_list = NULL;
956 new_list = g_list_prepend(new_list, file_data_ref(fd));
959 return g_list_reverse(new_list);
962 GList *filelist_from_path_list(GList *list)
964 GList *new_list = NULL;
975 new_list = g_list_prepend(new_list, file_data_new_simple(path));
978 return g_list_reverse(new_list);
981 GList *filelist_to_path_list(GList *list)
983 GList *new_list = NULL;
994 new_list = g_list_prepend(new_list, g_strdup(fd->path));
997 return g_list_reverse(new_list);
1000 GList *filelist_filter(GList *list, gint is_dir_list)
1004 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1009 FileData *fd = (FileData *)(work->data);
1010 const gchar *name = fd->name;
1012 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1013 (!is_dir_list && !filter_name_exists(name)) ||
1014 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1015 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1019 list = g_list_remove_link(list, link);
1020 file_data_unref(fd);
1031 *-----------------------------------------------------------------------------
1032 * filelist recursive
1033 *-----------------------------------------------------------------------------
1036 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1038 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1041 GList *filelist_sort_path(GList *list)
1043 return g_list_sort(list, filelist_sort_path_cb);
1046 static void filelist_recursive_append(GList **list, GList *dirs)
1053 FileData *fd = (FileData *)(work->data);
1057 if (filelist_read(fd, &f, &d))
1059 f = filelist_filter(f, FALSE);
1060 f = filelist_sort_path(f);
1061 *list = g_list_concat(*list, f);
1063 d = filelist_filter(d, TRUE);
1064 d = filelist_sort_path(d);
1065 filelist_recursive_append(list, d);
1073 GList *filelist_recursive(FileData *dir_fd)
1078 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1079 list = filelist_filter(list, FALSE);
1080 list = filelist_sort_path(list);
1082 d = filelist_filter(d, TRUE);
1083 d = filelist_sort_path(d);
1084 filelist_recursive_append(&list, d);
1092 * marks and orientation
1095 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1096 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1097 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1099 gboolean file_data_get_mark(FileData *fd, gint n)
1101 gboolean valid = (fd->valid_marks & (1 << n));
1102 if (file_data_get_mark_func[n] && !valid)
1104 guint old = fd->marks;
1105 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1106 if (!value != !(fd->marks & (1 << n)))
1108 fd->marks = fd->marks ^ (1 << n);
1110 fd->valid_marks |= (1 << n);
1111 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1113 file_data_unref(fd);
1115 else if (!old && fd->marks)
1121 return !!(fd->marks & (1 << n));
1124 guint file_data_get_marks(FileData *fd)
1127 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1131 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1134 if (!value == !file_data_get_mark(fd, n)) return;
1136 if (file_data_set_mark_func[n])
1138 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1143 fd->marks = fd->marks ^ (1 << n);
1145 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1147 file_data_unref(fd);
1149 else if (!old && fd->marks)
1154 file_data_increment_version(fd);
1155 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1158 gboolean file_data_filter_marks(FileData *fd, guint filter)
1161 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1162 return ((fd->marks & filter) == filter);
1165 GList *file_data_filter_marks_list(GList *list, guint filter)
1172 FileData *fd = work->data;
1176 if (!file_data_filter_marks(fd, filter))
1178 list = g_list_remove_link(list, link);
1179 file_data_unref(fd);
1187 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1189 FileData *fd = value;
1190 file_data_increment_version(fd);
1191 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1194 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1196 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1198 file_data_get_mark_func[n] = get_mark_func;
1199 file_data_set_mark_func[n] = set_mark_func;
1200 file_data_mark_func_data[n] = data;
1204 /* this effectively changes all known files */
1205 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1211 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1213 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1214 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1215 if (data) *data = file_data_mark_func_data[n];
1218 gint file_data_get_user_orientation(FileData *fd)
1220 return fd->user_orientation;
1223 void file_data_set_user_orientation(FileData *fd, gint value)
1225 if (fd->user_orientation == value) return;
1227 fd->user_orientation = value;
1228 file_data_increment_version(fd);
1229 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1234 * file_data - operates on the given fd
1235 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1239 /* return list of sidecar file extensions in a string */
1240 gchar *file_data_sc_list_to_string(FileData *fd)
1243 GString *result = g_string_new("");
1245 work = fd->sidecar_files;
1248 FileData *sfd = work->data;
1250 result = g_string_append(result, "+ ");
1251 result = g_string_append(result, sfd->extension);
1253 if (work) result = g_string_append_c(result, ' ');
1256 return g_string_free(result, FALSE);
1262 * add FileDataChangeInfo (see typedefs.h) for the given operation
1263 * uses file_data_add_change_info
1265 * fails if the fd->change already exists - change operations can't run in parallel
1266 * fd->change_info works as a lock
1268 * dest can be NULL - in this case the current name is used for now, it will
1273 FileDataChangeInfo types:
1275 MOVE - path is changed, name may be changed too
1276 RENAME - path remains unchanged, name is changed
1277 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1278 sidecar names are changed too, extensions are not changed
1280 UPDATE - file size, date or grouping has been changed
1283 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1285 FileDataChangeInfo *fdci;
1287 if (fd->change) return FALSE;
1289 fdci = g_new0(FileDataChangeInfo, 1);
1294 fdci->source = g_strdup(src);
1296 fdci->source = g_strdup(fd->path);
1299 fdci->dest = g_strdup(dest);
1306 static void file_data_planned_change_remove(FileData *fd)
1308 if (file_data_planned_change_hash &&
1309 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1311 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1313 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1314 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1315 file_data_unref(fd);
1316 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1318 g_hash_table_destroy(file_data_planned_change_hash);
1319 file_data_planned_change_hash = NULL;
1320 DEBUG_1("planned change: empty");
1327 void file_data_free_ci(FileData *fd)
1329 FileDataChangeInfo *fdci = fd->change;
1334 file_data_planned_change_remove(fd);
1336 g_free(fdci->source);
1345 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1349 if (fd->parent) fd = fd->parent;
1351 if (fd->change) return FALSE;
1353 work = fd->sidecar_files;
1356 FileData *sfd = work->data;
1358 if (sfd->change) return FALSE;
1362 file_data_add_ci(fd, type, NULL, NULL);
1364 work = fd->sidecar_files;
1367 FileData *sfd = work->data;
1369 file_data_add_ci(sfd, type, NULL, NULL);
1376 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1380 if (fd->parent) fd = fd->parent;
1382 if (!fd->change || fd->change->type != type) return FALSE;
1384 work = fd->sidecar_files;
1387 FileData *sfd = work->data;
1389 if (!sfd->change || sfd->change->type != type) return FALSE;
1397 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1399 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1400 file_data_sc_update_ci_copy(fd, dest_path);
1404 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1406 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1407 file_data_sc_update_ci_move(fd, dest_path);
1411 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1413 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1414 file_data_sc_update_ci_rename(fd, dest_path);
1418 gboolean file_data_sc_add_ci_delete(FileData *fd)
1420 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1423 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1425 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1426 file_data_sc_update_ci_unspecified(fd, dest_path);
1430 gboolean file_data_add_ci_write_metadata(FileData *fd)
1432 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1435 void file_data_sc_free_ci(FileData *fd)
1439 if (fd->parent) fd = fd->parent;
1441 file_data_free_ci(fd);
1443 work = fd->sidecar_files;
1446 FileData *sfd = work->data;
1448 file_data_free_ci(sfd);
1453 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1456 gboolean ret = TRUE;
1461 FileData *fd = work->data;
1463 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1470 static void file_data_sc_revert_ci_list(GList *fd_list)
1477 FileData *fd = work->data;
1479 file_data_sc_free_ci(fd);
1484 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1491 FileData *fd = work->data;
1493 if (!func(fd, dest))
1495 file_data_sc_revert_ci_list(work->prev);
1504 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1506 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1509 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1511 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1514 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1516 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1519 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1521 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1524 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1527 gboolean ret = TRUE;
1532 FileData *fd = work->data;
1534 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1541 void file_data_free_ci_list(GList *fd_list)
1548 FileData *fd = work->data;
1550 file_data_free_ci(fd);
1555 void file_data_sc_free_ci_list(GList *fd_list)
1562 FileData *fd = work->data;
1564 file_data_sc_free_ci(fd);
1570 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1571 * fails if fd->change does not exist or the change type does not match
1574 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1576 FileDataChangeType type = fd->change->type;
1578 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1582 if (!file_data_planned_change_hash)
1583 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1585 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1587 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1588 g_hash_table_remove(file_data_planned_change_hash, old_path);
1589 file_data_unref(fd);
1592 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1597 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1598 g_hash_table_remove(file_data_planned_change_hash, new_path);
1599 file_data_unref(ofd);
1602 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1604 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1609 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1611 gchar *old_path = fd->change->dest;
1613 fd->change->dest = g_strdup(dest_path);
1614 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1618 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1620 const gchar *extension = extension_from_path(fd->change->source);
1621 gchar *base = remove_extension_from_path(dest_path);
1622 gchar *old_path = fd->change->dest;
1624 fd->change->dest = g_strconcat(base, extension, NULL);
1625 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1631 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1634 gchar *dest_path_full = NULL;
1636 if (fd->parent) fd = fd->parent;
1640 dest_path = fd->path;
1642 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1644 gchar *dir = remove_level_from_path(fd->path);
1646 dest_path_full = g_build_filename(dir, dest_path, NULL);
1648 dest_path = dest_path_full;
1650 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1652 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1653 dest_path = dest_path_full;
1656 file_data_update_ci_dest(fd, dest_path);
1658 work = fd->sidecar_files;
1661 FileData *sfd = work->data;
1663 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1667 g_free(dest_path_full);
1670 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1672 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1673 file_data_sc_update_ci(fd, dest_path);
1677 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1679 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1682 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1684 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1687 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1689 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1692 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1694 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1697 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1699 gboolean (*func)(FileData *, const gchar *))
1702 gboolean ret = TRUE;
1707 FileData *fd = work->data;
1709 if (!func(fd, dest)) ret = FALSE;
1716 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1718 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1721 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1723 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1726 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1728 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1733 * verify source and dest paths - dest image exists, etc.
1734 * it should detect all possible problems with the planned operation
1737 gint file_data_verify_ci(FileData *fd)
1739 gint ret = CHANGE_OK;
1744 DEBUG_1("Change checked: no change info: %s", fd->path);
1748 if (!isname(fd->path))
1750 /* this probably should not happen */
1751 ret |= CHANGE_NO_SRC;
1752 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1756 dir = remove_level_from_path(fd->path);
1758 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1759 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1760 !access_file(fd->path, R_OK))
1762 ret |= CHANGE_NO_READ_PERM;
1763 DEBUG_1("Change checked: no read permission: %s", fd->path);
1765 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1766 !access_file(dir, W_OK))
1768 ret |= CHANGE_NO_WRITE_PERM_DIR;
1769 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1771 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1772 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1773 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1774 !access_file(fd->path, W_OK))
1776 ret |= CHANGE_WARN_NO_WRITE_PERM;
1777 DEBUG_1("Change checked: no write permission: %s", fd->path);
1779 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1780 - that means that there are no hard errors and warnings can be disabled
1781 - the destination is determined during the check
1783 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1785 /* determine destination file */
1786 gboolean have_dest = FALSE;
1787 gchar *dest_dir = NULL;
1789 if (options->metadata.save_in_image_file)
1791 if (file_data_can_write_directly(fd))
1793 /* we can write the file directly */
1794 if (access_file(fd->path, W_OK))
1800 if (options->metadata.warn_on_write_problems)
1802 ret |= CHANGE_WARN_NO_WRITE_PERM;
1803 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1807 else if (file_data_can_write_sidecar(fd))
1809 /* we can write sidecar */
1810 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1811 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1813 file_data_update_ci_dest(fd, sidecar);
1818 if (options->metadata.warn_on_write_problems)
1820 ret |= CHANGE_WARN_NO_WRITE_PERM;
1821 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1830 /* write private metadata file under ~/.geeqie */
1832 /* If an existing metadata file exists, we will try writing to
1833 * it's location regardless of the user's preference.
1835 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1836 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1838 if (metadata_path && !access_file(metadata_path, W_OK))
1840 g_free(metadata_path);
1841 metadata_path = NULL;
1848 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1849 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1851 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1853 metadata_path = g_build_filename(dest_dir, filename, NULL);
1857 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1859 file_data_update_ci_dest(fd, metadata_path);
1864 ret |= CHANGE_NO_WRITE_PERM_DEST;
1865 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1867 g_free(metadata_path);
1872 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1877 same = (strcmp(fd->path, fd->change->dest) == 0);
1881 const gchar *dest_ext = extension_from_path(fd->change->dest);
1882 if (!dest_ext) dest_ext = "";
1884 if (strcasecmp(fd->extension, dest_ext) != 0)
1886 ret |= CHANGE_WARN_CHANGED_EXT;
1887 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1892 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1894 ret |= CHANGE_WARN_SAME;
1895 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1899 dest_dir = remove_level_from_path(fd->change->dest);
1901 if (!isdir(dest_dir))
1903 ret |= CHANGE_NO_DEST_DIR;
1904 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1906 else if (!access_file(dest_dir, W_OK))
1908 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1909 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1913 if (isfile(fd->change->dest))
1915 if (!access_file(fd->change->dest, W_OK))
1917 ret |= CHANGE_NO_WRITE_PERM_DEST;
1918 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1922 ret |= CHANGE_WARN_DEST_EXISTS;
1923 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1926 else if (isdir(fd->change->dest))
1928 ret |= CHANGE_DEST_EXISTS;
1929 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1936 fd->change->error = ret;
1937 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1944 gint file_data_sc_verify_ci(FileData *fd)
1949 ret = file_data_verify_ci(fd);
1951 work = fd->sidecar_files;
1954 FileData *sfd = work->data;
1956 ret |= file_data_verify_ci(sfd);
1963 gchar *file_data_get_error_string(gint error)
1965 GString *result = g_string_new("");
1967 if (error & CHANGE_NO_SRC)
1969 if (result->len > 0) g_string_append(result, ", ");
1970 g_string_append(result, _("file or directory does not exist"));
1973 if (error & CHANGE_DEST_EXISTS)
1975 if (result->len > 0) g_string_append(result, ", ");
1976 g_string_append(result, _("destination already exists"));
1979 if (error & CHANGE_NO_WRITE_PERM_DEST)
1981 if (result->len > 0) g_string_append(result, ", ");
1982 g_string_append(result, _("destination can't be overwritten"));
1985 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1987 if (result->len > 0) g_string_append(result, ", ");
1988 g_string_append(result, _("destination directory is not writable"));
1991 if (error & CHANGE_NO_DEST_DIR)
1993 if (result->len > 0) g_string_append(result, ", ");
1994 g_string_append(result, _("destination directory does not exist"));
1997 if (error & CHANGE_NO_WRITE_PERM_DIR)
1999 if (result->len > 0) g_string_append(result, ", ");
2000 g_string_append(result, _("source directory is not writable"));
2003 if (error & CHANGE_NO_READ_PERM)
2005 if (result->len > 0) g_string_append(result, ", ");
2006 g_string_append(result, _("no read permission"));
2009 if (error & CHANGE_WARN_NO_WRITE_PERM)
2011 if (result->len > 0) g_string_append(result, ", ");
2012 g_string_append(result, _("file is readonly"));
2015 if (error & CHANGE_WARN_DEST_EXISTS)
2017 if (result->len > 0) g_string_append(result, ", ");
2018 g_string_append(result, _("destination already exists and will be overwritten"));
2021 if (error & CHANGE_WARN_SAME)
2023 if (result->len > 0) g_string_append(result, ", ");
2024 g_string_append(result, _("source and destination are the same"));
2027 if (error & CHANGE_WARN_CHANGED_EXT)
2029 if (result->len > 0) g_string_append(result, ", ");
2030 g_string_append(result, _("source and destination have different extension"));
2033 return g_string_free(result, FALSE);
2036 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2039 gint all_errors = 0;
2040 gint common_errors = ~0;
2045 if (!list) return 0;
2047 num = g_list_length(list);
2048 errors = g_new(int, num);
2059 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2060 all_errors |= error;
2061 common_errors &= error;
2068 if (desc && all_errors)
2071 GString *result = g_string_new("");
2075 gchar *str = file_data_get_error_string(common_errors);
2076 g_string_append(result, str);
2077 g_string_append(result, "\n");
2091 error = errors[i] & ~common_errors;
2095 gchar *str = file_data_get_error_string(error);
2096 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2101 *desc = g_string_free(result, FALSE);
2110 * perform the change described by FileFataChangeInfo
2111 * it is used for internal operations,
2112 * this function actually operates with files on the filesystem
2113 * it should implement safe delete
2116 static gboolean file_data_perform_move(FileData *fd)
2118 g_assert(!strcmp(fd->change->source, fd->path));
2119 return move_file(fd->change->source, fd->change->dest);
2122 static gboolean file_data_perform_copy(FileData *fd)
2124 g_assert(!strcmp(fd->change->source, fd->path));
2125 return copy_file(fd->change->source, fd->change->dest);
2128 static gboolean file_data_perform_delete(FileData *fd)
2130 if (isdir(fd->path) && !islink(fd->path))
2131 return rmdir_utf8(fd->path);
2133 if (options->file_ops.safe_delete_enable)
2134 return file_util_safe_unlink(fd->path);
2136 return unlink_file(fd->path);
2139 gboolean file_data_perform_ci(FileData *fd)
2141 FileDataChangeType type = fd->change->type;
2144 case FILEDATA_CHANGE_MOVE:
2145 return file_data_perform_move(fd);
2146 case FILEDATA_CHANGE_COPY:
2147 return file_data_perform_copy(fd);
2148 case FILEDATA_CHANGE_RENAME:
2149 return file_data_perform_move(fd); /* the same as move */
2150 case FILEDATA_CHANGE_DELETE:
2151 return file_data_perform_delete(fd);
2152 case FILEDATA_CHANGE_WRITE_METADATA:
2153 return metadata_write_perform(fd);
2154 case FILEDATA_CHANGE_UNSPECIFIED:
2155 /* nothing to do here */
2163 gboolean file_data_sc_perform_ci(FileData *fd)
2166 gboolean ret = TRUE;
2167 FileDataChangeType type = fd->change->type;
2169 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2171 work = fd->sidecar_files;
2174 FileData *sfd = work->data;
2176 if (!file_data_perform_ci(sfd)) ret = FALSE;
2180 if (!file_data_perform_ci(fd)) ret = FALSE;
2186 * updates FileData structure according to FileDataChangeInfo
2189 gint file_data_apply_ci(FileData *fd)
2191 FileDataChangeType type = fd->change->type;
2194 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2196 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2197 file_data_planned_change_remove(fd);
2199 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2201 /* this change overwrites another file which is already known to other modules
2202 renaming fd would create duplicate FileData structure
2203 the best thing we can do is nothing
2204 FIXME: maybe we could copy stuff like marks
2206 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2210 file_data_set_path(fd, fd->change->dest);
2213 file_data_increment_version(fd);
2214 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2219 gint file_data_sc_apply_ci(FileData *fd)
2222 FileDataChangeType type = fd->change->type;
2224 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2226 work = fd->sidecar_files;
2229 FileData *sfd = work->data;
2231 file_data_apply_ci(sfd);
2235 file_data_apply_ci(fd);
2241 * notify other modules about the change described by FileFataChangeInfo
2244 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2245 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2246 implementation in view_file_list.c */
2251 typedef struct _NotifyData NotifyData;
2253 struct _NotifyData {
2254 FileDataNotifyFunc func;
2256 NotifyPriority priority;
2259 static GList *notify_func_list = NULL;
2261 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2263 NotifyData *nda = (NotifyData *)a;
2264 NotifyData *ndb = (NotifyData *)b;
2266 if (nda->priority < ndb->priority) return -1;
2267 if (nda->priority > ndb->priority) return 1;
2271 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2275 nd = g_new(NotifyData, 1);
2278 nd->priority = priority;
2280 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2281 DEBUG_1("Notify func registered: %p", nd);
2286 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2288 GList *work = notify_func_list;
2292 NotifyData *nd = (NotifyData *)work->data;
2294 if (nd->func == func && nd->data == data)
2296 notify_func_list = g_list_delete_link(notify_func_list, work);
2298 DEBUG_1("Notify func unregistered: %p", nd);
2308 void file_data_send_notification(FileData *fd, NotifyType type)
2310 GList *work = notify_func_list;
2314 NotifyData *nd = (NotifyData *)work->data;
2316 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2317 nd->func(fd, type, nd->data);
2322 static GHashTable *file_data_monitor_pool = NULL;
2323 static gint realtime_monitor_id = -1;
2325 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2329 file_data_check_changed_files(fd);
2331 DEBUG_1("monitor %s", fd->path);
2334 static gboolean realtime_monitor_cb(gpointer data)
2336 if (!options->update_on_time_change) return TRUE;
2337 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2341 gint file_data_register_real_time_monitor(FileData *fd)
2347 if (!file_data_monitor_pool)
2348 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2350 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2352 DEBUG_1("Register realtime %d %s", count, fd->path);
2355 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2357 if (realtime_monitor_id == -1)
2359 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2365 gint file_data_unregister_real_time_monitor(FileData *fd)
2369 g_assert(file_data_monitor_pool);
2371 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2373 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2375 g_assert(count > 0);
2380 g_hash_table_remove(file_data_monitor_pool, fd);
2382 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2384 file_data_unref(fd);
2386 if (g_hash_table_size(file_data_monitor_pool) == 0)
2388 g_source_remove(realtime_monitor_id);
2389 realtime_monitor_id = -1;
2395 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */