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"
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)
226 fd->extension = fd->name + strlen(fd->name);
229 file_data_set_collate_keys(fd);
232 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
234 gboolean ret = FALSE;
237 if (fd->size != st->st_size ||
238 fd->date != st->st_mtime)
240 fd->size = st->st_size;
241 fd->date = st->st_mtime;
242 fd->mode = st->st_mode;
243 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
244 fd->thumb_pixbuf = NULL;
245 file_data_increment_version(fd);
246 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
250 work = fd->sidecar_files;
253 FileData *sfd = work->data;
257 if (!stat_utf8(sfd->path, &st))
261 file_data_disconnect_sidecar_file(fd, sfd);
266 ret |= file_data_check_changed_files_recursive(sfd, &st);
272 gboolean file_data_check_changed_files(FileData *fd)
274 gboolean ret = FALSE;
277 if (fd->parent) fd = fd->parent;
279 if (!stat_utf8(fd->path, &st))
282 FileData *sfd = NULL;
284 /* parent is missing, we have to rebuild whole group */
289 work = fd->sidecar_files;
295 file_data_disconnect_sidecar_file(fd, sfd);
297 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
298 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
302 ret |= file_data_check_changed_files_recursive(fd, &st);
308 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
312 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
315 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
317 fd = g_hash_table_lookup(file_data_pool, path_utf8);
323 if (!fd && file_data_planned_change_hash)
325 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
328 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
330 file_data_apply_ci(fd);
339 changed = file_data_check_changed_files(fd);
341 changed = file_data_check_changed_files_recursive(fd, st);
342 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
343 file_data_check_sidecars(fd);
344 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
349 fd = g_new0(FileData, 1);
351 fd->size = st->st_size;
352 fd->date = st->st_mtime;
353 fd->mode = st->st_mode;
355 fd->magick = 0x12345678;
357 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
360 file_data_check_sidecars(fd);
365 static void file_data_check_sidecars(FileData *fd)
369 FileData *parent_fd = NULL;
372 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
375 base_len = fd->extension - fd->path;
376 fname = g_string_new_len(fd->path, base_len);
377 work = sidecar_ext_get_list();
381 /* check for possible sidecar files;
382 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
383 they have fd->ref set to 0 and file_data unref must chack and free them all together
384 (using fd->ref would cause loops and leaks)
388 gchar *ext = work->data;
392 if (g_ascii_strcasecmp(ext, fd->extension) == 0)
394 new_fd = fd; /* processing the original file */
399 g_string_truncate(fname, base_len);
401 if (!stat_utf8_case_insensitive_ext(fname, ext, &nst))
404 new_fd = file_data_new(fname->str, &nst, FALSE);
406 if (new_fd->disable_grouping)
408 file_data_unref(new_fd);
412 new_fd->ref--; /* do not use ref here */
416 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
418 file_data_merge_sidecar_files(parent_fd, new_fd);
420 g_string_free(fname, TRUE);
424 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
426 gchar *path_utf8 = path_to_utf8(path);
427 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
433 FileData *file_data_new_simple(const gchar *path_utf8)
437 if (!stat_utf8(path_utf8, &st))
443 return file_data_new(path_utf8, &st, TRUE);
446 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
448 sfd->parent = target;
449 if (!g_list_find(target->sidecar_files, sfd))
450 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
451 file_data_increment_version(sfd); /* increments both sfd and target */
456 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
460 file_data_add_sidecar_file(target, source);
462 work = source->sidecar_files;
465 FileData *sfd = work->data;
466 file_data_add_sidecar_file(target, sfd);
470 g_list_free(source->sidecar_files);
471 source->sidecar_files = NULL;
473 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
478 #ifdef DEBUG_FILEDATA
479 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
481 FileData *file_data_ref(FileData *fd)
484 if (fd == NULL) return NULL;
485 #ifdef DEBUG_FILEDATA
486 if (fd->magick != 0x12345678)
487 DEBUG_0("fd magick mismatch at %s:%d", file, line);
489 g_assert(fd->magick == 0x12345678);
492 #ifdef DEBUG_FILEDATA
493 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
495 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
500 static void file_data_free(FileData *fd)
502 g_assert(fd->magick == 0x12345678);
503 g_assert(fd->ref == 0);
505 g_hash_table_remove(file_data_pool, fd->original_path);
508 g_free(fd->original_path);
509 g_free(fd->collate_key_name);
510 g_free(fd->collate_key_name_nocase);
511 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
514 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
516 file_data_change_info_free(NULL, fd);
520 #ifdef DEBUG_FILEDATA
521 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
523 void file_data_unref(FileData *fd)
526 if (fd == NULL) return;
527 #ifdef DEBUG_FILEDATA
528 if (fd->magick != 0x12345678)
529 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
531 g_assert(fd->magick == 0x12345678);
534 #ifdef DEBUG_FILEDATA
535 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
537 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
542 FileData *parent = fd->parent ? fd->parent : fd;
544 if (parent->ref > 0) return;
546 work = parent->sidecar_files;
549 FileData *sfd = work->data;
550 if (sfd->ref > 0) return;
554 /* none of parent/children is referenced, we can free everything */
556 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
558 work = parent->sidecar_files;
561 FileData *sfd = work->data;
566 g_list_free(parent->sidecar_files);
567 parent->sidecar_files = NULL;
569 file_data_free(parent);
573 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
575 sfd->parent = target;
576 g_assert(g_list_find(target->sidecar_files, sfd));
578 file_data_increment_version(sfd); /* increments both sfd and target */
580 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
592 /* disables / enables grouping for particular file, sends UPDATE notification */
593 void file_data_disable_grouping(FileData *fd, gboolean disable)
595 if (!fd->disable_grouping == !disable) return;
596 fd->disable_grouping = !!disable;
602 FileData *parent = file_data_ref(fd->parent);
603 file_data_disconnect_sidecar_file(parent, fd);
604 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
605 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
606 file_data_unref(parent);
608 else if (fd->sidecar_files)
610 GList *sidecar_files = filelist_copy(fd->sidecar_files);
611 GList *work = sidecar_files;
614 FileData *sfd = work->data;
616 file_data_disconnect_sidecar_file(fd, sfd);
617 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
619 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
620 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
621 filelist_free(sidecar_files);
626 file_data_check_sidecars(fd);
627 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
631 /* compare name without extension */
632 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
634 size_t len1 = fd1->extension - fd1->name;
635 size_t len2 = fd2->extension - fd2->name;
637 if (len1 < len2) return -1;
638 if (len1 > len2) return 1;
640 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
643 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
645 if (!fdci && fd) fdci = fd->change;
649 g_free(fdci->source);
654 if (fd) fd->change = NULL;
657 static gboolean file_data_can_write_directly(FileData *fd)
659 return filter_name_is_writable(fd->extension);
662 static gboolean file_data_can_write_sidecar(FileData *fd)
664 return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
667 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
669 gchar *sidecar_path = NULL;
672 if (!file_data_can_write_sidecar(fd)) return NULL;
674 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
677 FileData *sfd = work->data;
679 if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
681 sidecar_path = g_strdup(sfd->path);
686 if (!existing_only && !sidecar_path)
688 gchar *base = remove_extension_from_path(fd->path);
689 sidecar_path = g_strconcat(base, ".xmp", NULL);
698 *-----------------------------------------------------------------------------
699 * sidecar file info struct
700 *-----------------------------------------------------------------------------
705 static gint sidecar_file_priority(const gchar *path)
707 const gchar *extension = extension_from_path(path);
711 if (extension == NULL)
714 work = sidecar_ext_get_list();
717 gchar *ext = work->data;
720 if (g_ascii_strcasecmp(extension, ext) == 0) return i;
728 *-----------------------------------------------------------------------------
730 *-----------------------------------------------------------------------------
733 static SortType filelist_sort_method = SORT_NONE;
734 static gboolean filelist_sort_ascend = TRUE;
737 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
739 if (!filelist_sort_ascend)
746 switch (filelist_sort_method)
751 if (fa->size < fb->size) return -1;
752 if (fa->size > fb->size) return 1;
753 /* fall back to name */
756 if (fa->date < fb->date) return -1;
757 if (fa->date > fb->date) return 1;
758 /* fall back to name */
760 #ifdef HAVE_STRVERSCMP
762 return strverscmp(fa->name, fb->name);
769 if (options->file_sort.case_sensitive)
770 return strcmp(fa->collate_key_name, fb->collate_key_name);
772 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
775 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gboolean ascend)
777 filelist_sort_method = method;
778 filelist_sort_ascend = ascend;
779 return filelist_sort_compare_filedata(fa, fb);
782 static gint filelist_sort_file_cb(gpointer a, gpointer b)
784 return filelist_sort_compare_filedata(a, b);
787 GList *filelist_sort_full(GList *list, SortType method, gboolean ascend, GCompareFunc cb)
789 filelist_sort_method = method;
790 filelist_sort_ascend = ascend;
791 return g_list_sort(list, cb);
794 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gboolean ascend, GCompareFunc cb)
796 filelist_sort_method = method;
797 filelist_sort_ascend = ascend;
798 return g_list_insert_sorted(list, data, cb);
801 GList *filelist_sort(GList *list, SortType method, gboolean ascend)
803 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
806 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gboolean ascend)
808 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
812 static GList *filelist_filter_out_sidecars(GList *flist)
815 GList *flist_filtered = NULL;
819 FileData *fd = work->data;
822 if (fd->parent) /* remove fd's that are children */
825 flist_filtered = g_list_prepend(flist_filtered, fd);
829 return flist_filtered;
832 static gboolean is_hidden_file(const gchar *name)
834 if (name[0] != '.') return FALSE;
835 if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) return FALSE;
839 static gboolean filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gboolean follow_symlinks)
846 gint (*stat_func)(const gchar *path, struct stat *buf);
848 g_assert(files || dirs);
850 if (files) *files = NULL;
851 if (dirs) *dirs = NULL;
853 pathl = path_from_utf8(dir_fd->path);
854 if (!pathl) return FALSE;
868 while ((dir = readdir(dp)) != NULL)
870 struct stat ent_sbuf;
871 const gchar *name = dir->d_name;
874 if (!options->file_filter.show_hidden_files && is_hidden_file(name))
877 filepath = g_build_filename(pathl, name, NULL);
878 if (stat_func(filepath, &ent_sbuf) >= 0)
880 if (S_ISDIR(ent_sbuf.st_mode))
882 /* we ignore the .thumbnails dir for cleanliness */
884 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
885 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
886 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
887 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
889 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
894 if (files && filter_name_exists(name))
896 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
907 if (dirs) *dirs = dlist;
908 if (files) *files = filelist_filter_out_sidecars(flist);
913 gboolean filelist_read(FileData *dir_fd, GList **files, GList **dirs)
915 return filelist_read_real(dir_fd, files, dirs, TRUE);
918 gboolean filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
920 return filelist_read_real(dir_fd, files, dirs, FALSE);
923 void filelist_free(GList *list)
930 file_data_unref((FileData *)work->data);
938 GList *filelist_copy(GList *list)
940 GList *new_list = NULL;
951 new_list = g_list_prepend(new_list, file_data_ref(fd));
954 return g_list_reverse(new_list);
957 GList *filelist_from_path_list(GList *list)
959 GList *new_list = NULL;
970 new_list = g_list_prepend(new_list, file_data_new_simple(path));
973 return g_list_reverse(new_list);
976 GList *filelist_to_path_list(GList *list)
978 GList *new_list = NULL;
989 new_list = g_list_prepend(new_list, g_strdup(fd->path));
992 return g_list_reverse(new_list);
995 GList *filelist_filter(GList *list, gboolean is_dir_list)
999 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1004 FileData *fd = (FileData *)(work->data);
1005 const gchar *name = fd->name;
1007 if ((!options->file_filter.show_hidden_files && is_hidden_file(name)) ||
1008 (!is_dir_list && !filter_name_exists(name)) ||
1009 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1010 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1014 list = g_list_remove_link(list, link);
1015 file_data_unref(fd);
1026 *-----------------------------------------------------------------------------
1027 * filelist recursive
1028 *-----------------------------------------------------------------------------
1031 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1033 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1036 GList *filelist_sort_path(GList *list)
1038 return g_list_sort(list, filelist_sort_path_cb);
1041 static void filelist_recursive_append(GList **list, GList *dirs)
1048 FileData *fd = (FileData *)(work->data);
1052 if (filelist_read(fd, &f, &d))
1054 f = filelist_filter(f, FALSE);
1055 f = filelist_sort_path(f);
1056 *list = g_list_concat(*list, f);
1058 d = filelist_filter(d, TRUE);
1059 d = filelist_sort_path(d);
1060 filelist_recursive_append(list, d);
1068 GList *filelist_recursive(FileData *dir_fd)
1073 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1074 list = filelist_filter(list, FALSE);
1075 list = filelist_sort_path(list);
1077 d = filelist_filter(d, TRUE);
1078 d = filelist_sort_path(d);
1079 filelist_recursive_append(&list, d);
1087 * marks and orientation
1090 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1091 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1092 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1093 static GDestroyNotify file_data_destroy_mark_func[FILEDATA_MARKS_SIZE];
1095 gboolean file_data_get_mark(FileData *fd, gint n)
1097 gboolean valid = (fd->valid_marks & (1 << n));
1099 if (file_data_get_mark_func[n] && !valid)
1101 guint old = fd->marks;
1102 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1104 if (!value != !(fd->marks & (1 << n)))
1106 fd->marks = fd->marks ^ (1 << n);
1109 fd->valid_marks |= (1 << n);
1110 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1112 file_data_unref(fd);
1114 else if (!old && fd->marks)
1120 return !!(fd->marks & (1 << n));
1123 guint file_data_get_marks(FileData *fd)
1126 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1130 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1133 if (!value == !file_data_get_mark(fd, n)) return;
1135 if (file_data_set_mark_func[n])
1137 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1142 fd->marks = fd->marks ^ (1 << n);
1144 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1146 file_data_unref(fd);
1148 else if (!old && fd->marks)
1153 file_data_increment_version(fd);
1154 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1157 gboolean file_data_filter_marks(FileData *fd, guint filter)
1160 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1161 return ((fd->marks & filter) == filter);
1164 GList *file_data_filter_marks_list(GList *list, guint filter)
1171 FileData *fd = work->data;
1175 if (!file_data_filter_marks(fd, filter))
1177 list = g_list_remove_link(list, link);
1178 file_data_unref(fd);
1186 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1188 FileData *fd = value;
1189 file_data_increment_version(fd);
1190 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1193 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data, GDestroyNotify notify)
1195 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1197 if (file_data_destroy_mark_func[n]) (file_data_destroy_mark_func[n])(file_data_mark_func_data[n]);
1199 file_data_get_mark_func[n] = get_mark_func;
1200 file_data_set_mark_func[n] = set_mark_func;
1201 file_data_mark_func_data[n] = data;
1202 file_data_destroy_mark_func[n] = notify;
1206 /* this effectively changes all known files */
1207 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1213 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1215 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1216 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1217 if (data) *data = file_data_mark_func_data[n];
1220 gint file_data_get_user_orientation(FileData *fd)
1222 return fd->user_orientation;
1225 void file_data_set_user_orientation(FileData *fd, gint value)
1227 if (fd->user_orientation == value) return;
1229 fd->user_orientation = value;
1230 file_data_increment_version(fd);
1231 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1236 * file_data - operates on the given fd
1237 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1241 /* return list of sidecar file extensions in a string */
1242 gchar *file_data_sc_list_to_string(FileData *fd)
1245 GString *result = g_string_new("");
1247 work = fd->sidecar_files;
1250 FileData *sfd = work->data;
1252 result = g_string_append(result, "+ ");
1253 result = g_string_append(result, sfd->extension);
1255 if (work) result = g_string_append_c(result, ' ');
1258 return g_string_free(result, FALSE);
1264 * add FileDataChangeInfo (see typedefs.h) for the given operation
1265 * uses file_data_add_change_info
1267 * fails if the fd->change already exists - change operations can't run in parallel
1268 * fd->change_info works as a lock
1270 * dest can be NULL - in this case the current name is used for now, it will
1275 FileDataChangeInfo types:
1277 MOVE - path is changed, name may be changed too
1278 RENAME - path remains unchanged, name is changed
1279 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1280 sidecar names are changed too, extensions are not changed
1282 UPDATE - file size, date or grouping has been changed
1285 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1287 FileDataChangeInfo *fdci;
1289 if (fd->change) return FALSE;
1291 fdci = g_new0(FileDataChangeInfo, 1);
1296 fdci->source = g_strdup(src);
1298 fdci->source = g_strdup(fd->path);
1301 fdci->dest = g_strdup(dest);
1308 static void file_data_planned_change_remove(FileData *fd)
1310 if (file_data_planned_change_hash &&
1311 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1313 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1315 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1316 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1317 file_data_unref(fd);
1318 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1320 g_hash_table_destroy(file_data_planned_change_hash);
1321 file_data_planned_change_hash = NULL;
1322 DEBUG_1("planned change: empty");
1329 void file_data_free_ci(FileData *fd)
1331 FileDataChangeInfo *fdci = fd->change;
1335 file_data_planned_change_remove(fd);
1337 g_free(fdci->source);
1346 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1350 if (fd->parent) fd = fd->parent;
1352 if (fd->change) return FALSE;
1354 work = fd->sidecar_files;
1357 FileData *sfd = work->data;
1359 if (sfd->change) return FALSE;
1363 file_data_add_ci(fd, type, NULL, NULL);
1365 work = fd->sidecar_files;
1368 FileData *sfd = work->data;
1370 file_data_add_ci(sfd, type, NULL, NULL);
1377 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1381 if (fd->parent) fd = fd->parent;
1383 if (!fd->change || fd->change->type != type) return FALSE;
1385 work = fd->sidecar_files;
1388 FileData *sfd = work->data;
1390 if (!sfd->change || sfd->change->type != type) return FALSE;
1398 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1400 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1401 file_data_sc_update_ci_copy(fd, dest_path);
1405 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1407 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1408 file_data_sc_update_ci_move(fd, dest_path);
1412 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1414 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1415 file_data_sc_update_ci_rename(fd, dest_path);
1419 gboolean file_data_sc_add_ci_delete(FileData *fd)
1421 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1424 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1426 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1427 file_data_sc_update_ci_unspecified(fd, dest_path);
1431 gboolean file_data_add_ci_write_metadata(FileData *fd)
1433 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1436 void file_data_sc_free_ci(FileData *fd)
1440 if (fd->parent) fd = fd->parent;
1442 file_data_free_ci(fd);
1444 work = fd->sidecar_files;
1447 FileData *sfd = work->data;
1449 file_data_free_ci(sfd);
1454 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1457 gboolean ret = TRUE;
1462 FileData *fd = work->data;
1464 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1471 static void file_data_sc_revert_ci_list(GList *fd_list)
1478 FileData *fd = work->data;
1480 file_data_sc_free_ci(fd);
1485 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1492 FileData *fd = work->data;
1494 if (!func(fd, dest))
1496 file_data_sc_revert_ci_list(work->prev);
1505 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1507 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1510 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1512 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1515 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1517 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1520 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1522 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1525 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1528 gboolean ret = TRUE;
1533 FileData *fd = work->data;
1535 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1542 void file_data_free_ci_list(GList *fd_list)
1549 FileData *fd = work->data;
1551 file_data_free_ci(fd);
1556 void file_data_sc_free_ci_list(GList *fd_list)
1563 FileData *fd = work->data;
1565 file_data_sc_free_ci(fd);
1571 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1572 * fails if fd->change does not exist or the change type does not match
1575 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1577 FileDataChangeType type = fd->change->type;
1579 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1583 if (!file_data_planned_change_hash)
1584 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1586 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1588 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1589 g_hash_table_remove(file_data_planned_change_hash, old_path);
1590 file_data_unref(fd);
1593 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1598 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1599 g_hash_table_remove(file_data_planned_change_hash, new_path);
1600 file_data_unref(ofd);
1603 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1605 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1610 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1612 gchar *old_path = fd->change->dest;
1614 fd->change->dest = g_strdup(dest_path);
1615 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1619 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1621 const gchar *extension = extension_from_path(fd->change->source);
1622 gchar *base = remove_extension_from_path(dest_path);
1623 gchar *old_path = fd->change->dest;
1625 fd->change->dest = g_strconcat(base, extension, NULL);
1626 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1632 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1635 gchar *dest_path_full = NULL;
1637 if (fd->parent) fd = fd->parent;
1641 dest_path = fd->path;
1643 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1645 gchar *dir = remove_level_from_path(fd->path);
1647 dest_path_full = g_build_filename(dir, dest_path, NULL);
1649 dest_path = dest_path_full;
1651 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1653 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1654 dest_path = dest_path_full;
1657 file_data_update_ci_dest(fd, dest_path);
1659 work = fd->sidecar_files;
1662 FileData *sfd = work->data;
1664 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1668 g_free(dest_path_full);
1671 static gboolean file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1673 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1674 file_data_sc_update_ci(fd, dest_path);
1678 gboolean file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1680 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1683 gboolean file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1685 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1688 gboolean file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1690 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1693 gboolean file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1695 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1698 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1700 gboolean (*func)(FileData *, const gchar *))
1703 gboolean ret = TRUE;
1708 FileData *fd = work->data;
1710 if (!func(fd, dest)) ret = FALSE;
1717 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1719 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1722 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1724 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1727 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1729 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1734 * verify source and dest paths - dest image exists, etc.
1735 * it should detect all possible problems with the planned operation
1738 gint file_data_verify_ci(FileData *fd)
1740 gint ret = CHANGE_OK;
1745 DEBUG_1("Change checked: no change info: %s", fd->path);
1749 if (!isname(fd->path))
1751 /* this probably should not happen */
1752 ret |= CHANGE_NO_SRC;
1753 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1757 dir = remove_level_from_path(fd->path);
1759 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1760 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1761 !access_file(fd->path, R_OK))
1763 ret |= CHANGE_NO_READ_PERM;
1764 DEBUG_1("Change checked: no read permission: %s", fd->path);
1766 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1767 !access_file(dir, W_OK))
1769 ret |= CHANGE_NO_WRITE_PERM_DIR;
1770 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1772 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1773 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1774 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1775 !access_file(fd->path, W_OK))
1777 ret |= CHANGE_WARN_NO_WRITE_PERM;
1778 DEBUG_1("Change checked: no write permission: %s", fd->path);
1780 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1781 - that means that there are no hard errors and warnings can be disabled
1782 - the destination is determined during the check
1784 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1786 /* determine destination file */
1787 gboolean have_dest = FALSE;
1788 gchar *dest_dir = NULL;
1790 if (options->metadata.save_in_image_file)
1792 if (file_data_can_write_directly(fd))
1794 /* we can write the file directly */
1795 if (access_file(fd->path, W_OK))
1801 if (options->metadata.warn_on_write_problems)
1803 ret |= CHANGE_WARN_NO_WRITE_PERM;
1804 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1808 else if (file_data_can_write_sidecar(fd))
1810 /* we can write sidecar */
1811 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1812 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1814 file_data_update_ci_dest(fd, sidecar);
1819 if (options->metadata.warn_on_write_problems)
1821 ret |= CHANGE_WARN_NO_WRITE_PERM;
1822 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1831 /* write private metadata file under ~/.geeqie */
1833 /* If an existing metadata file exists, we will try writing to
1834 * it's location regardless of the user's preference.
1836 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1837 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1839 if (metadata_path && !access_file(metadata_path, W_OK))
1841 g_free(metadata_path);
1842 metadata_path = NULL;
1849 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1850 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1852 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1854 metadata_path = g_build_filename(dest_dir, filename, NULL);
1858 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1860 file_data_update_ci_dest(fd, metadata_path);
1865 ret |= CHANGE_NO_WRITE_PERM_DEST;
1866 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1868 g_free(metadata_path);
1873 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1878 same = (strcmp(fd->path, fd->change->dest) == 0);
1882 const gchar *dest_ext = extension_from_path(fd->change->dest);
1883 if (!dest_ext) dest_ext = "";
1885 if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
1887 ret |= CHANGE_WARN_CHANGED_EXT;
1888 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1893 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1895 ret |= CHANGE_WARN_SAME;
1896 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1900 dest_dir = remove_level_from_path(fd->change->dest);
1902 if (!isdir(dest_dir))
1904 ret |= CHANGE_NO_DEST_DIR;
1905 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1907 else if (!access_file(dest_dir, W_OK))
1909 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1910 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1914 if (isfile(fd->change->dest))
1916 if (!access_file(fd->change->dest, W_OK))
1918 ret |= CHANGE_NO_WRITE_PERM_DEST;
1919 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1923 ret |= CHANGE_WARN_DEST_EXISTS;
1924 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1927 else if (isdir(fd->change->dest))
1929 ret |= CHANGE_DEST_EXISTS;
1930 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1937 fd->change->error = ret;
1938 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1945 gint file_data_sc_verify_ci(FileData *fd)
1950 ret = file_data_verify_ci(fd);
1952 work = fd->sidecar_files;
1955 FileData *sfd = work->data;
1957 ret |= file_data_verify_ci(sfd);
1964 gchar *file_data_get_error_string(gint error)
1966 GString *result = g_string_new("");
1968 if (error & CHANGE_NO_SRC)
1970 if (result->len > 0) g_string_append(result, ", ");
1971 g_string_append(result, _("file or directory does not exist"));
1974 if (error & CHANGE_DEST_EXISTS)
1976 if (result->len > 0) g_string_append(result, ", ");
1977 g_string_append(result, _("destination already exists"));
1980 if (error & CHANGE_NO_WRITE_PERM_DEST)
1982 if (result->len > 0) g_string_append(result, ", ");
1983 g_string_append(result, _("destination can't be overwritten"));
1986 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1988 if (result->len > 0) g_string_append(result, ", ");
1989 g_string_append(result, _("destination directory is not writable"));
1992 if (error & CHANGE_NO_DEST_DIR)
1994 if (result->len > 0) g_string_append(result, ", ");
1995 g_string_append(result, _("destination directory does not exist"));
1998 if (error & CHANGE_NO_WRITE_PERM_DIR)
2000 if (result->len > 0) g_string_append(result, ", ");
2001 g_string_append(result, _("source directory is not writable"));
2004 if (error & CHANGE_NO_READ_PERM)
2006 if (result->len > 0) g_string_append(result, ", ");
2007 g_string_append(result, _("no read permission"));
2010 if (error & CHANGE_WARN_NO_WRITE_PERM)
2012 if (result->len > 0) g_string_append(result, ", ");
2013 g_string_append(result, _("file is readonly"));
2016 if (error & CHANGE_WARN_DEST_EXISTS)
2018 if (result->len > 0) g_string_append(result, ", ");
2019 g_string_append(result, _("destination already exists and will be overwritten"));
2022 if (error & CHANGE_WARN_SAME)
2024 if (result->len > 0) g_string_append(result, ", ");
2025 g_string_append(result, _("source and destination are the same"));
2028 if (error & CHANGE_WARN_CHANGED_EXT)
2030 if (result->len > 0) g_string_append(result, ", ");
2031 g_string_append(result, _("source and destination have different extension"));
2034 return g_string_free(result, FALSE);
2037 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2040 gint all_errors = 0;
2041 gint common_errors = ~0;
2046 if (!list) return 0;
2048 num = g_list_length(list);
2049 errors = g_new(int, num);
2060 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2061 all_errors |= error;
2062 common_errors &= error;
2069 if (desc && all_errors)
2072 GString *result = g_string_new("");
2076 gchar *str = file_data_get_error_string(common_errors);
2077 g_string_append(result, str);
2078 g_string_append(result, "\n");
2092 error = errors[i] & ~common_errors;
2096 gchar *str = file_data_get_error_string(error);
2097 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2102 *desc = g_string_free(result, FALSE);
2111 * perform the change described by FileFataChangeInfo
2112 * it is used for internal operations,
2113 * this function actually operates with files on the filesystem
2114 * it should implement safe delete
2117 static gboolean file_data_perform_move(FileData *fd)
2119 g_assert(!strcmp(fd->change->source, fd->path));
2120 return move_file(fd->change->source, fd->change->dest);
2123 static gboolean file_data_perform_copy(FileData *fd)
2125 g_assert(!strcmp(fd->change->source, fd->path));
2126 return copy_file(fd->change->source, fd->change->dest);
2129 static gboolean file_data_perform_delete(FileData *fd)
2131 if (isdir(fd->path) && !islink(fd->path))
2132 return rmdir_utf8(fd->path);
2134 if (options->file_ops.safe_delete_enable)
2135 return file_util_safe_unlink(fd->path);
2137 return unlink_file(fd->path);
2140 gboolean file_data_perform_ci(FileData *fd)
2142 FileDataChangeType type = fd->change->type;
2146 case FILEDATA_CHANGE_MOVE:
2147 return file_data_perform_move(fd);
2148 case FILEDATA_CHANGE_COPY:
2149 return file_data_perform_copy(fd);
2150 case FILEDATA_CHANGE_RENAME:
2151 return file_data_perform_move(fd); /* the same as move */
2152 case FILEDATA_CHANGE_DELETE:
2153 return file_data_perform_delete(fd);
2154 case FILEDATA_CHANGE_WRITE_METADATA:
2155 return metadata_write_perform(fd);
2156 case FILEDATA_CHANGE_UNSPECIFIED:
2157 /* nothing to do here */
2165 gboolean file_data_sc_perform_ci(FileData *fd)
2168 gboolean ret = TRUE;
2169 FileDataChangeType type = fd->change->type;
2171 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2173 work = fd->sidecar_files;
2176 FileData *sfd = work->data;
2178 if (!file_data_perform_ci(sfd)) ret = FALSE;
2182 if (!file_data_perform_ci(fd)) ret = FALSE;
2188 * updates FileData structure according to FileDataChangeInfo
2191 gboolean file_data_apply_ci(FileData *fd)
2193 FileDataChangeType type = fd->change->type;
2196 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2198 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2199 file_data_planned_change_remove(fd);
2201 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2203 /* this change overwrites another file which is already known to other modules
2204 renaming fd would create duplicate FileData structure
2205 the best thing we can do is nothing
2206 FIXME: maybe we could copy stuff like marks
2208 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2212 file_data_set_path(fd, fd->change->dest);
2215 file_data_increment_version(fd);
2216 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2221 gboolean file_data_sc_apply_ci(FileData *fd)
2224 FileDataChangeType type = fd->change->type;
2226 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2228 work = fd->sidecar_files;
2231 FileData *sfd = work->data;
2233 file_data_apply_ci(sfd);
2237 file_data_apply_ci(fd);
2243 * notify other modules about the change described by FileFataChangeInfo
2246 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2247 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2248 implementation in view_file_list.c */
2253 typedef struct _NotifyData NotifyData;
2255 struct _NotifyData {
2256 FileDataNotifyFunc func;
2258 NotifyPriority priority;
2261 static GList *notify_func_list = NULL;
2263 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2265 NotifyData *nda = (NotifyData *)a;
2266 NotifyData *ndb = (NotifyData *)b;
2268 if (nda->priority < ndb->priority) return -1;
2269 if (nda->priority > ndb->priority) return 1;
2273 gboolean file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2277 nd = g_new(NotifyData, 1);
2280 nd->priority = priority;
2282 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2283 DEBUG_1("Notify func registered: %p", nd);
2288 gboolean file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2290 GList *work = notify_func_list;
2294 NotifyData *nd = (NotifyData *)work->data;
2296 if (nd->func == func && nd->data == data)
2298 notify_func_list = g_list_delete_link(notify_func_list, work);
2300 DEBUG_1("Notify func unregistered: %p", nd);
2310 void file_data_send_notification(FileData *fd, NotifyType type)
2312 GList *work = notify_func_list;
2316 NotifyData *nd = (NotifyData *)work->data;
2318 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2319 nd->func(fd, type, nd->data);
2324 static GHashTable *file_data_monitor_pool = NULL;
2325 static gint realtime_monitor_id = -1;
2327 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2331 file_data_check_changed_files(fd);
2333 DEBUG_1("monitor %s", fd->path);
2336 static gboolean realtime_monitor_cb(gpointer data)
2338 if (!options->update_on_time_change) return TRUE;
2339 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2343 gboolean file_data_register_real_time_monitor(FileData *fd)
2349 if (!file_data_monitor_pool)
2350 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2352 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2354 DEBUG_1("Register realtime %d %s", count, fd->path);
2357 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2359 if (realtime_monitor_id == -1)
2361 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2367 gboolean file_data_unregister_real_time_monitor(FileData *fd)
2371 g_assert(file_data_monitor_pool);
2373 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2375 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2377 g_assert(count > 0);
2382 g_hash_table_remove(file_data_monitor_pool, fd);
2384 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2386 file_data_unref(fd);
2388 if (g_hash_table_size(file_data_monitor_pool) == 0)
2390 g_source_remove(realtime_monitor_id);
2391 realtime_monitor_id = -1;
2397 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */