4 * Copyright (C) 2008 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)
145 if (fd->parent) fd->parent->version++;
148 static void file_data_set_collate_keys(FileData *fd)
150 gchar *caseless_name;
152 caseless_name = g_utf8_casefold(fd->name, -1);
154 g_free(fd->collate_key_name);
155 g_free(fd->collate_key_name_nocase);
157 #if GLIB_CHECK_VERSION(2, 8, 0)
158 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
159 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
161 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
162 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
164 g_free(caseless_name);
167 static void file_data_set_path(FileData *fd, const gchar *path)
169 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
170 g_assert(file_data_pool);
174 if (fd->original_path)
176 g_hash_table_remove(file_data_pool, fd->original_path);
177 g_free(fd->original_path);
180 g_assert(!g_hash_table_lookup(file_data_pool, path));
182 fd->original_path = g_strdup(path);
183 g_hash_table_insert(file_data_pool, fd->original_path, fd);
185 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
187 fd->path = g_strdup(path);
189 fd->extension = fd->name + 1;
190 file_data_set_collate_keys(fd);
194 fd->path = g_strdup(path);
195 fd->name = filename_from_path(fd->path);
197 if (strcmp(fd->name, "..") == 0)
199 gchar *dir = remove_level_from_path(path);
201 fd->path = remove_level_from_path(dir);
204 fd->extension = fd->name + 2;
205 file_data_set_collate_keys(fd);
208 else if (strcmp(fd->name, ".") == 0)
211 fd->path = remove_level_from_path(path);
213 fd->extension = fd->name + 1;
214 file_data_set_collate_keys(fd);
218 fd->extension = extension_from_path(fd->path);
219 if (fd->extension == NULL)
220 fd->extension = fd->name + strlen(fd->name);
222 file_data_set_collate_keys(fd);
225 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
227 gboolean ret = FALSE;
230 if (fd->size != st->st_size ||
231 fd->date != st->st_mtime)
233 fd->size = st->st_size;
234 fd->date = st->st_mtime;
235 fd->mode = st->st_mode;
236 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
237 fd->thumb_pixbuf = NULL;
238 file_data_increment_version(fd);
239 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
243 work = fd->sidecar_files;
246 FileData *sfd = work->data;
250 if (!stat_utf8(sfd->path, &st))
254 file_data_disconnect_sidecar_file(fd, sfd);
259 ret |= file_data_check_changed_files_recursive(sfd, &st);
265 gboolean file_data_check_changed_files(FileData *fd)
267 gboolean ret = FALSE;
270 if (fd->parent) fd = fd->parent;
272 if (!stat_utf8(fd->path, &st))
275 FileData *sfd = NULL;
277 /* parent is missing, we have to rebuild whole group */
282 work = fd->sidecar_files;
288 file_data_disconnect_sidecar_file(fd, sfd);
290 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
291 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
295 ret |= file_data_check_changed_files_recursive(fd, &st);
301 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
305 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
308 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
310 fd = g_hash_table_lookup(file_data_pool, path_utf8);
316 if (!fd && file_data_planned_change_hash)
318 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
321 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
323 file_data_apply_ci(fd);
332 changed = file_data_check_changed_files(fd);
334 changed = file_data_check_changed_files_recursive(fd, st);
335 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
336 file_data_check_sidecars(fd);
337 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
342 fd = g_new0(FileData, 1);
346 fd->collate_key_name = NULL;
347 fd->collate_key_name_nocase = NULL;
348 fd->original_path = NULL;
350 fd->size = st->st_size;
351 fd->date = st->st_mtime;
352 fd->mode = st->st_mode;
353 fd->thumb_pixbuf = NULL;
354 fd->sidecar_files = NULL;
356 fd->magick = 0x12345678;
358 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
361 file_data_check_sidecars(fd);
366 static void file_data_check_sidecars(FileData *fd)
370 FileData *parent_fd = NULL;
373 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
376 base_len = fd->extension - fd->path;
377 fname = g_string_new_len(fd->path, base_len);
378 work = sidecar_ext_get_list();
382 /* check for possible sidecar files;
383 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
384 they have fd->ref set to 0 and file_data unref must chack and free them all together
385 (using fd->ref would cause loops and leaks)
389 gchar *ext = work->data;
393 if (strcmp(ext, fd->extension) == 0)
395 new_fd = fd; /* processing the original file */
400 g_string_truncate(fname, base_len);
401 g_string_append(fname, ext);
403 if (!stat_utf8(fname->str, &nst))
406 new_fd = file_data_new(fname->str, &nst, FALSE);
408 if (new_fd->disable_grouping)
410 file_data_unref(new_fd);
414 new_fd->ref--; /* do not use ref here */
418 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
420 file_data_merge_sidecar_files(parent_fd, new_fd);
422 g_string_free(fname, TRUE);
426 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
428 gchar *path_utf8 = path_to_utf8(path);
429 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
435 FileData *file_data_new_simple(const gchar *path_utf8)
439 if (!stat_utf8(path_utf8, &st))
445 return file_data_new(path_utf8, &st, TRUE);
448 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
450 sfd->parent = target;
451 if (!g_list_find(target->sidecar_files, sfd))
452 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
453 file_data_increment_version(sfd); /* increments both sfd and target */
458 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
462 file_data_add_sidecar_file(target, source);
464 work = source->sidecar_files;
467 FileData *sfd = work->data;
468 file_data_add_sidecar_file(target, sfd);
472 g_list_free(source->sidecar_files);
473 source->sidecar_files = NULL;
475 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
480 #ifdef DEBUG_FILEDATA
481 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
483 FileData *file_data_ref(FileData *fd)
486 if (fd == NULL) return NULL;
487 #ifdef DEBUG_FILEDATA
488 if (fd->magick != 0x12345678)
489 DEBUG_0("fd magick mismatch at %s:%d", file, line);
491 g_assert(fd->magick == 0x12345678);
494 #ifdef DEBUG_FILEDATA
495 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
497 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
502 static void file_data_free(FileData *fd)
504 g_assert(fd->magick == 0x12345678);
505 g_assert(fd->ref == 0);
507 g_hash_table_remove(file_data_pool, fd->original_path);
510 g_free(fd->original_path);
511 g_free(fd->collate_key_name);
512 g_free(fd->collate_key_name_nocase);
513 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
515 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
517 file_data_change_info_free(NULL, fd);
521 #ifdef DEBUG_FILEDATA
522 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
524 void file_data_unref(FileData *fd)
527 if (fd == NULL) return;
528 #ifdef DEBUG_FILEDATA
529 if (fd->magick != 0x12345678)
530 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
532 g_assert(fd->magick == 0x12345678);
535 #ifdef DEBUG_FILEDATA
536 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
539 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
544 FileData *parent = fd->parent ? fd->parent : fd;
549 work = parent->sidecar_files;
552 FileData *sfd = work->data;
558 /* none of parent/children is referenced, we can free everything */
560 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
562 work = parent->sidecar_files;
565 FileData *sfd = work->data;
570 g_list_free(parent->sidecar_files);
571 parent->sidecar_files = NULL;
573 file_data_free(parent);
577 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
579 sfd->parent = target;
580 g_assert(g_list_find(target->sidecar_files, sfd));
582 file_data_increment_version(sfd); /* increments both sfd and target */
584 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
596 /* disables / enables grouping for particular file, sends UPDATE notification */
597 void file_data_disable_grouping(FileData *fd, gboolean disable)
599 if (!fd->disable_grouping == !disable) return;
600 fd->disable_grouping = !!disable;
606 FileData *parent = file_data_ref(fd->parent);
607 file_data_disconnect_sidecar_file(parent, fd);
608 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
609 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
610 file_data_unref(parent);
612 else if (fd->sidecar_files)
614 GList *sidecar_files = filelist_copy(fd->sidecar_files);
615 GList *work = sidecar_files;
618 FileData *sfd = work->data;
620 file_data_disconnect_sidecar_file(fd, sfd);
621 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
623 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
624 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
625 filelist_free(sidecar_files);
630 file_data_check_sidecars(fd);
631 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
635 /* compare name without extension */
636 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
638 size_t len1 = fd1->extension - fd1->name;
639 size_t len2 = fd2->extension - fd2->name;
641 if (len1 < len2) return -1;
642 if (len1 > len2) return 1;
644 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
647 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
655 g_free(fdci->source);
664 static gboolean file_data_can_write_directly(FileData *fd)
666 return (filter_file_class(fd->extension, FORMAT_CLASS_IMAGE));
667 /* FIXME: detect what exiv2 really supports */
670 static gboolean file_data_can_write_sidecar(FileData *fd)
672 return (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE));
673 /* FIXME: detect what exiv2 really supports */
676 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
678 gchar *sidecar_path = NULL;
680 if (!file_data_can_write_sidecar(fd)) return NULL;
682 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
685 FileData *sfd = work->data;
687 if (strcasecmp(sfd->extension, ".xmp") == 0)
689 sidecar_path = g_strdup(sfd->path);
694 if (!existing_only && !sidecar_path)
696 gchar *base = remove_extension_from_path(fd->path);
697 sidecar_path = g_strconcat(base, ".xmp", NULL);
706 *-----------------------------------------------------------------------------
707 * sidecar file info struct
708 *-----------------------------------------------------------------------------
713 static gint sidecar_file_priority(const gchar *path)
715 const gchar *extension = extension_from_path(path);
719 if (extension == NULL)
722 work = sidecar_ext_get_list();
725 gchar *ext = work->data;
728 if (strcmp(extension, ext) == 0) return i;
736 *-----------------------------------------------------------------------------
738 *-----------------------------------------------------------------------------
741 static SortType filelist_sort_method = SORT_NONE;
742 static gint filelist_sort_ascend = TRUE;
745 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
747 if (!filelist_sort_ascend)
754 switch (filelist_sort_method)
759 if (fa->size < fb->size) return -1;
760 if (fa->size > fb->size) return 1;
761 /* fall back to name */
764 if (fa->date < fb->date) return -1;
765 if (fa->date > fb->date) return 1;
766 /* fall back to name */
768 #ifdef HAVE_STRVERSCMP
770 return strverscmp(fa->name, fb->name);
777 if (options->file_sort.case_sensitive)
778 return strcmp(fa->collate_key_name, fb->collate_key_name);
780 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
783 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
785 filelist_sort_method = method;
786 filelist_sort_ascend = ascend;
787 return filelist_sort_compare_filedata(fa, fb);
790 static gint filelist_sort_file_cb(gpointer a, gpointer b)
792 return filelist_sort_compare_filedata(a, b);
795 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
797 filelist_sort_method = method;
798 filelist_sort_ascend = ascend;
799 return g_list_sort(list, cb);
802 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
804 filelist_sort_method = method;
805 filelist_sort_ascend = ascend;
806 return g_list_insert_sorted(list, data, cb);
809 GList *filelist_sort(GList *list, SortType method, gint ascend)
811 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
814 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
816 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
820 static GList *filelist_filter_out_sidecars(GList *flist)
823 GList *flist_filtered = NULL;
827 FileData *fd = work->data;
830 if (fd->parent) /* remove fd's that are children */
833 flist_filtered = g_list_prepend(flist_filtered, fd);
837 return flist_filtered;
840 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
847 gint (*stat_func)(const gchar *path, struct stat *buf);
849 g_assert(files || dirs);
851 if (files) *files = NULL;
852 if (dirs) *dirs = NULL;
854 pathl = path_from_utf8(dir_fd->path);
855 if (!pathl) return FALSE;
869 while ((dir = readdir(dp)) != NULL)
871 struct stat ent_sbuf;
872 const gchar *name = dir->d_name;
875 if (!options->file_filter.show_hidden_files && ishidden(name))
878 filepath = g_build_filename(pathl, name, NULL);
879 if (stat_func(filepath, &ent_sbuf) >= 0)
881 if (S_ISDIR(ent_sbuf.st_mode))
883 /* we ignore the .thumbnails dir for cleanliness */
885 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
886 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
887 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
888 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
890 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
895 if (files && filter_name_exists(name))
897 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
908 if (dirs) *dirs = dlist;
909 if (files) *files = filelist_filter_out_sidecars(flist);
914 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
916 return filelist_read_real(dir_fd, files, dirs, TRUE);
919 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
921 return filelist_read_real(dir_fd, files, dirs, FALSE);
924 void filelist_free(GList *list)
931 file_data_unref((FileData *)work->data);
939 GList *filelist_copy(GList *list)
941 GList *new_list = NULL;
952 new_list = g_list_prepend(new_list, file_data_ref(fd));
955 return g_list_reverse(new_list);
958 GList *filelist_from_path_list(GList *list)
960 GList *new_list = NULL;
971 new_list = g_list_prepend(new_list, file_data_new_simple(path));
974 return g_list_reverse(new_list);
977 GList *filelist_to_path_list(GList *list)
979 GList *new_list = NULL;
990 new_list = g_list_prepend(new_list, g_strdup(fd->path));
993 return g_list_reverse(new_list);
996 GList *filelist_filter(GList *list, gint is_dir_list)
1000 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1005 FileData *fd = (FileData *)(work->data);
1006 const gchar *name = fd->name;
1008 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1009 (!is_dir_list && !filter_name_exists(name)) ||
1010 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1011 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1015 list = g_list_remove_link(list, link);
1016 file_data_unref(fd);
1027 *-----------------------------------------------------------------------------
1028 * filelist recursive
1029 *-----------------------------------------------------------------------------
1032 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1034 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1037 GList *filelist_sort_path(GList *list)
1039 return g_list_sort(list, filelist_sort_path_cb);
1042 static void filelist_recursive_append(GList **list, GList *dirs)
1049 FileData *fd = (FileData *)(work->data);
1053 if (filelist_read(fd, &f, &d))
1055 f = filelist_filter(f, FALSE);
1056 f = filelist_sort_path(f);
1057 *list = g_list_concat(*list, f);
1059 d = filelist_filter(d, TRUE);
1060 d = filelist_sort_path(d);
1061 filelist_recursive_append(list, d);
1069 GList *filelist_recursive(FileData *dir_fd)
1074 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1075 list = filelist_filter(list, FALSE);
1076 list = filelist_sort_path(list);
1078 d = filelist_filter(d, TRUE);
1079 d = filelist_sort_path(d);
1080 filelist_recursive_append(&list, d);
1088 * marks and orientation
1091 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1092 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1093 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1095 gboolean file_data_get_mark(FileData *fd, gint n)
1097 if (file_data_get_mark_func[n])
1099 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1100 if (!value != !(fd->marks & (1 << n))) fd->marks = fd->marks ^ (1 << n);
1103 return !!(fd->marks & (1 << n));
1106 guint file_data_get_marks(FileData *fd)
1109 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1113 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1115 guint old = fd->marks;
1116 if (!value == !(fd->marks & (1 << n))) return;
1118 if (file_data_set_mark_func[n])
1120 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1123 fd->marks = fd->marks ^ (1 << n);
1125 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1127 file_data_unref(fd);
1129 else if (!old && fd->marks)
1134 file_data_increment_version(fd);
1135 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1138 gboolean file_data_filter_marks(FileData *fd, guint filter)
1141 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1142 return ((fd->marks & filter) == filter);
1145 GList *file_data_filter_marks_list(GList *list, guint filter)
1152 FileData *fd = work->data;
1156 if (!file_data_filter_marks(fd, filter))
1158 list = g_list_remove_link(list, link);
1159 file_data_unref(fd);
1167 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1169 FileData *fd = value;
1170 file_data_increment_version(fd);
1171 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1174 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1176 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1178 file_data_get_mark_func[n] = get_mark_func;
1179 file_data_set_mark_func[n] = set_mark_func;
1180 file_data_mark_func_data[n] = data;
1184 /* this effectively changes all known files */
1185 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1191 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1193 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1194 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1195 if (data) *data = file_data_mark_func_data[n];
1198 gint file_data_get_user_orientation(FileData *fd)
1200 return fd->user_orientation;
1203 void file_data_set_user_orientation(FileData *fd, gint value)
1205 if (fd->user_orientation == value) return;
1207 fd->user_orientation = value;
1208 file_data_increment_version(fd);
1209 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1214 * file_data - operates on the given fd
1215 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1219 /* return list of sidecar file extensions in a string */
1220 gchar *file_data_sc_list_to_string(FileData *fd)
1223 GString *result = g_string_new("");
1225 work = fd->sidecar_files;
1228 FileData *sfd = work->data;
1230 result = g_string_append(result, "+ ");
1231 result = g_string_append(result, sfd->extension);
1233 if (work) result = g_string_append_c(result, ' ');
1236 return g_string_free(result, FALSE);
1242 * add FileDataChangeInfo (see typedefs.h) for the given operation
1243 * uses file_data_add_change_info
1245 * fails if the fd->change already exists - change operations can't run in parallel
1246 * fd->change_info works as a lock
1248 * dest can be NULL - in this case the current name is used for now, it will
1253 FileDataChangeInfo types:
1255 MOVE - path is changed, name may be changed too
1256 RENAME - path remains unchanged, name is changed
1257 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1258 sidecar names are changed too, extensions are not changed
1260 UPDATE - file size, date or grouping has been changed
1263 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1265 FileDataChangeInfo *fdci;
1267 if (fd->change) return FALSE;
1269 fdci = g_new0(FileDataChangeInfo, 1);
1274 fdci->source = g_strdup(src);
1276 fdci->source = g_strdup(fd->path);
1279 fdci->dest = g_strdup(dest);
1286 static void file_data_planned_change_remove(FileData *fd)
1288 if (file_data_planned_change_hash &&
1289 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1291 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1293 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1294 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1295 file_data_unref(fd);
1296 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1298 g_hash_table_destroy(file_data_planned_change_hash);
1299 file_data_planned_change_hash = NULL;
1300 DEBUG_1("planned change: empty");
1307 void file_data_free_ci(FileData *fd)
1309 FileDataChangeInfo *fdci = fd->change;
1314 file_data_planned_change_remove(fd);
1316 g_free(fdci->source);
1325 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1329 if (fd->parent) fd = fd->parent;
1331 if (fd->change) return FALSE;
1333 work = fd->sidecar_files;
1336 FileData *sfd = work->data;
1338 if (sfd->change) return FALSE;
1342 file_data_add_ci(fd, type, NULL, NULL);
1344 work = fd->sidecar_files;
1347 FileData *sfd = work->data;
1349 file_data_add_ci(sfd, type, NULL, NULL);
1356 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1360 if (fd->parent) fd = fd->parent;
1362 if (!fd->change || fd->change->type != type) return FALSE;
1364 work = fd->sidecar_files;
1367 FileData *sfd = work->data;
1369 if (!sfd->change || sfd->change->type != type) return FALSE;
1377 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1379 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1380 file_data_sc_update_ci_copy(fd, dest_path);
1384 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1386 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1387 file_data_sc_update_ci_move(fd, dest_path);
1391 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1393 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1394 file_data_sc_update_ci_rename(fd, dest_path);
1398 gboolean file_data_sc_add_ci_delete(FileData *fd)
1400 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1403 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1405 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1406 file_data_sc_update_ci_unspecified(fd, dest_path);
1410 gboolean file_data_add_ci_write_metadata(FileData *fd)
1412 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1415 void file_data_sc_free_ci(FileData *fd)
1419 if (fd->parent) fd = fd->parent;
1421 file_data_free_ci(fd);
1423 work = fd->sidecar_files;
1426 FileData *sfd = work->data;
1428 file_data_free_ci(sfd);
1433 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1436 gboolean ret = TRUE;
1441 FileData *fd = work->data;
1443 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1450 static void file_data_sc_revert_ci_list(GList *fd_list)
1457 FileData *fd = work->data;
1459 file_data_sc_free_ci(fd);
1464 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1471 FileData *fd = work->data;
1473 if (!func(fd, dest))
1475 file_data_sc_revert_ci_list(work->prev);
1484 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1486 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1489 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1491 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1494 gboolean file_data_sc_add_ci_rename_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_rename);
1499 gboolean file_data_sc_add_ci_unspecified_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_unspecified);
1504 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1507 gboolean ret = TRUE;
1512 FileData *fd = work->data;
1514 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1521 void file_data_free_ci_list(GList *fd_list)
1528 FileData *fd = work->data;
1530 file_data_free_ci(fd);
1535 void file_data_sc_free_ci_list(GList *fd_list)
1542 FileData *fd = work->data;
1544 file_data_sc_free_ci(fd);
1550 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1551 * fails if fd->change does not exist or the change type does not match
1554 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1556 FileDataChangeType type = fd->change->type;
1558 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1562 if (!file_data_planned_change_hash)
1563 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1565 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1567 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1568 g_hash_table_remove(file_data_planned_change_hash, old_path);
1569 file_data_unref(fd);
1572 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1577 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1578 g_hash_table_remove(file_data_planned_change_hash, new_path);
1579 file_data_unref(ofd);
1582 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1584 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1589 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1591 gchar *old_path = fd->change->dest;
1593 fd->change->dest = g_strdup(dest_path);
1594 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1598 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1600 const gchar *extension = extension_from_path(fd->change->source);
1601 gchar *base = remove_extension_from_path(dest_path);
1602 gchar *old_path = fd->change->dest;
1604 fd->change->dest = g_strconcat(base, extension, NULL);
1605 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1611 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1614 gchar *dest_path_full = NULL;
1616 if (fd->parent) fd = fd->parent;
1620 dest_path = fd->path;
1622 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1624 gchar *dir = remove_level_from_path(fd->path);
1626 dest_path_full = g_build_filename(dir, dest_path, NULL);
1628 dest_path = dest_path_full;
1630 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1632 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1633 dest_path = dest_path_full;
1636 file_data_update_ci_dest(fd, dest_path);
1638 work = fd->sidecar_files;
1641 FileData *sfd = work->data;
1643 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1647 g_free(dest_path_full);
1650 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1652 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1653 file_data_sc_update_ci(fd, dest_path);
1657 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1659 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1662 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1664 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1667 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1669 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1672 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1674 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1677 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1679 gboolean (*func)(FileData *, const gchar *))
1682 gboolean ret = TRUE;
1687 FileData *fd = work->data;
1689 if (!func(fd, dest)) ret = FALSE;
1696 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1698 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1701 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1703 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1706 gboolean file_data_sc_update_ci_unspecified_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_unspecified);
1713 * verify source and dest paths - dest image exists, etc.
1714 * it should detect all possible problems with the planned operation
1717 gint file_data_verify_ci(FileData *fd)
1719 gint ret = CHANGE_OK;
1724 DEBUG_1("Change checked: no change info: %s", fd->path);
1728 if (!isname(fd->path))
1730 /* this probably should not happen */
1731 ret |= CHANGE_NO_SRC;
1732 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1736 dir = remove_level_from_path(fd->path);
1738 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1739 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1740 !access_file(fd->path, R_OK))
1742 ret |= CHANGE_NO_READ_PERM;
1743 DEBUG_1("Change checked: no read permission: %s", fd->path);
1745 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1746 !access_file(dir, W_OK))
1748 ret |= CHANGE_NO_WRITE_PERM_DIR;
1749 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1751 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1752 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1753 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1754 !access_file(fd->path, W_OK))
1756 ret |= CHANGE_WARN_NO_WRITE_PERM;
1757 DEBUG_1("Change checked: no write permission: %s", fd->path);
1759 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1760 - that means that there are no hard errors and warnings can be disabled
1761 - the destination is determined during the check
1763 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1765 /* determine destination file */
1766 gboolean have_dest = FALSE;
1767 gchar *dest_dir = NULL;
1769 if (options->metadata.save_in_image_file)
1771 if (file_data_can_write_directly(fd))
1773 /* we can write the file directly */
1774 if (access_file(fd->path, W_OK))
1780 if (options->metadata.warn_on_write_problems)
1782 ret |= CHANGE_WARN_NO_WRITE_PERM;
1783 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1787 else if (file_data_can_write_sidecar(fd))
1789 /* we can write sidecar */
1790 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1791 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1793 file_data_update_ci_dest(fd, sidecar);
1798 if (options->metadata.warn_on_write_problems)
1800 ret |= CHANGE_WARN_NO_WRITE_PERM;
1801 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1810 /* write private metadata file under ~/.geeqie */
1812 /* If an existing metadata file exists, we will try writing to
1813 * it's location regardless of the user's preference.
1815 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1816 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1818 if (metadata_path && !access_file(metadata_path, W_OK))
1820 g_free(metadata_path);
1821 metadata_path = NULL;
1828 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1829 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1831 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1833 metadata_path = g_build_filename(dest_dir, filename, NULL);
1837 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1839 file_data_update_ci_dest(fd, metadata_path);
1844 ret |= CHANGE_NO_WRITE_PERM_DEST;
1845 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1847 g_free(metadata_path);
1852 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1857 same = (strcmp(fd->path, fd->change->dest) == 0);
1861 const gchar *dest_ext = extension_from_path(fd->change->dest);
1862 if (!dest_ext) dest_ext = "";
1864 if (strcasecmp(fd->extension, dest_ext) != 0)
1866 ret |= CHANGE_WARN_CHANGED_EXT;
1867 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1872 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1874 ret |= CHANGE_WARN_SAME;
1875 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1879 dest_dir = remove_level_from_path(fd->change->dest);
1881 if (!isdir(dest_dir))
1883 ret |= CHANGE_NO_DEST_DIR;
1884 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1886 else if (!access_file(dest_dir, W_OK))
1888 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1889 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1893 if (isfile(fd->change->dest))
1895 if (!access_file(fd->change->dest, W_OK))
1897 ret |= CHANGE_NO_WRITE_PERM_DEST;
1898 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1902 ret |= CHANGE_WARN_DEST_EXISTS;
1903 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1906 else if (isdir(fd->change->dest))
1908 ret |= CHANGE_DEST_EXISTS;
1909 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1916 fd->change->error = ret;
1917 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1924 gint file_data_sc_verify_ci(FileData *fd)
1929 ret = file_data_verify_ci(fd);
1931 work = fd->sidecar_files;
1934 FileData *sfd = work->data;
1936 ret |= file_data_verify_ci(sfd);
1943 gchar *file_data_get_error_string(gint error)
1945 GString *result = g_string_new("");
1947 if (error & CHANGE_NO_SRC)
1949 if (result->len > 0) g_string_append(result, ", ");
1950 g_string_append(result, _("file or directory does not exist"));
1953 if (error & CHANGE_DEST_EXISTS)
1955 if (result->len > 0) g_string_append(result, ", ");
1956 g_string_append(result, _("destination already exists"));
1959 if (error & CHANGE_NO_WRITE_PERM_DEST)
1961 if (result->len > 0) g_string_append(result, ", ");
1962 g_string_append(result, _("destination can't be overwritten"));
1965 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1967 if (result->len > 0) g_string_append(result, ", ");
1968 g_string_append(result, _("destination directory is not writable"));
1971 if (error & CHANGE_NO_DEST_DIR)
1973 if (result->len > 0) g_string_append(result, ", ");
1974 g_string_append(result, _("destination directory does not exist"));
1977 if (error & CHANGE_NO_WRITE_PERM_DIR)
1979 if (result->len > 0) g_string_append(result, ", ");
1980 g_string_append(result, _("source directory is not writable"));
1983 if (error & CHANGE_NO_READ_PERM)
1985 if (result->len > 0) g_string_append(result, ", ");
1986 g_string_append(result, _("no read permission"));
1989 if (error & CHANGE_WARN_NO_WRITE_PERM)
1991 if (result->len > 0) g_string_append(result, ", ");
1992 g_string_append(result, _("file is readonly"));
1995 if (error & CHANGE_WARN_DEST_EXISTS)
1997 if (result->len > 0) g_string_append(result, ", ");
1998 g_string_append(result, _("destination already exists and will be overwritten"));
2001 if (error & CHANGE_WARN_SAME)
2003 if (result->len > 0) g_string_append(result, ", ");
2004 g_string_append(result, _("source and destination are the same"));
2007 if (error & CHANGE_WARN_CHANGED_EXT)
2009 if (result->len > 0) g_string_append(result, ", ");
2010 g_string_append(result, _("source and destination have different extension"));
2013 return g_string_free(result, FALSE);
2016 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2019 gint all_errors = 0;
2020 gint common_errors = ~0;
2025 if (!list) return 0;
2027 num = g_list_length(list);
2028 errors = g_new(int, num);
2039 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2040 all_errors |= error;
2041 common_errors &= error;
2048 if (desc && all_errors)
2051 GString *result = g_string_new("");
2055 gchar *str = file_data_get_error_string(common_errors);
2056 g_string_append(result, str);
2057 g_string_append(result, "\n");
2071 error = errors[i] & ~common_errors;
2075 gchar *str = file_data_get_error_string(error);
2076 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2081 *desc = g_string_free(result, FALSE);
2090 * perform the change described by FileFataChangeInfo
2091 * it is used for internal operations,
2092 * this function actually operates with files on the filesystem
2093 * it should implement safe delete
2096 static gboolean file_data_perform_move(FileData *fd)
2098 g_assert(!strcmp(fd->change->source, fd->path));
2099 return move_file(fd->change->source, fd->change->dest);
2102 static gboolean file_data_perform_copy(FileData *fd)
2104 g_assert(!strcmp(fd->change->source, fd->path));
2105 return copy_file(fd->change->source, fd->change->dest);
2108 static gboolean file_data_perform_delete(FileData *fd)
2110 if (isdir(fd->path) && !islink(fd->path))
2111 return rmdir_utf8(fd->path);
2113 if (options->file_ops.safe_delete_enable)
2114 return file_util_safe_unlink(fd->path);
2116 return unlink_file(fd->path);
2119 gboolean file_data_perform_ci(FileData *fd)
2121 FileDataChangeType type = fd->change->type;
2124 case FILEDATA_CHANGE_MOVE:
2125 return file_data_perform_move(fd);
2126 case FILEDATA_CHANGE_COPY:
2127 return file_data_perform_copy(fd);
2128 case FILEDATA_CHANGE_RENAME:
2129 return file_data_perform_move(fd); /* the same as move */
2130 case FILEDATA_CHANGE_DELETE:
2131 return file_data_perform_delete(fd);
2132 case FILEDATA_CHANGE_WRITE_METADATA:
2133 return metadata_write_perform(fd);
2134 case FILEDATA_CHANGE_UNSPECIFIED:
2135 /* nothing to do here */
2143 gboolean file_data_sc_perform_ci(FileData *fd)
2146 gboolean ret = TRUE;
2147 FileDataChangeType type = fd->change->type;
2149 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2151 work = fd->sidecar_files;
2154 FileData *sfd = work->data;
2156 if (!file_data_perform_ci(sfd)) ret = FALSE;
2160 if (!file_data_perform_ci(fd)) ret = FALSE;
2166 * updates FileData structure according to FileDataChangeInfo
2169 gint file_data_apply_ci(FileData *fd)
2171 FileDataChangeType type = fd->change->type;
2174 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2176 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2177 file_data_planned_change_remove(fd);
2179 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2181 /* this change overwrites another file which is already known to other modules
2182 renaming fd would create duplicate FileData structure
2183 the best thing we can do is nothing
2184 FIXME: maybe we could copy stuff like marks
2186 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2190 file_data_set_path(fd, fd->change->dest);
2193 file_data_increment_version(fd);
2194 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2199 gint file_data_sc_apply_ci(FileData *fd)
2202 FileDataChangeType type = fd->change->type;
2204 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2206 work = fd->sidecar_files;
2209 FileData *sfd = work->data;
2211 file_data_apply_ci(sfd);
2215 file_data_apply_ci(fd);
2221 * notify other modules about the change described by FileFataChangeInfo
2224 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2225 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2226 implementation in view_file_list.c */
2231 typedef struct _NotifyData NotifyData;
2233 struct _NotifyData {
2234 FileDataNotifyFunc func;
2236 NotifyPriority priority;
2239 static GList *notify_func_list = NULL;
2241 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2243 NotifyData *nda = (NotifyData *)a;
2244 NotifyData *ndb = (NotifyData *)b;
2246 if (nda->priority < ndb->priority) return -1;
2247 if (nda->priority > ndb->priority) return 1;
2251 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2255 nd = g_new(NotifyData, 1);
2258 nd->priority = priority;
2260 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2261 DEBUG_1("Notify func registered: %p", nd);
2266 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2268 GList *work = notify_func_list;
2272 NotifyData *nd = (NotifyData *)work->data;
2274 if (nd->func == func && nd->data == data)
2276 notify_func_list = g_list_delete_link(notify_func_list, work);
2278 DEBUG_1("Notify func unregistered: %p", nd);
2288 void file_data_send_notification(FileData *fd, NotifyType type)
2290 GList *work = notify_func_list;
2294 NotifyData *nd = (NotifyData *)work->data;
2296 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2297 nd->func(fd, type, nd->data);
2302 static GHashTable *file_data_monitor_pool = NULL;
2303 static gint realtime_monitor_id = -1;
2305 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2309 file_data_check_changed_files(fd);
2311 DEBUG_1("monitor %s", fd->path);
2314 static gboolean realtime_monitor_cb(gpointer data)
2316 if (!options->update_on_time_change) return TRUE;
2317 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2321 gint file_data_register_real_time_monitor(FileData *fd)
2327 if (!file_data_monitor_pool)
2328 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2330 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2332 DEBUG_1("Register realtime %d %s", count, fd->path);
2335 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2337 if (realtime_monitor_id == -1)
2339 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2345 gint file_data_unregister_real_time_monitor(FileData *fd)
2349 g_assert(file_data_monitor_pool);
2351 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2353 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2355 g_assert(count > 0);
2360 g_hash_table_remove(file_data_monitor_pool, fd);
2362 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2364 file_data_unref(fd);
2366 if (g_hash_table_size(file_data_monitor_pool) == 0)
2368 g_source_remove(realtime_monitor_id);
2369 realtime_monitor_id = -1;
2375 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */