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];
1094 gboolean file_data_get_mark(FileData *fd, gint n)
1096 gboolean valid = (fd->valid_marks & (1 << n));
1098 if (file_data_get_mark_func[n] && !valid)
1100 guint old = fd->marks;
1101 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1103 if (!value != !(fd->marks & (1 << n)))
1105 fd->marks = fd->marks ^ (1 << n);
1108 fd->valid_marks |= (1 << n);
1109 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1111 file_data_unref(fd);
1113 else if (!old && fd->marks)
1119 return !!(fd->marks & (1 << n));
1122 guint file_data_get_marks(FileData *fd)
1125 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1129 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1132 if (!value == !file_data_get_mark(fd, n)) return;
1134 if (file_data_set_mark_func[n])
1136 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1141 fd->marks = fd->marks ^ (1 << n);
1143 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1145 file_data_unref(fd);
1147 else if (!old && fd->marks)
1152 file_data_increment_version(fd);
1153 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1156 gboolean file_data_filter_marks(FileData *fd, guint filter)
1159 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1160 return ((fd->marks & filter) == filter);
1163 GList *file_data_filter_marks_list(GList *list, guint filter)
1170 FileData *fd = work->data;
1174 if (!file_data_filter_marks(fd, filter))
1176 list = g_list_remove_link(list, link);
1177 file_data_unref(fd);
1185 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1187 FileData *fd = value;
1188 file_data_increment_version(fd);
1189 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1192 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1194 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1196 file_data_get_mark_func[n] = get_mark_func;
1197 file_data_set_mark_func[n] = set_mark_func;
1198 file_data_mark_func_data[n] = data;
1202 /* this effectively changes all known files */
1203 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1209 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1211 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1212 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1213 if (data) *data = file_data_mark_func_data[n];
1216 gint file_data_get_user_orientation(FileData *fd)
1218 return fd->user_orientation;
1221 void file_data_set_user_orientation(FileData *fd, gint value)
1223 if (fd->user_orientation == value) return;
1225 fd->user_orientation = value;
1226 file_data_increment_version(fd);
1227 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1232 * file_data - operates on the given fd
1233 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1237 /* return list of sidecar file extensions in a string */
1238 gchar *file_data_sc_list_to_string(FileData *fd)
1241 GString *result = g_string_new("");
1243 work = fd->sidecar_files;
1246 FileData *sfd = work->data;
1248 result = g_string_append(result, "+ ");
1249 result = g_string_append(result, sfd->extension);
1251 if (work) result = g_string_append_c(result, ' ');
1254 return g_string_free(result, FALSE);
1260 * add FileDataChangeInfo (see typedefs.h) for the given operation
1261 * uses file_data_add_change_info
1263 * fails if the fd->change already exists - change operations can't run in parallel
1264 * fd->change_info works as a lock
1266 * dest can be NULL - in this case the current name is used for now, it will
1271 FileDataChangeInfo types:
1273 MOVE - path is changed, name may be changed too
1274 RENAME - path remains unchanged, name is changed
1275 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1276 sidecar names are changed too, extensions are not changed
1278 UPDATE - file size, date or grouping has been changed
1281 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1283 FileDataChangeInfo *fdci;
1285 if (fd->change) return FALSE;
1287 fdci = g_new0(FileDataChangeInfo, 1);
1292 fdci->source = g_strdup(src);
1294 fdci->source = g_strdup(fd->path);
1297 fdci->dest = g_strdup(dest);
1304 static void file_data_planned_change_remove(FileData *fd)
1306 if (file_data_planned_change_hash &&
1307 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1309 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1311 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1312 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1313 file_data_unref(fd);
1314 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1316 g_hash_table_destroy(file_data_planned_change_hash);
1317 file_data_planned_change_hash = NULL;
1318 DEBUG_1("planned change: empty");
1325 void file_data_free_ci(FileData *fd)
1327 FileDataChangeInfo *fdci = fd->change;
1331 file_data_planned_change_remove(fd);
1333 g_free(fdci->source);
1342 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1346 if (fd->parent) fd = fd->parent;
1348 if (fd->change) return FALSE;
1350 work = fd->sidecar_files;
1353 FileData *sfd = work->data;
1355 if (sfd->change) return FALSE;
1359 file_data_add_ci(fd, type, NULL, NULL);
1361 work = fd->sidecar_files;
1364 FileData *sfd = work->data;
1366 file_data_add_ci(sfd, type, NULL, NULL);
1373 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1377 if (fd->parent) fd = fd->parent;
1379 if (!fd->change || fd->change->type != type) return FALSE;
1381 work = fd->sidecar_files;
1384 FileData *sfd = work->data;
1386 if (!sfd->change || sfd->change->type != type) return FALSE;
1394 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1396 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1397 file_data_sc_update_ci_copy(fd, dest_path);
1401 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1403 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1404 file_data_sc_update_ci_move(fd, dest_path);
1408 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1410 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1411 file_data_sc_update_ci_rename(fd, dest_path);
1415 gboolean file_data_sc_add_ci_delete(FileData *fd)
1417 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1420 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1422 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1423 file_data_sc_update_ci_unspecified(fd, dest_path);
1427 gboolean file_data_add_ci_write_metadata(FileData *fd)
1429 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1432 void file_data_sc_free_ci(FileData *fd)
1436 if (fd->parent) fd = fd->parent;
1438 file_data_free_ci(fd);
1440 work = fd->sidecar_files;
1443 FileData *sfd = work->data;
1445 file_data_free_ci(sfd);
1450 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1453 gboolean ret = TRUE;
1458 FileData *fd = work->data;
1460 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1467 static void file_data_sc_revert_ci_list(GList *fd_list)
1474 FileData *fd = work->data;
1476 file_data_sc_free_ci(fd);
1481 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1488 FileData *fd = work->data;
1490 if (!func(fd, dest))
1492 file_data_sc_revert_ci_list(work->prev);
1501 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1503 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1506 gboolean file_data_sc_add_ci_move_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_move);
1511 gboolean file_data_sc_add_ci_rename_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_rename);
1516 gboolean file_data_sc_add_ci_unspecified_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_unspecified);
1521 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1524 gboolean ret = TRUE;
1529 FileData *fd = work->data;
1531 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1538 void file_data_free_ci_list(GList *fd_list)
1545 FileData *fd = work->data;
1547 file_data_free_ci(fd);
1552 void file_data_sc_free_ci_list(GList *fd_list)
1559 FileData *fd = work->data;
1561 file_data_sc_free_ci(fd);
1567 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1568 * fails if fd->change does not exist or the change type does not match
1571 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1573 FileDataChangeType type = fd->change->type;
1575 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1579 if (!file_data_planned_change_hash)
1580 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1582 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1584 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1585 g_hash_table_remove(file_data_planned_change_hash, old_path);
1586 file_data_unref(fd);
1589 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1594 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1595 g_hash_table_remove(file_data_planned_change_hash, new_path);
1596 file_data_unref(ofd);
1599 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1601 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1606 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1608 gchar *old_path = fd->change->dest;
1610 fd->change->dest = g_strdup(dest_path);
1611 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1615 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1617 const gchar *extension = extension_from_path(fd->change->source);
1618 gchar *base = remove_extension_from_path(dest_path);
1619 gchar *old_path = fd->change->dest;
1621 fd->change->dest = g_strconcat(base, extension, NULL);
1622 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1628 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1631 gchar *dest_path_full = NULL;
1633 if (fd->parent) fd = fd->parent;
1637 dest_path = fd->path;
1639 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1641 gchar *dir = remove_level_from_path(fd->path);
1643 dest_path_full = g_build_filename(dir, dest_path, NULL);
1645 dest_path = dest_path_full;
1647 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1649 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1650 dest_path = dest_path_full;
1653 file_data_update_ci_dest(fd, dest_path);
1655 work = fd->sidecar_files;
1658 FileData *sfd = work->data;
1660 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1664 g_free(dest_path_full);
1667 static gboolean file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1669 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1670 file_data_sc_update_ci(fd, dest_path);
1674 gboolean file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1676 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1679 gboolean file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1681 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1684 gboolean file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1686 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1689 gboolean file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1691 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1694 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1696 gboolean (*func)(FileData *, const gchar *))
1699 gboolean ret = TRUE;
1704 FileData *fd = work->data;
1706 if (!func(fd, dest)) ret = FALSE;
1713 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1715 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1718 gboolean file_data_sc_update_ci_copy_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_copy);
1723 gboolean file_data_sc_update_ci_unspecified_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_unspecified);
1730 * verify source and dest paths - dest image exists, etc.
1731 * it should detect all possible problems with the planned operation
1734 gint file_data_verify_ci(FileData *fd)
1736 gint ret = CHANGE_OK;
1741 DEBUG_1("Change checked: no change info: %s", fd->path);
1745 if (!isname(fd->path))
1747 /* this probably should not happen */
1748 ret |= CHANGE_NO_SRC;
1749 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1753 dir = remove_level_from_path(fd->path);
1755 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1756 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1757 !access_file(fd->path, R_OK))
1759 ret |= CHANGE_NO_READ_PERM;
1760 DEBUG_1("Change checked: no read permission: %s", fd->path);
1762 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1763 !access_file(dir, W_OK))
1765 ret |= CHANGE_NO_WRITE_PERM_DIR;
1766 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1768 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1769 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1770 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1771 !access_file(fd->path, W_OK))
1773 ret |= CHANGE_WARN_NO_WRITE_PERM;
1774 DEBUG_1("Change checked: no write permission: %s", fd->path);
1776 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1777 - that means that there are no hard errors and warnings can be disabled
1778 - the destination is determined during the check
1780 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1782 /* determine destination file */
1783 gboolean have_dest = FALSE;
1784 gchar *dest_dir = NULL;
1786 if (options->metadata.save_in_image_file)
1788 if (file_data_can_write_directly(fd))
1790 /* we can write the file directly */
1791 if (access_file(fd->path, W_OK))
1797 if (options->metadata.warn_on_write_problems)
1799 ret |= CHANGE_WARN_NO_WRITE_PERM;
1800 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1804 else if (file_data_can_write_sidecar(fd))
1806 /* we can write sidecar */
1807 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1808 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1810 file_data_update_ci_dest(fd, sidecar);
1815 if (options->metadata.warn_on_write_problems)
1817 ret |= CHANGE_WARN_NO_WRITE_PERM;
1818 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1827 /* write private metadata file under ~/.geeqie */
1829 /* If an existing metadata file exists, we will try writing to
1830 * it's location regardless of the user's preference.
1832 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1833 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1835 if (metadata_path && !access_file(metadata_path, W_OK))
1837 g_free(metadata_path);
1838 metadata_path = NULL;
1845 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1846 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1848 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1850 metadata_path = g_build_filename(dest_dir, filename, NULL);
1854 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1856 file_data_update_ci_dest(fd, metadata_path);
1861 ret |= CHANGE_NO_WRITE_PERM_DEST;
1862 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1864 g_free(metadata_path);
1869 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1874 same = (strcmp(fd->path, fd->change->dest) == 0);
1878 const gchar *dest_ext = extension_from_path(fd->change->dest);
1879 if (!dest_ext) dest_ext = "";
1881 if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
1883 ret |= CHANGE_WARN_CHANGED_EXT;
1884 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1889 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1891 ret |= CHANGE_WARN_SAME;
1892 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1896 dest_dir = remove_level_from_path(fd->change->dest);
1898 if (!isdir(dest_dir))
1900 ret |= CHANGE_NO_DEST_DIR;
1901 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1903 else if (!access_file(dest_dir, W_OK))
1905 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1906 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1910 if (isfile(fd->change->dest))
1912 if (!access_file(fd->change->dest, W_OK))
1914 ret |= CHANGE_NO_WRITE_PERM_DEST;
1915 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1919 ret |= CHANGE_WARN_DEST_EXISTS;
1920 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1923 else if (isdir(fd->change->dest))
1925 ret |= CHANGE_DEST_EXISTS;
1926 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1933 fd->change->error = ret;
1934 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1941 gint file_data_sc_verify_ci(FileData *fd)
1946 ret = file_data_verify_ci(fd);
1948 work = fd->sidecar_files;
1951 FileData *sfd = work->data;
1953 ret |= file_data_verify_ci(sfd);
1960 gchar *file_data_get_error_string(gint error)
1962 GString *result = g_string_new("");
1964 if (error & CHANGE_NO_SRC)
1966 if (result->len > 0) g_string_append(result, ", ");
1967 g_string_append(result, _("file or directory does not exist"));
1970 if (error & CHANGE_DEST_EXISTS)
1972 if (result->len > 0) g_string_append(result, ", ");
1973 g_string_append(result, _("destination already exists"));
1976 if (error & CHANGE_NO_WRITE_PERM_DEST)
1978 if (result->len > 0) g_string_append(result, ", ");
1979 g_string_append(result, _("destination can't be overwritten"));
1982 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1984 if (result->len > 0) g_string_append(result, ", ");
1985 g_string_append(result, _("destination directory is not writable"));
1988 if (error & CHANGE_NO_DEST_DIR)
1990 if (result->len > 0) g_string_append(result, ", ");
1991 g_string_append(result, _("destination directory does not exist"));
1994 if (error & CHANGE_NO_WRITE_PERM_DIR)
1996 if (result->len > 0) g_string_append(result, ", ");
1997 g_string_append(result, _("source directory is not writable"));
2000 if (error & CHANGE_NO_READ_PERM)
2002 if (result->len > 0) g_string_append(result, ", ");
2003 g_string_append(result, _("no read permission"));
2006 if (error & CHANGE_WARN_NO_WRITE_PERM)
2008 if (result->len > 0) g_string_append(result, ", ");
2009 g_string_append(result, _("file is readonly"));
2012 if (error & CHANGE_WARN_DEST_EXISTS)
2014 if (result->len > 0) g_string_append(result, ", ");
2015 g_string_append(result, _("destination already exists and will be overwritten"));
2018 if (error & CHANGE_WARN_SAME)
2020 if (result->len > 0) g_string_append(result, ", ");
2021 g_string_append(result, _("source and destination are the same"));
2024 if (error & CHANGE_WARN_CHANGED_EXT)
2026 if (result->len > 0) g_string_append(result, ", ");
2027 g_string_append(result, _("source and destination have different extension"));
2030 return g_string_free(result, FALSE);
2033 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2036 gint all_errors = 0;
2037 gint common_errors = ~0;
2042 if (!list) return 0;
2044 num = g_list_length(list);
2045 errors = g_new(int, num);
2056 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2057 all_errors |= error;
2058 common_errors &= error;
2065 if (desc && all_errors)
2068 GString *result = g_string_new("");
2072 gchar *str = file_data_get_error_string(common_errors);
2073 g_string_append(result, str);
2074 g_string_append(result, "\n");
2088 error = errors[i] & ~common_errors;
2092 gchar *str = file_data_get_error_string(error);
2093 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2098 *desc = g_string_free(result, FALSE);
2107 * perform the change described by FileFataChangeInfo
2108 * it is used for internal operations,
2109 * this function actually operates with files on the filesystem
2110 * it should implement safe delete
2113 static gboolean file_data_perform_move(FileData *fd)
2115 g_assert(!strcmp(fd->change->source, fd->path));
2116 return move_file(fd->change->source, fd->change->dest);
2119 static gboolean file_data_perform_copy(FileData *fd)
2121 g_assert(!strcmp(fd->change->source, fd->path));
2122 return copy_file(fd->change->source, fd->change->dest);
2125 static gboolean file_data_perform_delete(FileData *fd)
2127 if (isdir(fd->path) && !islink(fd->path))
2128 return rmdir_utf8(fd->path);
2130 if (options->file_ops.safe_delete_enable)
2131 return file_util_safe_unlink(fd->path);
2133 return unlink_file(fd->path);
2136 gboolean file_data_perform_ci(FileData *fd)
2138 FileDataChangeType type = fd->change->type;
2142 case FILEDATA_CHANGE_MOVE:
2143 return file_data_perform_move(fd);
2144 case FILEDATA_CHANGE_COPY:
2145 return file_data_perform_copy(fd);
2146 case FILEDATA_CHANGE_RENAME:
2147 return file_data_perform_move(fd); /* the same as move */
2148 case FILEDATA_CHANGE_DELETE:
2149 return file_data_perform_delete(fd);
2150 case FILEDATA_CHANGE_WRITE_METADATA:
2151 return metadata_write_perform(fd);
2152 case FILEDATA_CHANGE_UNSPECIFIED:
2153 /* nothing to do here */
2161 gboolean file_data_sc_perform_ci(FileData *fd)
2164 gboolean ret = TRUE;
2165 FileDataChangeType type = fd->change->type;
2167 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2169 work = fd->sidecar_files;
2172 FileData *sfd = work->data;
2174 if (!file_data_perform_ci(sfd)) ret = FALSE;
2178 if (!file_data_perform_ci(fd)) ret = FALSE;
2184 * updates FileData structure according to FileDataChangeInfo
2187 gboolean file_data_apply_ci(FileData *fd)
2189 FileDataChangeType type = fd->change->type;
2192 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2194 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2195 file_data_planned_change_remove(fd);
2197 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2199 /* this change overwrites another file which is already known to other modules
2200 renaming fd would create duplicate FileData structure
2201 the best thing we can do is nothing
2202 FIXME: maybe we could copy stuff like marks
2204 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2208 file_data_set_path(fd, fd->change->dest);
2211 file_data_increment_version(fd);
2212 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2217 gboolean file_data_sc_apply_ci(FileData *fd)
2220 FileDataChangeType type = fd->change->type;
2222 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2224 work = fd->sidecar_files;
2227 FileData *sfd = work->data;
2229 file_data_apply_ci(sfd);
2233 file_data_apply_ci(fd);
2239 * notify other modules about the change described by FileFataChangeInfo
2242 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2243 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2244 implementation in view_file_list.c */
2249 typedef struct _NotifyData NotifyData;
2251 struct _NotifyData {
2252 FileDataNotifyFunc func;
2254 NotifyPriority priority;
2257 static GList *notify_func_list = NULL;
2259 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2261 NotifyData *nda = (NotifyData *)a;
2262 NotifyData *ndb = (NotifyData *)b;
2264 if (nda->priority < ndb->priority) return -1;
2265 if (nda->priority > ndb->priority) return 1;
2269 gboolean file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2273 nd = g_new(NotifyData, 1);
2276 nd->priority = priority;
2278 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2279 DEBUG_1("Notify func registered: %p", nd);
2284 gboolean file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2286 GList *work = notify_func_list;
2290 NotifyData *nd = (NotifyData *)work->data;
2292 if (nd->func == func && nd->data == data)
2294 notify_func_list = g_list_delete_link(notify_func_list, work);
2296 DEBUG_1("Notify func unregistered: %p", nd);
2306 void file_data_send_notification(FileData *fd, NotifyType type)
2308 GList *work = notify_func_list;
2312 NotifyData *nd = (NotifyData *)work->data;
2314 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2315 nd->func(fd, type, nd->data);
2320 static GHashTable *file_data_monitor_pool = NULL;
2321 static gint realtime_monitor_id = -1;
2323 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2327 file_data_check_changed_files(fd);
2329 DEBUG_1("monitor %s", fd->path);
2332 static gboolean realtime_monitor_cb(gpointer data)
2334 if (!options->update_on_time_change) return TRUE;
2335 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2339 gboolean file_data_register_real_time_monitor(FileData *fd)
2345 if (!file_data_monitor_pool)
2346 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2348 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2350 DEBUG_1("Register realtime %d %s", count, fd->path);
2353 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2355 if (realtime_monitor_id == -1)
2357 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2363 gboolean file_data_unregister_real_time_monitor(FileData *fd)
2367 g_assert(file_data_monitor_pool);
2369 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2371 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2373 g_assert(count > 0);
2378 g_hash_table_remove(file_data_monitor_pool, fd);
2380 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2382 file_data_unref(fd);
2384 if (g_hash_table_size(file_data_monitor_pool) == 0)
2386 g_source_remove(realtime_monitor_id);
2387 realtime_monitor_id = -1;
2393 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */