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)
225 fd->extension = fd->name + strlen(fd->name);
227 file_data_set_collate_keys(fd);
230 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
232 gboolean ret = FALSE;
235 if (fd->size != st->st_size ||
236 fd->date != st->st_mtime)
238 fd->size = st->st_size;
239 fd->date = st->st_mtime;
240 fd->mode = st->st_mode;
241 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
242 fd->thumb_pixbuf = NULL;
243 file_data_increment_version(fd);
244 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
248 work = fd->sidecar_files;
251 FileData *sfd = work->data;
255 if (!stat_utf8(sfd->path, &st))
259 file_data_disconnect_sidecar_file(fd, sfd);
264 ret |= file_data_check_changed_files_recursive(sfd, &st);
270 gboolean file_data_check_changed_files(FileData *fd)
272 gboolean ret = FALSE;
275 if (fd->parent) fd = fd->parent;
277 if (!stat_utf8(fd->path, &st))
280 FileData *sfd = NULL;
282 /* parent is missing, we have to rebuild whole group */
287 work = fd->sidecar_files;
293 file_data_disconnect_sidecar_file(fd, sfd);
295 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
296 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
300 ret |= file_data_check_changed_files_recursive(fd, &st);
306 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
310 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
313 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
315 fd = g_hash_table_lookup(file_data_pool, path_utf8);
321 if (!fd && file_data_planned_change_hash)
323 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
326 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
328 file_data_apply_ci(fd);
337 changed = file_data_check_changed_files(fd);
339 changed = file_data_check_changed_files_recursive(fd, st);
340 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
341 file_data_check_sidecars(fd);
342 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
347 fd = g_new0(FileData, 1);
351 fd->collate_key_name = NULL;
352 fd->collate_key_name_nocase = NULL;
353 fd->original_path = NULL;
355 fd->size = st->st_size;
356 fd->date = st->st_mtime;
357 fd->mode = st->st_mode;
358 fd->thumb_pixbuf = NULL;
359 fd->sidecar_files = NULL;
361 fd->magick = 0x12345678;
363 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
366 file_data_check_sidecars(fd);
371 static void file_data_check_sidecars(FileData *fd)
375 FileData *parent_fd = NULL;
378 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
381 base_len = fd->extension - fd->path;
382 fname = g_string_new_len(fd->path, base_len);
383 work = sidecar_ext_get_list();
387 /* check for possible sidecar files;
388 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
389 they have fd->ref set to 0 and file_data unref must chack and free them all together
390 (using fd->ref would cause loops and leaks)
394 gchar *ext = work->data;
398 if (strcasecmp(ext, fd->extension) == 0)
400 new_fd = fd; /* processing the original file */
405 g_string_truncate(fname, base_len);
407 if (!stat_utf8_case_insensitive_ext(fname, ext, &nst))
410 new_fd = file_data_new(fname->str, &nst, FALSE);
412 if (new_fd->disable_grouping)
414 file_data_unref(new_fd);
418 new_fd->ref--; /* do not use ref here */
422 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
424 file_data_merge_sidecar_files(parent_fd, new_fd);
426 g_string_free(fname, TRUE);
430 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
432 gchar *path_utf8 = path_to_utf8(path);
433 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
439 FileData *file_data_new_simple(const gchar *path_utf8)
443 if (!stat_utf8(path_utf8, &st))
449 return file_data_new(path_utf8, &st, TRUE);
452 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
454 sfd->parent = target;
455 if (!g_list_find(target->sidecar_files, sfd))
456 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
457 file_data_increment_version(sfd); /* increments both sfd and target */
462 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
466 file_data_add_sidecar_file(target, source);
468 work = source->sidecar_files;
471 FileData *sfd = work->data;
472 file_data_add_sidecar_file(target, sfd);
476 g_list_free(source->sidecar_files);
477 source->sidecar_files = NULL;
479 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
484 #ifdef DEBUG_FILEDATA
485 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
487 FileData *file_data_ref(FileData *fd)
490 if (fd == NULL) return NULL;
491 #ifdef DEBUG_FILEDATA
492 if (fd->magick != 0x12345678)
493 DEBUG_0("fd magick mismatch at %s:%d", file, line);
495 g_assert(fd->magick == 0x12345678);
498 #ifdef DEBUG_FILEDATA
499 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
501 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
506 static void file_data_free(FileData *fd)
508 g_assert(fd->magick == 0x12345678);
509 g_assert(fd->ref == 0);
511 g_hash_table_remove(file_data_pool, fd->original_path);
514 g_free(fd->original_path);
515 g_free(fd->collate_key_name);
516 g_free(fd->collate_key_name_nocase);
517 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
520 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
522 file_data_change_info_free(NULL, fd);
526 #ifdef DEBUG_FILEDATA
527 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
529 void file_data_unref(FileData *fd)
532 if (fd == NULL) return;
533 #ifdef DEBUG_FILEDATA
534 if (fd->magick != 0x12345678)
535 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
537 g_assert(fd->magick == 0x12345678);
540 #ifdef DEBUG_FILEDATA
541 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
544 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
549 FileData *parent = fd->parent ? fd->parent : fd;
554 work = parent->sidecar_files;
557 FileData *sfd = work->data;
563 /* none of parent/children is referenced, we can free everything */
565 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
567 work = parent->sidecar_files;
570 FileData *sfd = work->data;
575 g_list_free(parent->sidecar_files);
576 parent->sidecar_files = NULL;
578 file_data_free(parent);
582 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
584 sfd->parent = target;
585 g_assert(g_list_find(target->sidecar_files, sfd));
587 file_data_increment_version(sfd); /* increments both sfd and target */
589 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
601 /* disables / enables grouping for particular file, sends UPDATE notification */
602 void file_data_disable_grouping(FileData *fd, gboolean disable)
604 if (!fd->disable_grouping == !disable) return;
605 fd->disable_grouping = !!disable;
611 FileData *parent = file_data_ref(fd->parent);
612 file_data_disconnect_sidecar_file(parent, fd);
613 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
614 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
615 file_data_unref(parent);
617 else if (fd->sidecar_files)
619 GList *sidecar_files = filelist_copy(fd->sidecar_files);
620 GList *work = sidecar_files;
623 FileData *sfd = work->data;
625 file_data_disconnect_sidecar_file(fd, sfd);
626 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
628 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
629 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
630 filelist_free(sidecar_files);
635 file_data_check_sidecars(fd);
636 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
640 /* compare name without extension */
641 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
643 size_t len1 = fd1->extension - fd1->name;
644 size_t len2 = fd2->extension - fd2->name;
646 if (len1 < len2) return -1;
647 if (len1 > len2) return 1;
649 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
652 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
660 g_free(fdci->source);
669 static gboolean file_data_can_write_directly(FileData *fd)
671 return filter_name_is_writable(fd->extension);
674 static gboolean file_data_can_write_sidecar(FileData *fd)
676 return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
679 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
681 gchar *sidecar_path = NULL;
683 if (!file_data_can_write_sidecar(fd)) return NULL;
685 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
688 FileData *sfd = work->data;
690 if (strcasecmp(sfd->extension, ".xmp") == 0)
692 sidecar_path = g_strdup(sfd->path);
697 if (!existing_only && !sidecar_path)
699 gchar *base = remove_extension_from_path(fd->path);
700 sidecar_path = g_strconcat(base, ".xmp", NULL);
709 *-----------------------------------------------------------------------------
710 * sidecar file info struct
711 *-----------------------------------------------------------------------------
716 static gint sidecar_file_priority(const gchar *path)
718 const gchar *extension = extension_from_path(path);
722 if (extension == NULL)
725 work = sidecar_ext_get_list();
728 gchar *ext = work->data;
731 if (strcasecmp(extension, ext) == 0) return i;
739 *-----------------------------------------------------------------------------
741 *-----------------------------------------------------------------------------
744 static SortType filelist_sort_method = SORT_NONE;
745 static gint filelist_sort_ascend = TRUE;
748 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
750 if (!filelist_sort_ascend)
757 switch (filelist_sort_method)
762 if (fa->size < fb->size) return -1;
763 if (fa->size > fb->size) return 1;
764 /* fall back to name */
767 if (fa->date < fb->date) return -1;
768 if (fa->date > fb->date) return 1;
769 /* fall back to name */
771 #ifdef HAVE_STRVERSCMP
773 return strverscmp(fa->name, fb->name);
780 if (options->file_sort.case_sensitive)
781 return strcmp(fa->collate_key_name, fb->collate_key_name);
783 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
786 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
788 filelist_sort_method = method;
789 filelist_sort_ascend = ascend;
790 return filelist_sort_compare_filedata(fa, fb);
793 static gint filelist_sort_file_cb(gpointer a, gpointer b)
795 return filelist_sort_compare_filedata(a, b);
798 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
800 filelist_sort_method = method;
801 filelist_sort_ascend = ascend;
802 return g_list_sort(list, cb);
805 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
807 filelist_sort_method = method;
808 filelist_sort_ascend = ascend;
809 return g_list_insert_sorted(list, data, cb);
812 GList *filelist_sort(GList *list, SortType method, gint ascend)
814 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
817 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
819 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
823 static GList *filelist_filter_out_sidecars(GList *flist)
826 GList *flist_filtered = NULL;
830 FileData *fd = work->data;
833 if (fd->parent) /* remove fd's that are children */
836 flist_filtered = g_list_prepend(flist_filtered, fd);
840 return flist_filtered;
843 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
850 gint (*stat_func)(const gchar *path, struct stat *buf);
852 g_assert(files || dirs);
854 if (files) *files = NULL;
855 if (dirs) *dirs = NULL;
857 pathl = path_from_utf8(dir_fd->path);
858 if (!pathl) return FALSE;
872 while ((dir = readdir(dp)) != NULL)
874 struct stat ent_sbuf;
875 const gchar *name = dir->d_name;
878 if (!options->file_filter.show_hidden_files && ishidden(name))
881 filepath = g_build_filename(pathl, name, NULL);
882 if (stat_func(filepath, &ent_sbuf) >= 0)
884 if (S_ISDIR(ent_sbuf.st_mode))
886 /* we ignore the .thumbnails dir for cleanliness */
888 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
889 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
890 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
891 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
893 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
898 if (files && filter_name_exists(name))
900 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
911 if (dirs) *dirs = dlist;
912 if (files) *files = filelist_filter_out_sidecars(flist);
917 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
919 return filelist_read_real(dir_fd, files, dirs, TRUE);
922 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
924 return filelist_read_real(dir_fd, files, dirs, FALSE);
927 void filelist_free(GList *list)
934 file_data_unref((FileData *)work->data);
942 GList *filelist_copy(GList *list)
944 GList *new_list = NULL;
955 new_list = g_list_prepend(new_list, file_data_ref(fd));
958 return g_list_reverse(new_list);
961 GList *filelist_from_path_list(GList *list)
963 GList *new_list = NULL;
974 new_list = g_list_prepend(new_list, file_data_new_simple(path));
977 return g_list_reverse(new_list);
980 GList *filelist_to_path_list(GList *list)
982 GList *new_list = NULL;
993 new_list = g_list_prepend(new_list, g_strdup(fd->path));
996 return g_list_reverse(new_list);
999 GList *filelist_filter(GList *list, gint is_dir_list)
1003 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1008 FileData *fd = (FileData *)(work->data);
1009 const gchar *name = fd->name;
1011 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1012 (!is_dir_list && !filter_name_exists(name)) ||
1013 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1014 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1018 list = g_list_remove_link(list, link);
1019 file_data_unref(fd);
1030 *-----------------------------------------------------------------------------
1031 * filelist recursive
1032 *-----------------------------------------------------------------------------
1035 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1037 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1040 GList *filelist_sort_path(GList *list)
1042 return g_list_sort(list, filelist_sort_path_cb);
1045 static void filelist_recursive_append(GList **list, GList *dirs)
1052 FileData *fd = (FileData *)(work->data);
1056 if (filelist_read(fd, &f, &d))
1058 f = filelist_filter(f, FALSE);
1059 f = filelist_sort_path(f);
1060 *list = g_list_concat(*list, f);
1062 d = filelist_filter(d, TRUE);
1063 d = filelist_sort_path(d);
1064 filelist_recursive_append(list, d);
1072 GList *filelist_recursive(FileData *dir_fd)
1077 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1078 list = filelist_filter(list, FALSE);
1079 list = filelist_sort_path(list);
1081 d = filelist_filter(d, TRUE);
1082 d = filelist_sort_path(d);
1083 filelist_recursive_append(&list, d);
1091 * marks and orientation
1094 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1095 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1096 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1098 gboolean file_data_get_mark(FileData *fd, gint n)
1100 gboolean valid = (fd->valid_marks & (1 << n));
1101 if (file_data_get_mark_func[n] && !valid)
1103 guint old = fd->marks;
1104 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1105 if (!value != !(fd->marks & (1 << n)))
1107 fd->marks = fd->marks ^ (1 << n);
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)
1195 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1197 file_data_get_mark_func[n] = get_mark_func;
1198 file_data_set_mark_func[n] = set_mark_func;
1199 file_data_mark_func_data[n] = data;
1203 /* this effectively changes all known files */
1204 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1210 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1212 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1213 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1214 if (data) *data = file_data_mark_func_data[n];
1217 gint file_data_get_user_orientation(FileData *fd)
1219 return fd->user_orientation;
1222 void file_data_set_user_orientation(FileData *fd, gint value)
1224 if (fd->user_orientation == value) return;
1226 fd->user_orientation = value;
1227 file_data_increment_version(fd);
1228 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1233 * file_data - operates on the given fd
1234 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1238 /* return list of sidecar file extensions in a string */
1239 gchar *file_data_sc_list_to_string(FileData *fd)
1242 GString *result = g_string_new("");
1244 work = fd->sidecar_files;
1247 FileData *sfd = work->data;
1249 result = g_string_append(result, "+ ");
1250 result = g_string_append(result, sfd->extension);
1252 if (work) result = g_string_append_c(result, ' ');
1255 return g_string_free(result, FALSE);
1261 * add FileDataChangeInfo (see typedefs.h) for the given operation
1262 * uses file_data_add_change_info
1264 * fails if the fd->change already exists - change operations can't run in parallel
1265 * fd->change_info works as a lock
1267 * dest can be NULL - in this case the current name is used for now, it will
1272 FileDataChangeInfo types:
1274 MOVE - path is changed, name may be changed too
1275 RENAME - path remains unchanged, name is changed
1276 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1277 sidecar names are changed too, extensions are not changed
1279 UPDATE - file size, date or grouping has been changed
1282 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1284 FileDataChangeInfo *fdci;
1286 if (fd->change) return FALSE;
1288 fdci = g_new0(FileDataChangeInfo, 1);
1293 fdci->source = g_strdup(src);
1295 fdci->source = g_strdup(fd->path);
1298 fdci->dest = g_strdup(dest);
1305 static void file_data_planned_change_remove(FileData *fd)
1307 if (file_data_planned_change_hash &&
1308 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1310 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1312 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1313 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1314 file_data_unref(fd);
1315 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1317 g_hash_table_destroy(file_data_planned_change_hash);
1318 file_data_planned_change_hash = NULL;
1319 DEBUG_1("planned change: empty");
1326 void file_data_free_ci(FileData *fd)
1328 FileDataChangeInfo *fdci = fd->change;
1333 file_data_planned_change_remove(fd);
1335 g_free(fdci->source);
1344 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1348 if (fd->parent) fd = fd->parent;
1350 if (fd->change) return FALSE;
1352 work = fd->sidecar_files;
1355 FileData *sfd = work->data;
1357 if (sfd->change) return FALSE;
1361 file_data_add_ci(fd, type, NULL, NULL);
1363 work = fd->sidecar_files;
1366 FileData *sfd = work->data;
1368 file_data_add_ci(sfd, type, NULL, NULL);
1375 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1379 if (fd->parent) fd = fd->parent;
1381 if (!fd->change || fd->change->type != type) return FALSE;
1383 work = fd->sidecar_files;
1386 FileData *sfd = work->data;
1388 if (!sfd->change || sfd->change->type != type) return FALSE;
1396 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1398 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1399 file_data_sc_update_ci_copy(fd, dest_path);
1403 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1405 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1406 file_data_sc_update_ci_move(fd, dest_path);
1410 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1412 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1413 file_data_sc_update_ci_rename(fd, dest_path);
1417 gboolean file_data_sc_add_ci_delete(FileData *fd)
1419 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1422 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1424 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1425 file_data_sc_update_ci_unspecified(fd, dest_path);
1429 gboolean file_data_add_ci_write_metadata(FileData *fd)
1431 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1434 void file_data_sc_free_ci(FileData *fd)
1438 if (fd->parent) fd = fd->parent;
1440 file_data_free_ci(fd);
1442 work = fd->sidecar_files;
1445 FileData *sfd = work->data;
1447 file_data_free_ci(sfd);
1452 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1455 gboolean ret = TRUE;
1460 FileData *fd = work->data;
1462 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1469 static void file_data_sc_revert_ci_list(GList *fd_list)
1476 FileData *fd = work->data;
1478 file_data_sc_free_ci(fd);
1483 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1490 FileData *fd = work->data;
1492 if (!func(fd, dest))
1494 file_data_sc_revert_ci_list(work->prev);
1503 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1505 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1508 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1510 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1513 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1515 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1518 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1520 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1523 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1526 gboolean ret = TRUE;
1531 FileData *fd = work->data;
1533 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1540 void file_data_free_ci_list(GList *fd_list)
1547 FileData *fd = work->data;
1549 file_data_free_ci(fd);
1554 void file_data_sc_free_ci_list(GList *fd_list)
1561 FileData *fd = work->data;
1563 file_data_sc_free_ci(fd);
1569 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1570 * fails if fd->change does not exist or the change type does not match
1573 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1575 FileDataChangeType type = fd->change->type;
1577 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1581 if (!file_data_planned_change_hash)
1582 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1584 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1586 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1587 g_hash_table_remove(file_data_planned_change_hash, old_path);
1588 file_data_unref(fd);
1591 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1596 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1597 g_hash_table_remove(file_data_planned_change_hash, new_path);
1598 file_data_unref(ofd);
1601 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1603 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1608 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1610 gchar *old_path = fd->change->dest;
1612 fd->change->dest = g_strdup(dest_path);
1613 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1617 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1619 const gchar *extension = extension_from_path(fd->change->source);
1620 gchar *base = remove_extension_from_path(dest_path);
1621 gchar *old_path = fd->change->dest;
1623 fd->change->dest = g_strconcat(base, extension, NULL);
1624 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1630 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1633 gchar *dest_path_full = NULL;
1635 if (fd->parent) fd = fd->parent;
1639 dest_path = fd->path;
1641 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1643 gchar *dir = remove_level_from_path(fd->path);
1645 dest_path_full = g_build_filename(dir, dest_path, NULL);
1647 dest_path = dest_path_full;
1649 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1651 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1652 dest_path = dest_path_full;
1655 file_data_update_ci_dest(fd, dest_path);
1657 work = fd->sidecar_files;
1660 FileData *sfd = work->data;
1662 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1666 g_free(dest_path_full);
1669 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1671 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1672 file_data_sc_update_ci(fd, dest_path);
1676 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1678 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1681 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1683 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1686 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1688 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1691 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1693 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1696 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1698 gboolean (*func)(FileData *, const gchar *))
1701 gboolean ret = TRUE;
1706 FileData *fd = work->data;
1708 if (!func(fd, dest)) ret = FALSE;
1715 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1717 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1720 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1722 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1725 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1727 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1732 * verify source and dest paths - dest image exists, etc.
1733 * it should detect all possible problems with the planned operation
1736 gint file_data_verify_ci(FileData *fd)
1738 gint ret = CHANGE_OK;
1743 DEBUG_1("Change checked: no change info: %s", fd->path);
1747 if (!isname(fd->path))
1749 /* this probably should not happen */
1750 ret |= CHANGE_NO_SRC;
1751 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1755 dir = remove_level_from_path(fd->path);
1757 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1758 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1759 !access_file(fd->path, R_OK))
1761 ret |= CHANGE_NO_READ_PERM;
1762 DEBUG_1("Change checked: no read permission: %s", fd->path);
1764 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1765 !access_file(dir, W_OK))
1767 ret |= CHANGE_NO_WRITE_PERM_DIR;
1768 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1770 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1771 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1772 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1773 !access_file(fd->path, W_OK))
1775 ret |= CHANGE_WARN_NO_WRITE_PERM;
1776 DEBUG_1("Change checked: no write permission: %s", fd->path);
1778 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1779 - that means that there are no hard errors and warnings can be disabled
1780 - the destination is determined during the check
1782 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1784 /* determine destination file */
1785 gboolean have_dest = FALSE;
1786 gchar *dest_dir = NULL;
1788 if (options->metadata.save_in_image_file)
1790 if (file_data_can_write_directly(fd))
1792 /* we can write the file directly */
1793 if (access_file(fd->path, W_OK))
1799 if (options->metadata.warn_on_write_problems)
1801 ret |= CHANGE_WARN_NO_WRITE_PERM;
1802 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1806 else if (file_data_can_write_sidecar(fd))
1808 /* we can write sidecar */
1809 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1810 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1812 file_data_update_ci_dest(fd, sidecar);
1817 if (options->metadata.warn_on_write_problems)
1819 ret |= CHANGE_WARN_NO_WRITE_PERM;
1820 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1829 /* write private metadata file under ~/.geeqie */
1831 /* If an existing metadata file exists, we will try writing to
1832 * it's location regardless of the user's preference.
1834 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1835 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1837 if (metadata_path && !access_file(metadata_path, W_OK))
1839 g_free(metadata_path);
1840 metadata_path = NULL;
1847 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1848 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1850 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1852 metadata_path = g_build_filename(dest_dir, filename, NULL);
1856 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1858 file_data_update_ci_dest(fd, metadata_path);
1863 ret |= CHANGE_NO_WRITE_PERM_DEST;
1864 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1866 g_free(metadata_path);
1871 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1876 same = (strcmp(fd->path, fd->change->dest) == 0);
1880 const gchar *dest_ext = extension_from_path(fd->change->dest);
1881 if (!dest_ext) dest_ext = "";
1883 if (strcasecmp(fd->extension, dest_ext) != 0)
1885 ret |= CHANGE_WARN_CHANGED_EXT;
1886 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1891 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1893 ret |= CHANGE_WARN_SAME;
1894 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1898 dest_dir = remove_level_from_path(fd->change->dest);
1900 if (!isdir(dest_dir))
1902 ret |= CHANGE_NO_DEST_DIR;
1903 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1905 else if (!access_file(dest_dir, W_OK))
1907 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1908 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1912 if (isfile(fd->change->dest))
1914 if (!access_file(fd->change->dest, W_OK))
1916 ret |= CHANGE_NO_WRITE_PERM_DEST;
1917 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1921 ret |= CHANGE_WARN_DEST_EXISTS;
1922 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1925 else if (isdir(fd->change->dest))
1927 ret |= CHANGE_DEST_EXISTS;
1928 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1935 fd->change->error = ret;
1936 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1943 gint file_data_sc_verify_ci(FileData *fd)
1948 ret = file_data_verify_ci(fd);
1950 work = fd->sidecar_files;
1953 FileData *sfd = work->data;
1955 ret |= file_data_verify_ci(sfd);
1962 gchar *file_data_get_error_string(gint error)
1964 GString *result = g_string_new("");
1966 if (error & CHANGE_NO_SRC)
1968 if (result->len > 0) g_string_append(result, ", ");
1969 g_string_append(result, _("file or directory does not exist"));
1972 if (error & CHANGE_DEST_EXISTS)
1974 if (result->len > 0) g_string_append(result, ", ");
1975 g_string_append(result, _("destination already exists"));
1978 if (error & CHANGE_NO_WRITE_PERM_DEST)
1980 if (result->len > 0) g_string_append(result, ", ");
1981 g_string_append(result, _("destination can't be overwritten"));
1984 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1986 if (result->len > 0) g_string_append(result, ", ");
1987 g_string_append(result, _("destination directory is not writable"));
1990 if (error & CHANGE_NO_DEST_DIR)
1992 if (result->len > 0) g_string_append(result, ", ");
1993 g_string_append(result, _("destination directory does not exist"));
1996 if (error & CHANGE_NO_WRITE_PERM_DIR)
1998 if (result->len > 0) g_string_append(result, ", ");
1999 g_string_append(result, _("source directory is not writable"));
2002 if (error & CHANGE_NO_READ_PERM)
2004 if (result->len > 0) g_string_append(result, ", ");
2005 g_string_append(result, _("no read permission"));
2008 if (error & CHANGE_WARN_NO_WRITE_PERM)
2010 if (result->len > 0) g_string_append(result, ", ");
2011 g_string_append(result, _("file is readonly"));
2014 if (error & CHANGE_WARN_DEST_EXISTS)
2016 if (result->len > 0) g_string_append(result, ", ");
2017 g_string_append(result, _("destination already exists and will be overwritten"));
2020 if (error & CHANGE_WARN_SAME)
2022 if (result->len > 0) g_string_append(result, ", ");
2023 g_string_append(result, _("source and destination are the same"));
2026 if (error & CHANGE_WARN_CHANGED_EXT)
2028 if (result->len > 0) g_string_append(result, ", ");
2029 g_string_append(result, _("source and destination have different extension"));
2032 return g_string_free(result, FALSE);
2035 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2038 gint all_errors = 0;
2039 gint common_errors = ~0;
2044 if (!list) return 0;
2046 num = g_list_length(list);
2047 errors = g_new(int, num);
2058 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2059 all_errors |= error;
2060 common_errors &= error;
2067 if (desc && all_errors)
2070 GString *result = g_string_new("");
2074 gchar *str = file_data_get_error_string(common_errors);
2075 g_string_append(result, str);
2076 g_string_append(result, "\n");
2090 error = errors[i] & ~common_errors;
2094 gchar *str = file_data_get_error_string(error);
2095 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2100 *desc = g_string_free(result, FALSE);
2109 * perform the change described by FileFataChangeInfo
2110 * it is used for internal operations,
2111 * this function actually operates with files on the filesystem
2112 * it should implement safe delete
2115 static gboolean file_data_perform_move(FileData *fd)
2117 g_assert(!strcmp(fd->change->source, fd->path));
2118 return move_file(fd->change->source, fd->change->dest);
2121 static gboolean file_data_perform_copy(FileData *fd)
2123 g_assert(!strcmp(fd->change->source, fd->path));
2124 return copy_file(fd->change->source, fd->change->dest);
2127 static gboolean file_data_perform_delete(FileData *fd)
2129 if (isdir(fd->path) && !islink(fd->path))
2130 return rmdir_utf8(fd->path);
2132 if (options->file_ops.safe_delete_enable)
2133 return file_util_safe_unlink(fd->path);
2135 return unlink_file(fd->path);
2138 gboolean file_data_perform_ci(FileData *fd)
2140 FileDataChangeType type = fd->change->type;
2143 case FILEDATA_CHANGE_MOVE:
2144 return file_data_perform_move(fd);
2145 case FILEDATA_CHANGE_COPY:
2146 return file_data_perform_copy(fd);
2147 case FILEDATA_CHANGE_RENAME:
2148 return file_data_perform_move(fd); /* the same as move */
2149 case FILEDATA_CHANGE_DELETE:
2150 return file_data_perform_delete(fd);
2151 case FILEDATA_CHANGE_WRITE_METADATA:
2152 return metadata_write_perform(fd);
2153 case FILEDATA_CHANGE_UNSPECIFIED:
2154 /* nothing to do here */
2162 gboolean file_data_sc_perform_ci(FileData *fd)
2165 gboolean ret = TRUE;
2166 FileDataChangeType type = fd->change->type;
2168 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2170 work = fd->sidecar_files;
2173 FileData *sfd = work->data;
2175 if (!file_data_perform_ci(sfd)) ret = FALSE;
2179 if (!file_data_perform_ci(fd)) ret = FALSE;
2185 * updates FileData structure according to FileDataChangeInfo
2188 gint file_data_apply_ci(FileData *fd)
2190 FileDataChangeType type = fd->change->type;
2193 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2195 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2196 file_data_planned_change_remove(fd);
2198 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2200 /* this change overwrites another file which is already known to other modules
2201 renaming fd would create duplicate FileData structure
2202 the best thing we can do is nothing
2203 FIXME: maybe we could copy stuff like marks
2205 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2209 file_data_set_path(fd, fd->change->dest);
2212 file_data_increment_version(fd);
2213 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2218 gint file_data_sc_apply_ci(FileData *fd)
2221 FileDataChangeType type = fd->change->type;
2223 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2225 work = fd->sidecar_files;
2228 FileData *sfd = work->data;
2230 file_data_apply_ci(sfd);
2234 file_data_apply_ci(fd);
2240 * notify other modules about the change described by FileFataChangeInfo
2243 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2244 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2245 implementation in view_file_list.c */
2250 typedef struct _NotifyData NotifyData;
2252 struct _NotifyData {
2253 FileDataNotifyFunc func;
2255 NotifyPriority priority;
2258 static GList *notify_func_list = NULL;
2260 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2262 NotifyData *nda = (NotifyData *)a;
2263 NotifyData *ndb = (NotifyData *)b;
2265 if (nda->priority < ndb->priority) return -1;
2266 if (nda->priority > ndb->priority) return 1;
2270 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2274 nd = g_new(NotifyData, 1);
2277 nd->priority = priority;
2279 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2280 DEBUG_1("Notify func registered: %p", nd);
2285 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2287 GList *work = notify_func_list;
2291 NotifyData *nd = (NotifyData *)work->data;
2293 if (nd->func == func && nd->data == data)
2295 notify_func_list = g_list_delete_link(notify_func_list, work);
2297 DEBUG_1("Notify func unregistered: %p", nd);
2307 void file_data_send_notification(FileData *fd, NotifyType type)
2309 GList *work = notify_func_list;
2313 NotifyData *nd = (NotifyData *)work->data;
2315 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2316 nd->func(fd, type, nd->data);
2321 static GHashTable *file_data_monitor_pool = NULL;
2322 static gint realtime_monitor_id = -1;
2324 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2328 file_data_check_changed_files(fd);
2330 DEBUG_1("monitor %s", fd->path);
2333 static gboolean realtime_monitor_cb(gpointer data)
2335 if (!options->update_on_time_change) return TRUE;
2336 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2340 gint file_data_register_real_time_monitor(FileData *fd)
2346 if (!file_data_monitor_pool)
2347 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2349 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2351 DEBUG_1("Register realtime %d %s", count, fd->path);
2354 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2356 if (realtime_monitor_id == -1)
2358 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2364 gint file_data_unregister_real_time_monitor(FileData *fd)
2368 g_assert(file_data_monitor_pool);
2370 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2372 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2374 g_assert(count > 0);
2379 g_hash_table_remove(file_data_monitor_pool, fd);
2381 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2383 file_data_unref(fd);
2385 if (g_hash_table_size(file_data_monitor_pool) == 0)
2387 g_source_remove(realtime_monitor_id);
2388 realtime_monitor_id = -1;
2394 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */