4 * Copyright (C) 2008 - 2009 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"
23 #include "histogram.h"
26 static GHashTable *file_data_pool = NULL;
27 static GHashTable *file_data_planned_change_hash = NULL;
29 static gint sidecar_file_priority(const gchar *path);
33 *-----------------------------------------------------------------------------
34 * text conversion utils
35 *-----------------------------------------------------------------------------
38 gchar *text_from_size(gint64 size)
44 /* what I would like to use is printf("%'d", size)
45 * BUT: not supported on every libc :(
49 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
50 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
54 a = g_strdup_printf("%d", (guint)size);
60 b = g_new(gchar, l + n + 1);
85 gchar *text_from_size_abrev(gint64 size)
87 if (size < (gint64)1024)
89 return g_strdup_printf(_("%d bytes"), (gint)size);
91 if (size < (gint64)1048576)
93 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
95 if (size < (gint64)1073741824)
97 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
100 /* to avoid overflowing the gdouble, do division in two steps */
102 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
105 /* note: returned string is valid until next call to text_from_time() */
106 const gchar *text_from_time(time_t t)
108 static gchar *ret = NULL;
112 GError *error = NULL;
114 btime = localtime(&t);
116 /* the %x warning about 2 digit years is not an error */
117 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
118 if (buflen < 1) return "";
121 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
124 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
133 *-----------------------------------------------------------------------------
135 *-----------------------------------------------------------------------------
138 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
139 static void file_data_check_sidecars(FileData *fd);
140 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
143 void file_data_increment_version(FileData *fd)
149 fd->parent->version++;
150 fd->parent->valid_marks = 0;
154 static void file_data_set_collate_keys(FileData *fd)
156 gchar *caseless_name;
158 caseless_name = g_utf8_casefold(fd->name, -1);
160 g_free(fd->collate_key_name);
161 g_free(fd->collate_key_name_nocase);
163 #if GLIB_CHECK_VERSION(2, 8, 0)
164 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
165 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
167 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
168 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
170 g_free(caseless_name);
173 static void file_data_set_path(FileData *fd, const gchar *path)
175 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
176 g_assert(file_data_pool);
180 if (fd->original_path)
182 g_hash_table_remove(file_data_pool, fd->original_path);
183 g_free(fd->original_path);
186 g_assert(!g_hash_table_lookup(file_data_pool, path));
188 fd->original_path = g_strdup(path);
189 g_hash_table_insert(file_data_pool, fd->original_path, fd);
191 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
193 fd->path = g_strdup(path);
195 fd->extension = fd->name + 1;
196 file_data_set_collate_keys(fd);
200 fd->path = g_strdup(path);
201 fd->name = filename_from_path(fd->path);
203 if (strcmp(fd->name, "..") == 0)
205 gchar *dir = remove_level_from_path(path);
207 fd->path = remove_level_from_path(dir);
210 fd->extension = fd->name + 2;
211 file_data_set_collate_keys(fd);
214 else if (strcmp(fd->name, ".") == 0)
217 fd->path = remove_level_from_path(path);
219 fd->extension = fd->name + 1;
220 file_data_set_collate_keys(fd);
224 fd->extension = extension_from_path(fd->path);
225 if (fd->extension == NULL)
227 fd->extension = fd->name + strlen(fd->name);
230 file_data_set_collate_keys(fd);
233 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
235 gboolean ret = FALSE;
238 if (fd->size != st->st_size ||
239 fd->date != st->st_mtime)
241 fd->size = st->st_size;
242 fd->date = st->st_mtime;
243 fd->mode = st->st_mode;
244 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
245 fd->thumb_pixbuf = NULL;
246 file_data_increment_version(fd);
247 file_data_send_notification(fd, NOTIFY_REREAD);
251 work = fd->sidecar_files;
254 FileData *sfd = work->data;
258 if (!stat_utf8(sfd->path, &st))
262 file_data_disconnect_sidecar_file(fd, sfd);
267 ret |= file_data_check_changed_files_recursive(sfd, &st);
273 gboolean file_data_check_changed_files(FileData *fd)
275 gboolean ret = FALSE;
278 if (fd->parent) fd = fd->parent;
280 if (!stat_utf8(fd->path, &st))
283 FileData *sfd = NULL;
285 /* parent is missing, we have to rebuild whole group */
290 work = fd->sidecar_files;
296 file_data_disconnect_sidecar_file(fd, sfd);
298 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
299 file_data_send_notification(fd, NOTIFY_REREAD);
303 ret |= file_data_check_changed_files_recursive(fd, &st);
309 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
313 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
316 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
318 fd = g_hash_table_lookup(file_data_pool, path_utf8);
324 if (!fd && file_data_planned_change_hash)
326 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
329 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
331 file_data_apply_ci(fd);
340 changed = file_data_check_changed_files(fd);
342 changed = file_data_check_changed_files_recursive(fd, st);
343 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
344 file_data_check_sidecars(fd);
345 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
350 fd = g_new0(FileData, 1);
352 fd->size = st->st_size;
353 fd->date = st->st_mtime;
354 fd->mode = st->st_mode;
356 fd->magick = 0x12345678;
358 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
361 file_data_check_sidecars(fd);
366 static void file_data_check_sidecars(FileData *fd)
370 FileData *parent_fd = NULL;
373 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
376 base_len = fd->extension - fd->path;
377 fname = g_string_new_len(fd->path, base_len);
378 work = sidecar_ext_get_list();
382 /* check for possible sidecar files;
383 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
384 they have fd->ref set to 0 and file_data unref must chack and free them all together
385 (using fd->ref would cause loops and leaks)
389 gchar *ext = work->data;
393 if (g_ascii_strcasecmp(ext, fd->extension) == 0)
395 new_fd = fd; /* processing the original file */
400 g_string_truncate(fname, base_len);
402 if (!stat_utf8_case_insensitive_ext(fname, ext, &nst))
405 new_fd = file_data_new(fname->str, &nst, FALSE);
407 if (new_fd->disable_grouping)
409 file_data_unref(new_fd);
413 new_fd->ref--; /* do not use ref here */
417 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
419 file_data_merge_sidecar_files(parent_fd, new_fd);
421 g_string_free(fname, TRUE);
425 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
427 gchar *path_utf8 = path_to_utf8(path);
428 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
434 FileData *file_data_new_simple(const gchar *path_utf8)
438 if (!stat_utf8(path_utf8, &st))
444 return file_data_new(path_utf8, &st, TRUE);
447 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
449 sfd->parent = target;
450 if (!g_list_find(target->sidecar_files, sfd))
451 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
452 file_data_increment_version(sfd); /* increments both sfd and target */
457 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
461 file_data_add_sidecar_file(target, source);
463 work = source->sidecar_files;
466 FileData *sfd = work->data;
467 file_data_add_sidecar_file(target, sfd);
471 g_list_free(source->sidecar_files);
472 source->sidecar_files = NULL;
474 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
479 #ifdef DEBUG_FILEDATA
480 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
482 FileData *file_data_ref(FileData *fd)
485 if (fd == NULL) return NULL;
486 #ifdef DEBUG_FILEDATA
487 if (fd->magick != 0x12345678)
488 DEBUG_0("fd magick mismatch at %s:%d", file, line);
490 g_assert(fd->magick == 0x12345678);
493 #ifdef DEBUG_FILEDATA
494 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
496 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
501 static void file_data_free(FileData *fd)
503 g_assert(fd->magick == 0x12345678);
504 g_assert(fd->ref == 0);
506 g_hash_table_remove(file_data_pool, fd->original_path);
509 g_free(fd->original_path);
510 g_free(fd->collate_key_name);
511 g_free(fd->collate_key_name_nocase);
512 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
513 histmap_free(fd->histmap);
515 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
517 file_data_change_info_free(NULL, fd);
521 #ifdef DEBUG_FILEDATA
522 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
524 void file_data_unref(FileData *fd)
527 if (fd == NULL) return;
528 #ifdef DEBUG_FILEDATA
529 if (fd->magick != 0x12345678)
530 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
532 g_assert(fd->magick == 0x12345678);
535 #ifdef DEBUG_FILEDATA
536 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
538 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
543 FileData *parent = fd->parent ? fd->parent : fd;
545 if (parent->ref > 0) return;
547 work = parent->sidecar_files;
550 FileData *sfd = work->data;
551 if (sfd->ref > 0) return;
555 /* none of parent/children is referenced, we can free everything */
557 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
559 work = parent->sidecar_files;
562 FileData *sfd = work->data;
567 g_list_free(parent->sidecar_files);
568 parent->sidecar_files = NULL;
570 file_data_free(parent);
574 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
576 sfd->parent = target;
577 g_assert(g_list_find(target->sidecar_files, sfd));
579 file_data_increment_version(sfd); /* increments both sfd and target */
581 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
593 /* disables / enables grouping for particular file, sends UPDATE notification */
594 void file_data_disable_grouping(FileData *fd, gboolean disable)
596 if (!fd->disable_grouping == !disable) return;
597 fd->disable_grouping = !!disable;
603 FileData *parent = file_data_ref(fd->parent);
604 file_data_disconnect_sidecar_file(parent, fd);
605 file_data_send_notification(fd, NOTIFY_GROUPING);
606 file_data_send_notification(parent, NOTIFY_GROUPING);
607 file_data_unref(parent);
609 else if (fd->sidecar_files)
611 GList *sidecar_files = filelist_copy(fd->sidecar_files);
612 GList *work = sidecar_files;
615 FileData *sfd = work->data;
617 file_data_disconnect_sidecar_file(fd, sfd);
618 file_data_send_notification(sfd, NOTIFY_GROUPING);
620 file_data_send_notification(fd, NOTIFY_GROUPING);
621 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
622 filelist_free(sidecar_files);
627 file_data_check_sidecars(fd);
628 file_data_send_notification(fd, NOTIFY_GROUPING);
632 /* compare name without extension */
633 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
635 size_t len1 = fd1->extension - fd1->name;
636 size_t len2 = fd2->extension - fd2->name;
638 if (len1 < len2) return -1;
639 if (len1 > len2) return 1;
641 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
644 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
646 if (!fdci && fd) fdci = fd->change;
650 g_free(fdci->source);
655 if (fd) fd->change = NULL;
658 static gboolean file_data_can_write_directly(FileData *fd)
660 return filter_name_is_writable(fd->extension);
663 static gboolean file_data_can_write_sidecar(FileData *fd)
665 return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
668 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
670 gchar *sidecar_path = NULL;
673 if (!file_data_can_write_sidecar(fd)) return NULL;
675 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
678 FileData *sfd = work->data;
680 if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
682 sidecar_path = g_strdup(sfd->path);
687 if (!existing_only && !sidecar_path)
689 gchar *base = remove_extension_from_path(fd->path);
690 sidecar_path = g_strconcat(base, ".xmp", NULL);
699 *-----------------------------------------------------------------------------
700 * sidecar file info struct
701 *-----------------------------------------------------------------------------
706 static gint sidecar_file_priority(const gchar *path)
708 const gchar *extension = extension_from_path(path);
712 if (extension == NULL)
715 work = sidecar_ext_get_list();
718 gchar *ext = work->data;
721 if (g_ascii_strcasecmp(extension, ext) == 0) return i;
729 *-----------------------------------------------------------------------------
731 *-----------------------------------------------------------------------------
734 static SortType filelist_sort_method = SORT_NONE;
735 static gboolean filelist_sort_ascend = TRUE;
738 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
740 if (!filelist_sort_ascend)
747 switch (filelist_sort_method)
752 if (fa->size < fb->size) return -1;
753 if (fa->size > fb->size) return 1;
754 /* fall back to name */
757 if (fa->date < fb->date) return -1;
758 if (fa->date > fb->date) return 1;
759 /* fall back to name */
761 #ifdef HAVE_STRVERSCMP
763 return strverscmp(fa->name, fb->name);
770 if (options->file_sort.case_sensitive)
771 return strcmp(fa->collate_key_name, fb->collate_key_name);
773 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
776 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gboolean ascend)
778 filelist_sort_method = method;
779 filelist_sort_ascend = ascend;
780 return filelist_sort_compare_filedata(fa, fb);
783 static gint filelist_sort_file_cb(gpointer a, gpointer b)
785 return filelist_sort_compare_filedata(a, b);
788 GList *filelist_sort_full(GList *list, SortType method, gboolean ascend, GCompareFunc cb)
790 filelist_sort_method = method;
791 filelist_sort_ascend = ascend;
792 return g_list_sort(list, cb);
795 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gboolean ascend, GCompareFunc cb)
797 filelist_sort_method = method;
798 filelist_sort_ascend = ascend;
799 return g_list_insert_sorted(list, data, cb);
802 GList *filelist_sort(GList *list, SortType method, gboolean ascend)
804 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
807 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gboolean ascend)
809 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
813 static GList *filelist_filter_out_sidecars(GList *flist)
816 GList *flist_filtered = NULL;
820 FileData *fd = work->data;
823 if (fd->parent) /* remove fd's that are children */
826 flist_filtered = g_list_prepend(flist_filtered, fd);
830 return flist_filtered;
833 static gboolean is_hidden_file(const gchar *name)
835 if (name[0] != '.') return FALSE;
836 if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) return FALSE;
840 static gboolean filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gboolean follow_symlinks)
847 gint (*stat_func)(const gchar *path, struct stat *buf);
849 g_assert(files || dirs);
851 if (files) *files = NULL;
852 if (dirs) *dirs = NULL;
854 pathl = path_from_utf8(dir_fd->path);
855 if (!pathl) return FALSE;
869 while ((dir = readdir(dp)) != NULL)
871 struct stat ent_sbuf;
872 const gchar *name = dir->d_name;
875 if (!options->file_filter.show_hidden_files && is_hidden_file(name))
878 filepath = g_build_filename(pathl, name, NULL);
879 if (stat_func(filepath, &ent_sbuf) >= 0)
881 if (S_ISDIR(ent_sbuf.st_mode))
883 /* we ignore the .thumbnails dir for cleanliness */
885 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
886 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
887 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
888 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
890 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
895 if (files && filter_name_exists(name))
897 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
908 if (dirs) *dirs = dlist;
909 if (files) *files = filelist_filter_out_sidecars(flist);
914 gboolean filelist_read(FileData *dir_fd, GList **files, GList **dirs)
916 return filelist_read_real(dir_fd, files, dirs, TRUE);
919 gboolean filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
921 return filelist_read_real(dir_fd, files, dirs, FALSE);
924 void filelist_free(GList *list)
931 file_data_unref((FileData *)work->data);
939 GList *filelist_copy(GList *list)
941 GList *new_list = NULL;
952 new_list = g_list_prepend(new_list, file_data_ref(fd));
955 return g_list_reverse(new_list);
958 GList *filelist_from_path_list(GList *list)
960 GList *new_list = NULL;
971 new_list = g_list_prepend(new_list, file_data_new_simple(path));
974 return g_list_reverse(new_list);
977 GList *filelist_to_path_list(GList *list)
979 GList *new_list = NULL;
990 new_list = g_list_prepend(new_list, g_strdup(fd->path));
993 return g_list_reverse(new_list);
996 GList *filelist_filter(GList *list, gboolean is_dir_list)
1000 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1005 FileData *fd = (FileData *)(work->data);
1006 const gchar *name = fd->name;
1008 if ((!options->file_filter.show_hidden_files && is_hidden_file(name)) ||
1009 (!is_dir_list && !filter_name_exists(name)) ||
1010 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1011 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1015 list = g_list_remove_link(list, link);
1016 file_data_unref(fd);
1027 *-----------------------------------------------------------------------------
1028 * filelist recursive
1029 *-----------------------------------------------------------------------------
1032 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1034 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1037 GList *filelist_sort_path(GList *list)
1039 return g_list_sort(list, filelist_sort_path_cb);
1042 static void filelist_recursive_append(GList **list, GList *dirs)
1049 FileData *fd = (FileData *)(work->data);
1053 if (filelist_read(fd, &f, &d))
1055 f = filelist_filter(f, FALSE);
1056 f = filelist_sort_path(f);
1057 *list = g_list_concat(*list, f);
1059 d = filelist_filter(d, TRUE);
1060 d = filelist_sort_path(d);
1061 filelist_recursive_append(list, d);
1069 GList *filelist_recursive(FileData *dir_fd)
1074 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1075 list = filelist_filter(list, FALSE);
1076 list = filelist_sort_path(list);
1078 d = filelist_filter(d, TRUE);
1079 d = filelist_sort_path(d);
1080 filelist_recursive_append(&list, d);
1088 * marks and orientation
1091 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1092 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1093 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1094 static GDestroyNotify file_data_destroy_mark_func[FILEDATA_MARKS_SIZE];
1096 gboolean file_data_get_mark(FileData *fd, gint n)
1098 gboolean valid = (fd->valid_marks & (1 << n));
1100 if (file_data_get_mark_func[n] && !valid)
1102 guint old = fd->marks;
1103 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1105 if (!value != !(fd->marks & (1 << n)))
1107 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_MARKS);
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_MARKS);
1194 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data, GDestroyNotify notify)
1196 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1198 if (file_data_destroy_mark_func[n]) (file_data_destroy_mark_func[n])(file_data_mark_func_data[n]);
1200 file_data_get_mark_func[n] = get_mark_func;
1201 file_data_set_mark_func[n] = set_mark_func;
1202 file_data_mark_func_data[n] = data;
1203 file_data_destroy_mark_func[n] = notify;
1207 /* this effectively changes all known files */
1208 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1214 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1216 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1217 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1218 if (data) *data = file_data_mark_func_data[n];
1221 gint file_data_get_user_orientation(FileData *fd)
1223 return fd->user_orientation;
1226 void file_data_set_user_orientation(FileData *fd, gint value)
1228 if (fd->user_orientation == value) return;
1230 fd->user_orientation = value;
1231 file_data_increment_version(fd);
1232 file_data_send_notification(fd, NOTIFY_ORIENTATION);
1237 * file_data - operates on the given fd
1238 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1242 /* return list of sidecar file extensions in a string */
1243 gchar *file_data_sc_list_to_string(FileData *fd)
1246 GString *result = g_string_new("");
1248 work = fd->sidecar_files;
1251 FileData *sfd = work->data;
1253 result = g_string_append(result, "+ ");
1254 result = g_string_append(result, sfd->extension);
1256 if (work) result = g_string_append_c(result, ' ');
1259 return g_string_free(result, FALSE);
1265 * add FileDataChangeInfo (see typedefs.h) for the given operation
1266 * uses file_data_add_change_info
1268 * fails if the fd->change already exists - change operations can't run in parallel
1269 * fd->change_info works as a lock
1271 * dest can be NULL - in this case the current name is used for now, it will
1276 FileDataChangeInfo types:
1278 MOVE - path is changed, name may be changed too
1279 RENAME - path remains unchanged, name is changed
1280 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1281 sidecar names are changed too, extensions are not changed
1283 UPDATE - file size, date or grouping has been changed
1286 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1288 FileDataChangeInfo *fdci;
1290 if (fd->change) return FALSE;
1292 fdci = g_new0(FileDataChangeInfo, 1);
1297 fdci->source = g_strdup(src);
1299 fdci->source = g_strdup(fd->path);
1302 fdci->dest = g_strdup(dest);
1309 static void file_data_planned_change_remove(FileData *fd)
1311 if (file_data_planned_change_hash &&
1312 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1314 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1316 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1317 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1318 file_data_unref(fd);
1319 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1321 g_hash_table_destroy(file_data_planned_change_hash);
1322 file_data_planned_change_hash = NULL;
1323 DEBUG_1("planned change: empty");
1330 void file_data_free_ci(FileData *fd)
1332 FileDataChangeInfo *fdci = fd->change;
1336 file_data_planned_change_remove(fd);
1338 g_free(fdci->source);
1347 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1351 if (fd->parent) fd = fd->parent;
1353 if (fd->change) return FALSE;
1355 work = fd->sidecar_files;
1358 FileData *sfd = work->data;
1360 if (sfd->change) return FALSE;
1364 file_data_add_ci(fd, type, NULL, NULL);
1366 work = fd->sidecar_files;
1369 FileData *sfd = work->data;
1371 file_data_add_ci(sfd, type, NULL, NULL);
1378 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1382 if (fd->parent) fd = fd->parent;
1384 if (!fd->change || fd->change->type != type) return FALSE;
1386 work = fd->sidecar_files;
1389 FileData *sfd = work->data;
1391 if (!sfd->change || sfd->change->type != type) return FALSE;
1399 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1401 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1402 file_data_sc_update_ci_copy(fd, dest_path);
1406 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1408 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1409 file_data_sc_update_ci_move(fd, dest_path);
1413 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1415 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1416 file_data_sc_update_ci_rename(fd, dest_path);
1420 gboolean file_data_sc_add_ci_delete(FileData *fd)
1422 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1425 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1427 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1428 file_data_sc_update_ci_unspecified(fd, dest_path);
1432 gboolean file_data_add_ci_write_metadata(FileData *fd)
1434 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1437 void file_data_sc_free_ci(FileData *fd)
1441 if (fd->parent) fd = fd->parent;
1443 file_data_free_ci(fd);
1445 work = fd->sidecar_files;
1448 FileData *sfd = work->data;
1450 file_data_free_ci(sfd);
1455 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1458 gboolean ret = TRUE;
1463 FileData *fd = work->data;
1465 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1472 static void file_data_sc_revert_ci_list(GList *fd_list)
1479 FileData *fd = work->data;
1481 file_data_sc_free_ci(fd);
1486 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1493 FileData *fd = work->data;
1495 if (!func(fd, dest))
1497 file_data_sc_revert_ci_list(work->prev);
1506 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1508 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1511 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1513 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1516 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1518 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1521 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1523 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1526 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1529 gboolean ret = TRUE;
1534 FileData *fd = work->data;
1536 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1543 void file_data_free_ci_list(GList *fd_list)
1550 FileData *fd = work->data;
1552 file_data_free_ci(fd);
1557 void file_data_sc_free_ci_list(GList *fd_list)
1564 FileData *fd = work->data;
1566 file_data_sc_free_ci(fd);
1572 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1573 * fails if fd->change does not exist or the change type does not match
1576 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1578 FileDataChangeType type = fd->change->type;
1580 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1584 if (!file_data_planned_change_hash)
1585 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1587 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1589 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1590 g_hash_table_remove(file_data_planned_change_hash, old_path);
1591 file_data_unref(fd);
1594 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1599 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1600 g_hash_table_remove(file_data_planned_change_hash, new_path);
1601 file_data_unref(ofd);
1604 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1606 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1611 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1613 gchar *old_path = fd->change->dest;
1615 fd->change->dest = g_strdup(dest_path);
1616 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1620 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1622 const gchar *extension = extension_from_path(fd->change->source);
1623 gchar *base = remove_extension_from_path(dest_path);
1624 gchar *old_path = fd->change->dest;
1626 fd->change->dest = g_strconcat(base, extension, NULL);
1627 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1633 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1636 gchar *dest_path_full = NULL;
1638 if (fd->parent) fd = fd->parent;
1642 dest_path = fd->path;
1644 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1646 gchar *dir = remove_level_from_path(fd->path);
1648 dest_path_full = g_build_filename(dir, dest_path, NULL);
1650 dest_path = dest_path_full;
1652 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1654 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1655 dest_path = dest_path_full;
1658 file_data_update_ci_dest(fd, dest_path);
1660 work = fd->sidecar_files;
1663 FileData *sfd = work->data;
1665 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1669 g_free(dest_path_full);
1672 static gboolean file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1674 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1675 file_data_sc_update_ci(fd, dest_path);
1679 gboolean file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1681 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1684 gboolean file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1686 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1689 gboolean file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1691 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1694 gboolean file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1696 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1699 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1701 gboolean (*func)(FileData *, const gchar *))
1704 gboolean ret = TRUE;
1709 FileData *fd = work->data;
1711 if (!func(fd, dest)) ret = FALSE;
1718 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1720 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1723 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1725 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1728 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1730 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1735 * verify source and dest paths - dest image exists, etc.
1736 * it should detect all possible problems with the planned operation
1739 gint file_data_verify_ci(FileData *fd)
1741 gint ret = CHANGE_OK;
1746 DEBUG_1("Change checked: no change info: %s", fd->path);
1750 if (!isname(fd->path))
1752 /* this probably should not happen */
1753 ret |= CHANGE_NO_SRC;
1754 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1758 dir = remove_level_from_path(fd->path);
1760 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1761 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1762 !access_file(fd->path, R_OK))
1764 ret |= CHANGE_NO_READ_PERM;
1765 DEBUG_1("Change checked: no read permission: %s", fd->path);
1767 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1768 !access_file(dir, W_OK))
1770 ret |= CHANGE_NO_WRITE_PERM_DIR;
1771 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1773 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1774 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1775 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1776 !access_file(fd->path, W_OK))
1778 ret |= CHANGE_WARN_NO_WRITE_PERM;
1779 DEBUG_1("Change checked: no write permission: %s", fd->path);
1781 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1782 - that means that there are no hard errors and warnings can be disabled
1783 - the destination is determined during the check
1785 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1787 /* determine destination file */
1788 gboolean have_dest = FALSE;
1789 gchar *dest_dir = NULL;
1791 if (options->metadata.save_in_image_file)
1793 if (file_data_can_write_directly(fd))
1795 /* we can write the file directly */
1796 if (access_file(fd->path, W_OK))
1802 if (options->metadata.warn_on_write_problems)
1804 ret |= CHANGE_WARN_NO_WRITE_PERM;
1805 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1809 else if (file_data_can_write_sidecar(fd))
1811 /* we can write sidecar */
1812 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1813 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1815 file_data_update_ci_dest(fd, sidecar);
1820 if (options->metadata.warn_on_write_problems)
1822 ret |= CHANGE_WARN_NO_WRITE_PERM;
1823 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1832 /* write private metadata file under ~/.geeqie */
1834 /* If an existing metadata file exists, we will try writing to
1835 * it's location regardless of the user's preference.
1837 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1838 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1840 if (metadata_path && !access_file(metadata_path, W_OK))
1842 g_free(metadata_path);
1843 metadata_path = NULL;
1850 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1851 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1853 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1855 metadata_path = g_build_filename(dest_dir, filename, NULL);
1859 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1861 file_data_update_ci_dest(fd, metadata_path);
1866 ret |= CHANGE_NO_WRITE_PERM_DEST;
1867 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1869 g_free(metadata_path);
1874 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1879 same = (strcmp(fd->path, fd->change->dest) == 0);
1883 const gchar *dest_ext = extension_from_path(fd->change->dest);
1884 if (!dest_ext) dest_ext = "";
1886 if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
1888 ret |= CHANGE_WARN_CHANGED_EXT;
1889 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1894 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1896 ret |= CHANGE_WARN_SAME;
1897 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1901 dest_dir = remove_level_from_path(fd->change->dest);
1903 if (!isdir(dest_dir))
1905 ret |= CHANGE_NO_DEST_DIR;
1906 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1908 else if (!access_file(dest_dir, W_OK))
1910 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1911 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1915 if (isfile(fd->change->dest))
1917 if (!access_file(fd->change->dest, W_OK))
1919 ret |= CHANGE_NO_WRITE_PERM_DEST;
1920 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1924 ret |= CHANGE_WARN_DEST_EXISTS;
1925 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1928 else if (isdir(fd->change->dest))
1930 ret |= CHANGE_DEST_EXISTS;
1931 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1938 fd->change->error = ret;
1939 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1946 gint file_data_sc_verify_ci(FileData *fd)
1951 ret = file_data_verify_ci(fd);
1953 work = fd->sidecar_files;
1956 FileData *sfd = work->data;
1958 ret |= file_data_verify_ci(sfd);
1965 gchar *file_data_get_error_string(gint error)
1967 GString *result = g_string_new("");
1969 if (error & CHANGE_NO_SRC)
1971 if (result->len > 0) g_string_append(result, ", ");
1972 g_string_append(result, _("file or directory does not exist"));
1975 if (error & CHANGE_DEST_EXISTS)
1977 if (result->len > 0) g_string_append(result, ", ");
1978 g_string_append(result, _("destination already exists"));
1981 if (error & CHANGE_NO_WRITE_PERM_DEST)
1983 if (result->len > 0) g_string_append(result, ", ");
1984 g_string_append(result, _("destination can't be overwritten"));
1987 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1989 if (result->len > 0) g_string_append(result, ", ");
1990 g_string_append(result, _("destination directory is not writable"));
1993 if (error & CHANGE_NO_DEST_DIR)
1995 if (result->len > 0) g_string_append(result, ", ");
1996 g_string_append(result, _("destination directory does not exist"));
1999 if (error & CHANGE_NO_WRITE_PERM_DIR)
2001 if (result->len > 0) g_string_append(result, ", ");
2002 g_string_append(result, _("source directory is not writable"));
2005 if (error & CHANGE_NO_READ_PERM)
2007 if (result->len > 0) g_string_append(result, ", ");
2008 g_string_append(result, _("no read permission"));
2011 if (error & CHANGE_WARN_NO_WRITE_PERM)
2013 if (result->len > 0) g_string_append(result, ", ");
2014 g_string_append(result, _("file is readonly"));
2017 if (error & CHANGE_WARN_DEST_EXISTS)
2019 if (result->len > 0) g_string_append(result, ", ");
2020 g_string_append(result, _("destination already exists and will be overwritten"));
2023 if (error & CHANGE_WARN_SAME)
2025 if (result->len > 0) g_string_append(result, ", ");
2026 g_string_append(result, _("source and destination are the same"));
2029 if (error & CHANGE_WARN_CHANGED_EXT)
2031 if (result->len > 0) g_string_append(result, ", ");
2032 g_string_append(result, _("source and destination have different extension"));
2035 return g_string_free(result, FALSE);
2038 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2041 gint all_errors = 0;
2042 gint common_errors = ~0;
2047 if (!list) return 0;
2049 num = g_list_length(list);
2050 errors = g_new(int, num);
2061 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2062 all_errors |= error;
2063 common_errors &= error;
2070 if (desc && all_errors)
2073 GString *result = g_string_new("");
2077 gchar *str = file_data_get_error_string(common_errors);
2078 g_string_append(result, str);
2079 g_string_append(result, "\n");
2093 error = errors[i] & ~common_errors;
2097 gchar *str = file_data_get_error_string(error);
2098 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2103 *desc = g_string_free(result, FALSE);
2112 * perform the change described by FileFataChangeInfo
2113 * it is used for internal operations,
2114 * this function actually operates with files on the filesystem
2115 * it should implement safe delete
2118 static gboolean file_data_perform_move(FileData *fd)
2120 g_assert(!strcmp(fd->change->source, fd->path));
2121 return move_file(fd->change->source, fd->change->dest);
2124 static gboolean file_data_perform_copy(FileData *fd)
2126 g_assert(!strcmp(fd->change->source, fd->path));
2127 return copy_file(fd->change->source, fd->change->dest);
2130 static gboolean file_data_perform_delete(FileData *fd)
2132 if (isdir(fd->path) && !islink(fd->path))
2133 return rmdir_utf8(fd->path);
2135 if (options->file_ops.safe_delete_enable)
2136 return file_util_safe_unlink(fd->path);
2138 return unlink_file(fd->path);
2141 gboolean file_data_perform_ci(FileData *fd)
2143 FileDataChangeType type = fd->change->type;
2147 case FILEDATA_CHANGE_MOVE:
2148 return file_data_perform_move(fd);
2149 case FILEDATA_CHANGE_COPY:
2150 return file_data_perform_copy(fd);
2151 case FILEDATA_CHANGE_RENAME:
2152 return file_data_perform_move(fd); /* the same as move */
2153 case FILEDATA_CHANGE_DELETE:
2154 return file_data_perform_delete(fd);
2155 case FILEDATA_CHANGE_WRITE_METADATA:
2156 return metadata_write_perform(fd);
2157 case FILEDATA_CHANGE_UNSPECIFIED:
2158 /* nothing to do here */
2166 gboolean file_data_sc_perform_ci(FileData *fd)
2169 gboolean ret = TRUE;
2170 FileDataChangeType type = fd->change->type;
2172 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2174 work = fd->sidecar_files;
2177 FileData *sfd = work->data;
2179 if (!file_data_perform_ci(sfd)) ret = FALSE;
2183 if (!file_data_perform_ci(fd)) ret = FALSE;
2189 * updates FileData structure according to FileDataChangeInfo
2192 gboolean file_data_apply_ci(FileData *fd)
2194 FileDataChangeType type = fd->change->type;
2197 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2199 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2200 file_data_planned_change_remove(fd);
2202 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2204 /* this change overwrites another file which is already known to other modules
2205 renaming fd would create duplicate FileData structure
2206 the best thing we can do is nothing
2207 FIXME: maybe we could copy stuff like marks
2209 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2213 file_data_set_path(fd, fd->change->dest);
2216 file_data_increment_version(fd);
2217 file_data_send_notification(fd, NOTIFY_CHANGE);
2222 gboolean file_data_sc_apply_ci(FileData *fd)
2225 FileDataChangeType type = fd->change->type;
2227 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2229 work = fd->sidecar_files;
2232 FileData *sfd = work->data;
2234 file_data_apply_ci(sfd);
2238 file_data_apply_ci(fd);
2244 * notify other modules about the change described by FileFataChangeInfo
2247 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2248 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2249 implementation in view_file_list.c */
2254 typedef struct _NotifyData NotifyData;
2256 struct _NotifyData {
2257 FileDataNotifyFunc func;
2259 NotifyPriority priority;
2262 static GList *notify_func_list = NULL;
2264 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2266 NotifyData *nda = (NotifyData *)a;
2267 NotifyData *ndb = (NotifyData *)b;
2269 if (nda->priority < ndb->priority) return -1;
2270 if (nda->priority > ndb->priority) return 1;
2274 gboolean file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2278 nd = g_new(NotifyData, 1);
2281 nd->priority = priority;
2283 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2284 DEBUG_1("Notify func registered: %p", nd);
2289 gboolean file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2291 GList *work = notify_func_list;
2295 NotifyData *nd = (NotifyData *)work->data;
2297 if (nd->func == func && nd->data == data)
2299 notify_func_list = g_list_delete_link(notify_func_list, work);
2301 DEBUG_1("Notify func unregistered: %p", nd);
2311 void file_data_send_notification(FileData *fd, NotifyType type)
2313 GList *work = notify_func_list;
2317 NotifyData *nd = (NotifyData *)work->data;
2319 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2320 nd->func(fd, type, nd->data);
2325 static GHashTable *file_data_monitor_pool = NULL;
2326 static gint realtime_monitor_id = -1;
2328 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2332 file_data_check_changed_files(fd);
2334 DEBUG_1("monitor %s", fd->path);
2337 static gboolean realtime_monitor_cb(gpointer data)
2339 if (!options->update_on_time_change) return TRUE;
2340 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2344 gboolean file_data_register_real_time_monitor(FileData *fd)
2350 if (!file_data_monitor_pool)
2351 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2353 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2355 DEBUG_1("Register realtime %d %s", count, fd->path);
2358 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2360 if (realtime_monitor_id == -1)
2362 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2368 gboolean file_data_unregister_real_time_monitor(FileData *fd)
2372 g_assert(file_data_monitor_pool);
2374 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2376 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2378 g_assert(count > 0);
2383 g_hash_table_remove(file_data_monitor_pool, fd);
2385 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2387 file_data_unref(fd);
2389 if (g_hash_table_size(file_data_monitor_pool) == 0)
2391 g_source_remove(realtime_monitor_id);
2392 realtime_monitor_id = -1;
2398 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */