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 filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gboolean follow_symlinks)
839 gint (*stat_func)(const gchar *path, struct stat *buf);
841 g_assert(files || dirs);
843 if (files) *files = NULL;
844 if (dirs) *dirs = NULL;
846 pathl = path_from_utf8(dir_fd->path);
847 if (!pathl) return FALSE;
861 while ((dir = readdir(dp)) != NULL)
863 struct stat ent_sbuf;
864 const gchar *name = dir->d_name;
867 if (!options->file_filter.show_hidden_files && ishidden(name))
870 filepath = g_build_filename(pathl, name, NULL);
871 if (stat_func(filepath, &ent_sbuf) >= 0)
873 if (S_ISDIR(ent_sbuf.st_mode))
875 /* we ignore the .thumbnails dir for cleanliness */
877 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
878 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
879 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
880 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
882 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
887 if (files && filter_name_exists(name))
889 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
900 if (dirs) *dirs = dlist;
901 if (files) *files = filelist_filter_out_sidecars(flist);
906 gboolean filelist_read(FileData *dir_fd, GList **files, GList **dirs)
908 return filelist_read_real(dir_fd, files, dirs, TRUE);
911 gboolean filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
913 return filelist_read_real(dir_fd, files, dirs, FALSE);
916 void filelist_free(GList *list)
923 file_data_unref((FileData *)work->data);
931 GList *filelist_copy(GList *list)
933 GList *new_list = NULL;
944 new_list = g_list_prepend(new_list, file_data_ref(fd));
947 return g_list_reverse(new_list);
950 GList *filelist_from_path_list(GList *list)
952 GList *new_list = NULL;
963 new_list = g_list_prepend(new_list, file_data_new_simple(path));
966 return g_list_reverse(new_list);
969 GList *filelist_to_path_list(GList *list)
971 GList *new_list = NULL;
982 new_list = g_list_prepend(new_list, g_strdup(fd->path));
985 return g_list_reverse(new_list);
988 GList *filelist_filter(GList *list, gboolean is_dir_list)
992 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
997 FileData *fd = (FileData *)(work->data);
998 const gchar *name = fd->name;
1000 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1001 (!is_dir_list && !filter_name_exists(name)) ||
1002 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1003 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1007 list = g_list_remove_link(list, link);
1008 file_data_unref(fd);
1019 *-----------------------------------------------------------------------------
1020 * filelist recursive
1021 *-----------------------------------------------------------------------------
1024 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1026 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1029 GList *filelist_sort_path(GList *list)
1031 return g_list_sort(list, filelist_sort_path_cb);
1034 static void filelist_recursive_append(GList **list, GList *dirs)
1041 FileData *fd = (FileData *)(work->data);
1045 if (filelist_read(fd, &f, &d))
1047 f = filelist_filter(f, FALSE);
1048 f = filelist_sort_path(f);
1049 *list = g_list_concat(*list, f);
1051 d = filelist_filter(d, TRUE);
1052 d = filelist_sort_path(d);
1053 filelist_recursive_append(list, d);
1061 GList *filelist_recursive(FileData *dir_fd)
1066 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1067 list = filelist_filter(list, FALSE);
1068 list = filelist_sort_path(list);
1070 d = filelist_filter(d, TRUE);
1071 d = filelist_sort_path(d);
1072 filelist_recursive_append(&list, d);
1080 * marks and orientation
1083 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1084 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1085 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1087 gboolean file_data_get_mark(FileData *fd, gint n)
1089 gboolean valid = (fd->valid_marks & (1 << n));
1091 if (file_data_get_mark_func[n] && !valid)
1093 guint old = fd->marks;
1094 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1096 if (!value != !(fd->marks & (1 << n)))
1098 fd->marks = fd->marks ^ (1 << n);
1101 fd->valid_marks |= (1 << n);
1102 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1104 file_data_unref(fd);
1106 else if (!old && fd->marks)
1112 return !!(fd->marks & (1 << n));
1115 guint file_data_get_marks(FileData *fd)
1118 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1122 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1125 if (!value == !file_data_get_mark(fd, n)) return;
1127 if (file_data_set_mark_func[n])
1129 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1134 fd->marks = fd->marks ^ (1 << n);
1136 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1138 file_data_unref(fd);
1140 else if (!old && fd->marks)
1145 file_data_increment_version(fd);
1146 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1149 gboolean file_data_filter_marks(FileData *fd, guint filter)
1152 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1153 return ((fd->marks & filter) == filter);
1156 GList *file_data_filter_marks_list(GList *list, guint filter)
1163 FileData *fd = work->data;
1167 if (!file_data_filter_marks(fd, filter))
1169 list = g_list_remove_link(list, link);
1170 file_data_unref(fd);
1178 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1180 FileData *fd = value;
1181 file_data_increment_version(fd);
1182 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1185 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1187 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1189 file_data_get_mark_func[n] = get_mark_func;
1190 file_data_set_mark_func[n] = set_mark_func;
1191 file_data_mark_func_data[n] = data;
1195 /* this effectively changes all known files */
1196 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1202 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1204 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1205 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1206 if (data) *data = file_data_mark_func_data[n];
1209 gint file_data_get_user_orientation(FileData *fd)
1211 return fd->user_orientation;
1214 void file_data_set_user_orientation(FileData *fd, gint value)
1216 if (fd->user_orientation == value) return;
1218 fd->user_orientation = value;
1219 file_data_increment_version(fd);
1220 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1225 * file_data - operates on the given fd
1226 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1230 /* return list of sidecar file extensions in a string */
1231 gchar *file_data_sc_list_to_string(FileData *fd)
1234 GString *result = g_string_new("");
1236 work = fd->sidecar_files;
1239 FileData *sfd = work->data;
1241 result = g_string_append(result, "+ ");
1242 result = g_string_append(result, sfd->extension);
1244 if (work) result = g_string_append_c(result, ' ');
1247 return g_string_free(result, FALSE);
1253 * add FileDataChangeInfo (see typedefs.h) for the given operation
1254 * uses file_data_add_change_info
1256 * fails if the fd->change already exists - change operations can't run in parallel
1257 * fd->change_info works as a lock
1259 * dest can be NULL - in this case the current name is used for now, it will
1264 FileDataChangeInfo types:
1266 MOVE - path is changed, name may be changed too
1267 RENAME - path remains unchanged, name is changed
1268 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1269 sidecar names are changed too, extensions are not changed
1271 UPDATE - file size, date or grouping has been changed
1274 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1276 FileDataChangeInfo *fdci;
1278 if (fd->change) return FALSE;
1280 fdci = g_new0(FileDataChangeInfo, 1);
1285 fdci->source = g_strdup(src);
1287 fdci->source = g_strdup(fd->path);
1290 fdci->dest = g_strdup(dest);
1297 static void file_data_planned_change_remove(FileData *fd)
1299 if (file_data_planned_change_hash &&
1300 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1302 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1304 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1305 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1306 file_data_unref(fd);
1307 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1309 g_hash_table_destroy(file_data_planned_change_hash);
1310 file_data_planned_change_hash = NULL;
1311 DEBUG_1("planned change: empty");
1318 void file_data_free_ci(FileData *fd)
1320 FileDataChangeInfo *fdci = fd->change;
1324 file_data_planned_change_remove(fd);
1326 g_free(fdci->source);
1335 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1339 if (fd->parent) fd = fd->parent;
1341 if (fd->change) return FALSE;
1343 work = fd->sidecar_files;
1346 FileData *sfd = work->data;
1348 if (sfd->change) return FALSE;
1352 file_data_add_ci(fd, type, NULL, NULL);
1354 work = fd->sidecar_files;
1357 FileData *sfd = work->data;
1359 file_data_add_ci(sfd, type, NULL, NULL);
1366 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1370 if (fd->parent) fd = fd->parent;
1372 if (!fd->change || fd->change->type != type) return FALSE;
1374 work = fd->sidecar_files;
1377 FileData *sfd = work->data;
1379 if (!sfd->change || sfd->change->type != type) return FALSE;
1387 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1389 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1390 file_data_sc_update_ci_copy(fd, dest_path);
1394 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1396 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1397 file_data_sc_update_ci_move(fd, dest_path);
1401 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1403 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1404 file_data_sc_update_ci_rename(fd, dest_path);
1408 gboolean file_data_sc_add_ci_delete(FileData *fd)
1410 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1413 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1415 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1416 file_data_sc_update_ci_unspecified(fd, dest_path);
1420 gboolean file_data_add_ci_write_metadata(FileData *fd)
1422 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1425 void file_data_sc_free_ci(FileData *fd)
1429 if (fd->parent) fd = fd->parent;
1431 file_data_free_ci(fd);
1433 work = fd->sidecar_files;
1436 FileData *sfd = work->data;
1438 file_data_free_ci(sfd);
1443 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1446 gboolean ret = TRUE;
1451 FileData *fd = work->data;
1453 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1460 static void file_data_sc_revert_ci_list(GList *fd_list)
1467 FileData *fd = work->data;
1469 file_data_sc_free_ci(fd);
1474 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1481 FileData *fd = work->data;
1483 if (!func(fd, dest))
1485 file_data_sc_revert_ci_list(work->prev);
1494 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1496 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1499 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1501 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1504 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1506 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1509 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1511 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1514 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1517 gboolean ret = TRUE;
1522 FileData *fd = work->data;
1524 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1531 void file_data_free_ci_list(GList *fd_list)
1538 FileData *fd = work->data;
1540 file_data_free_ci(fd);
1545 void file_data_sc_free_ci_list(GList *fd_list)
1552 FileData *fd = work->data;
1554 file_data_sc_free_ci(fd);
1560 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1561 * fails if fd->change does not exist or the change type does not match
1564 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1566 FileDataChangeType type = fd->change->type;
1568 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1572 if (!file_data_planned_change_hash)
1573 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1575 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1577 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1578 g_hash_table_remove(file_data_planned_change_hash, old_path);
1579 file_data_unref(fd);
1582 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1587 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1588 g_hash_table_remove(file_data_planned_change_hash, new_path);
1589 file_data_unref(ofd);
1592 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1594 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1599 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1601 gchar *old_path = fd->change->dest;
1603 fd->change->dest = g_strdup(dest_path);
1604 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1608 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1610 const gchar *extension = extension_from_path(fd->change->source);
1611 gchar *base = remove_extension_from_path(dest_path);
1612 gchar *old_path = fd->change->dest;
1614 fd->change->dest = g_strconcat(base, extension, NULL);
1615 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1621 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1624 gchar *dest_path_full = NULL;
1626 if (fd->parent) fd = fd->parent;
1630 dest_path = fd->path;
1632 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1634 gchar *dir = remove_level_from_path(fd->path);
1636 dest_path_full = g_build_filename(dir, dest_path, NULL);
1638 dest_path = dest_path_full;
1640 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1642 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1643 dest_path = dest_path_full;
1646 file_data_update_ci_dest(fd, dest_path);
1648 work = fd->sidecar_files;
1651 FileData *sfd = work->data;
1653 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1657 g_free(dest_path_full);
1660 static gboolean file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1662 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1663 file_data_sc_update_ci(fd, dest_path);
1667 gboolean file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1669 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1672 gboolean file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1674 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1677 gboolean file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1679 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1682 gboolean file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1684 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1687 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1689 gboolean (*func)(FileData *, const gchar *))
1692 gboolean ret = TRUE;
1697 FileData *fd = work->data;
1699 if (!func(fd, dest)) ret = FALSE;
1706 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1708 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1711 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1713 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1716 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1718 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1723 * verify source and dest paths - dest image exists, etc.
1724 * it should detect all possible problems with the planned operation
1727 gint file_data_verify_ci(FileData *fd)
1729 gint ret = CHANGE_OK;
1734 DEBUG_1("Change checked: no change info: %s", fd->path);
1738 if (!isname(fd->path))
1740 /* this probably should not happen */
1741 ret |= CHANGE_NO_SRC;
1742 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1746 dir = remove_level_from_path(fd->path);
1748 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1749 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1750 !access_file(fd->path, R_OK))
1752 ret |= CHANGE_NO_READ_PERM;
1753 DEBUG_1("Change checked: no read permission: %s", fd->path);
1755 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1756 !access_file(dir, W_OK))
1758 ret |= CHANGE_NO_WRITE_PERM_DIR;
1759 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1761 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1762 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1763 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1764 !access_file(fd->path, W_OK))
1766 ret |= CHANGE_WARN_NO_WRITE_PERM;
1767 DEBUG_1("Change checked: no write permission: %s", fd->path);
1769 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1770 - that means that there are no hard errors and warnings can be disabled
1771 - the destination is determined during the check
1773 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1775 /* determine destination file */
1776 gboolean have_dest = FALSE;
1777 gchar *dest_dir = NULL;
1779 if (options->metadata.save_in_image_file)
1781 if (file_data_can_write_directly(fd))
1783 /* we can write the file directly */
1784 if (access_file(fd->path, W_OK))
1790 if (options->metadata.warn_on_write_problems)
1792 ret |= CHANGE_WARN_NO_WRITE_PERM;
1793 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1797 else if (file_data_can_write_sidecar(fd))
1799 /* we can write sidecar */
1800 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1801 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1803 file_data_update_ci_dest(fd, sidecar);
1808 if (options->metadata.warn_on_write_problems)
1810 ret |= CHANGE_WARN_NO_WRITE_PERM;
1811 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1820 /* write private metadata file under ~/.geeqie */
1822 /* If an existing metadata file exists, we will try writing to
1823 * it's location regardless of the user's preference.
1825 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1826 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1828 if (metadata_path && !access_file(metadata_path, W_OK))
1830 g_free(metadata_path);
1831 metadata_path = NULL;
1838 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1839 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1841 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1843 metadata_path = g_build_filename(dest_dir, filename, NULL);
1847 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1849 file_data_update_ci_dest(fd, metadata_path);
1854 ret |= CHANGE_NO_WRITE_PERM_DEST;
1855 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1857 g_free(metadata_path);
1862 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1867 same = (strcmp(fd->path, fd->change->dest) == 0);
1871 const gchar *dest_ext = extension_from_path(fd->change->dest);
1872 if (!dest_ext) dest_ext = "";
1874 if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
1876 ret |= CHANGE_WARN_CHANGED_EXT;
1877 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1882 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1884 ret |= CHANGE_WARN_SAME;
1885 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1889 dest_dir = remove_level_from_path(fd->change->dest);
1891 if (!isdir(dest_dir))
1893 ret |= CHANGE_NO_DEST_DIR;
1894 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1896 else if (!access_file(dest_dir, W_OK))
1898 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1899 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1903 if (isfile(fd->change->dest))
1905 if (!access_file(fd->change->dest, W_OK))
1907 ret |= CHANGE_NO_WRITE_PERM_DEST;
1908 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1912 ret |= CHANGE_WARN_DEST_EXISTS;
1913 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1916 else if (isdir(fd->change->dest))
1918 ret |= CHANGE_DEST_EXISTS;
1919 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1926 fd->change->error = ret;
1927 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1934 gint file_data_sc_verify_ci(FileData *fd)
1939 ret = file_data_verify_ci(fd);
1941 work = fd->sidecar_files;
1944 FileData *sfd = work->data;
1946 ret |= file_data_verify_ci(sfd);
1953 gchar *file_data_get_error_string(gint error)
1955 GString *result = g_string_new("");
1957 if (error & CHANGE_NO_SRC)
1959 if (result->len > 0) g_string_append(result, ", ");
1960 g_string_append(result, _("file or directory does not exist"));
1963 if (error & CHANGE_DEST_EXISTS)
1965 if (result->len > 0) g_string_append(result, ", ");
1966 g_string_append(result, _("destination already exists"));
1969 if (error & CHANGE_NO_WRITE_PERM_DEST)
1971 if (result->len > 0) g_string_append(result, ", ");
1972 g_string_append(result, _("destination can't be overwritten"));
1975 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1977 if (result->len > 0) g_string_append(result, ", ");
1978 g_string_append(result, _("destination directory is not writable"));
1981 if (error & CHANGE_NO_DEST_DIR)
1983 if (result->len > 0) g_string_append(result, ", ");
1984 g_string_append(result, _("destination directory does not exist"));
1987 if (error & CHANGE_NO_WRITE_PERM_DIR)
1989 if (result->len > 0) g_string_append(result, ", ");
1990 g_string_append(result, _("source directory is not writable"));
1993 if (error & CHANGE_NO_READ_PERM)
1995 if (result->len > 0) g_string_append(result, ", ");
1996 g_string_append(result, _("no read permission"));
1999 if (error & CHANGE_WARN_NO_WRITE_PERM)
2001 if (result->len > 0) g_string_append(result, ", ");
2002 g_string_append(result, _("file is readonly"));
2005 if (error & CHANGE_WARN_DEST_EXISTS)
2007 if (result->len > 0) g_string_append(result, ", ");
2008 g_string_append(result, _("destination already exists and will be overwritten"));
2011 if (error & CHANGE_WARN_SAME)
2013 if (result->len > 0) g_string_append(result, ", ");
2014 g_string_append(result, _("source and destination are the same"));
2017 if (error & CHANGE_WARN_CHANGED_EXT)
2019 if (result->len > 0) g_string_append(result, ", ");
2020 g_string_append(result, _("source and destination have different extension"));
2023 return g_string_free(result, FALSE);
2026 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2029 gint all_errors = 0;
2030 gint common_errors = ~0;
2035 if (!list) return 0;
2037 num = g_list_length(list);
2038 errors = g_new(int, num);
2049 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2050 all_errors |= error;
2051 common_errors &= error;
2058 if (desc && all_errors)
2061 GString *result = g_string_new("");
2065 gchar *str = file_data_get_error_string(common_errors);
2066 g_string_append(result, str);
2067 g_string_append(result, "\n");
2081 error = errors[i] & ~common_errors;
2085 gchar *str = file_data_get_error_string(error);
2086 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2091 *desc = g_string_free(result, FALSE);
2100 * perform the change described by FileFataChangeInfo
2101 * it is used for internal operations,
2102 * this function actually operates with files on the filesystem
2103 * it should implement safe delete
2106 static gboolean file_data_perform_move(FileData *fd)
2108 g_assert(!strcmp(fd->change->source, fd->path));
2109 return move_file(fd->change->source, fd->change->dest);
2112 static gboolean file_data_perform_copy(FileData *fd)
2114 g_assert(!strcmp(fd->change->source, fd->path));
2115 return copy_file(fd->change->source, fd->change->dest);
2118 static gboolean file_data_perform_delete(FileData *fd)
2120 if (isdir(fd->path) && !islink(fd->path))
2121 return rmdir_utf8(fd->path);
2123 if (options->file_ops.safe_delete_enable)
2124 return file_util_safe_unlink(fd->path);
2126 return unlink_file(fd->path);
2129 gboolean file_data_perform_ci(FileData *fd)
2131 FileDataChangeType type = fd->change->type;
2135 case FILEDATA_CHANGE_MOVE:
2136 return file_data_perform_move(fd);
2137 case FILEDATA_CHANGE_COPY:
2138 return file_data_perform_copy(fd);
2139 case FILEDATA_CHANGE_RENAME:
2140 return file_data_perform_move(fd); /* the same as move */
2141 case FILEDATA_CHANGE_DELETE:
2142 return file_data_perform_delete(fd);
2143 case FILEDATA_CHANGE_WRITE_METADATA:
2144 return metadata_write_perform(fd);
2145 case FILEDATA_CHANGE_UNSPECIFIED:
2146 /* nothing to do here */
2154 gboolean file_data_sc_perform_ci(FileData *fd)
2157 gboolean ret = TRUE;
2158 FileDataChangeType type = fd->change->type;
2160 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2162 work = fd->sidecar_files;
2165 FileData *sfd = work->data;
2167 if (!file_data_perform_ci(sfd)) ret = FALSE;
2171 if (!file_data_perform_ci(fd)) ret = FALSE;
2177 * updates FileData structure according to FileDataChangeInfo
2180 gboolean file_data_apply_ci(FileData *fd)
2182 FileDataChangeType type = fd->change->type;
2185 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2187 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2188 file_data_planned_change_remove(fd);
2190 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2192 /* this change overwrites another file which is already known to other modules
2193 renaming fd would create duplicate FileData structure
2194 the best thing we can do is nothing
2195 FIXME: maybe we could copy stuff like marks
2197 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2201 file_data_set_path(fd, fd->change->dest);
2204 file_data_increment_version(fd);
2205 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2210 gboolean file_data_sc_apply_ci(FileData *fd)
2213 FileDataChangeType type = fd->change->type;
2215 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2217 work = fd->sidecar_files;
2220 FileData *sfd = work->data;
2222 file_data_apply_ci(sfd);
2226 file_data_apply_ci(fd);
2232 * notify other modules about the change described by FileFataChangeInfo
2235 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2236 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2237 implementation in view_file_list.c */
2242 typedef struct _NotifyData NotifyData;
2244 struct _NotifyData {
2245 FileDataNotifyFunc func;
2247 NotifyPriority priority;
2250 static GList *notify_func_list = NULL;
2252 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2254 NotifyData *nda = (NotifyData *)a;
2255 NotifyData *ndb = (NotifyData *)b;
2257 if (nda->priority < ndb->priority) return -1;
2258 if (nda->priority > ndb->priority) return 1;
2262 gboolean file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2266 nd = g_new(NotifyData, 1);
2269 nd->priority = priority;
2271 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2272 DEBUG_1("Notify func registered: %p", nd);
2277 gboolean file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2279 GList *work = notify_func_list;
2283 NotifyData *nd = (NotifyData *)work->data;
2285 if (nd->func == func && nd->data == data)
2287 notify_func_list = g_list_delete_link(notify_func_list, work);
2289 DEBUG_1("Notify func unregistered: %p", nd);
2299 void file_data_send_notification(FileData *fd, NotifyType type)
2301 GList *work = notify_func_list;
2305 NotifyData *nd = (NotifyData *)work->data;
2307 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2308 nd->func(fd, type, nd->data);
2313 static GHashTable *file_data_monitor_pool = NULL;
2314 static gint realtime_monitor_id = -1;
2316 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2320 file_data_check_changed_files(fd);
2322 DEBUG_1("monitor %s", fd->path);
2325 static gboolean realtime_monitor_cb(gpointer data)
2327 if (!options->update_on_time_change) return TRUE;
2328 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2332 gboolean file_data_register_real_time_monitor(FileData *fd)
2338 if (!file_data_monitor_pool)
2339 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2341 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2343 DEBUG_1("Register realtime %d %s", count, fd->path);
2346 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2348 if (realtime_monitor_id == -1)
2350 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2356 gboolean file_data_unregister_real_time_monitor(FileData *fd)
2360 g_assert(file_data_monitor_pool);
2362 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2364 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2366 g_assert(count > 0);
2371 g_hash_table_remove(file_data_monitor_pool, fd);
2373 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2375 file_data_unref(fd);
2377 if (g_hash_table_size(file_data_monitor_pool) == 0)
2379 g_source_remove(realtime_monitor_id);
2380 realtime_monitor_id = -1;
2386 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */