4 * Copyright (C) 2008 - 2009 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
17 #include "filefilter.h"
19 #include "thumb_standard.h"
20 #include "ui_fileops.h"
25 static GHashTable *file_data_pool = NULL;
26 static GHashTable *file_data_planned_change_hash = NULL;
28 static gint sidecar_file_priority(const gchar *path);
32 *-----------------------------------------------------------------------------
33 * text conversion utils
34 *-----------------------------------------------------------------------------
37 gchar *text_from_size(gint64 size)
43 /* what I would like to use is printf("%'d", size)
44 * BUT: not supported on every libc :(
48 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
49 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
53 a = g_strdup_printf("%d", (guint)size);
59 b = g_new(gchar, l + n + 1);
84 gchar *text_from_size_abrev(gint64 size)
86 if (size < (gint64)1024)
88 return g_strdup_printf(_("%d bytes"), (gint)size);
90 if (size < (gint64)1048576)
92 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
94 if (size < (gint64)1073741824)
96 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
99 /* to avoid overflowing the gdouble, do division in two steps */
101 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
104 /* note: returned string is valid until next call to text_from_time() */
105 const gchar *text_from_time(time_t t)
107 static gchar *ret = NULL;
111 GError *error = NULL;
113 btime = localtime(&t);
115 /* the %x warning about 2 digit years is not an error */
116 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
117 if (buflen < 1) return "";
120 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
123 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
132 *-----------------------------------------------------------------------------
134 *-----------------------------------------------------------------------------
137 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
138 static void file_data_check_sidecars(FileData *fd);
139 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
142 void file_data_increment_version(FileData *fd)
148 fd->parent->version++;
149 fd->parent->valid_marks = 0;
153 static void file_data_set_collate_keys(FileData *fd)
155 gchar *caseless_name;
157 caseless_name = g_utf8_casefold(fd->name, -1);
159 g_free(fd->collate_key_name);
160 g_free(fd->collate_key_name_nocase);
162 #if GLIB_CHECK_VERSION(2, 8, 0)
163 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
164 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
166 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
167 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
169 g_free(caseless_name);
172 static void file_data_set_path(FileData *fd, const gchar *path)
174 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
175 g_assert(file_data_pool);
179 if (fd->original_path)
181 g_hash_table_remove(file_data_pool, fd->original_path);
182 g_free(fd->original_path);
185 g_assert(!g_hash_table_lookup(file_data_pool, path));
187 fd->original_path = g_strdup(path);
188 g_hash_table_insert(file_data_pool, fd->original_path, fd);
190 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
192 fd->path = g_strdup(path);
194 fd->extension = fd->name + 1;
195 file_data_set_collate_keys(fd);
199 fd->path = g_strdup(path);
200 fd->name = filename_from_path(fd->path);
202 if (strcmp(fd->name, "..") == 0)
204 gchar *dir = remove_level_from_path(path);
206 fd->path = remove_level_from_path(dir);
209 fd->extension = fd->name + 2;
210 file_data_set_collate_keys(fd);
213 else if (strcmp(fd->name, ".") == 0)
216 fd->path = remove_level_from_path(path);
218 fd->extension = fd->name + 1;
219 file_data_set_collate_keys(fd);
223 fd->extension = extension_from_path(fd->path);
224 if (fd->extension == NULL)
225 fd->extension = fd->name + strlen(fd->name);
227 file_data_set_collate_keys(fd);
230 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
232 gboolean ret = FALSE;
235 if (fd->size != st->st_size ||
236 fd->date != st->st_mtime)
238 fd->size = st->st_size;
239 fd->date = st->st_mtime;
240 fd->mode = st->st_mode;
241 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
242 fd->thumb_pixbuf = NULL;
243 file_data_increment_version(fd);
244 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
248 work = fd->sidecar_files;
251 FileData *sfd = work->data;
255 if (!stat_utf8(sfd->path, &st))
259 file_data_disconnect_sidecar_file(fd, sfd);
264 ret |= file_data_check_changed_files_recursive(sfd, &st);
270 gboolean file_data_check_changed_files(FileData *fd)
272 gboolean ret = FALSE;
275 if (fd->parent) fd = fd->parent;
277 if (!stat_utf8(fd->path, &st))
280 FileData *sfd = NULL;
282 /* parent is missing, we have to rebuild whole group */
287 work = fd->sidecar_files;
293 file_data_disconnect_sidecar_file(fd, sfd);
295 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
296 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
300 ret |= file_data_check_changed_files_recursive(fd, &st);
306 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
310 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
313 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
315 fd = g_hash_table_lookup(file_data_pool, path_utf8);
321 if (!fd && file_data_planned_change_hash)
323 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
326 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
328 file_data_apply_ci(fd);
337 changed = file_data_check_changed_files(fd);
339 changed = file_data_check_changed_files_recursive(fd, st);
340 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
341 file_data_check_sidecars(fd);
342 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
347 fd = g_new0(FileData, 1);
349 fd->size = st->st_size;
350 fd->date = st->st_mtime;
351 fd->mode = st->st_mode;
353 fd->magick = 0x12345678;
355 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
358 file_data_check_sidecars(fd);
363 static void file_data_check_sidecars(FileData *fd)
367 FileData *parent_fd = NULL;
370 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
373 base_len = fd->extension - fd->path;
374 fname = g_string_new_len(fd->path, base_len);
375 work = sidecar_ext_get_list();
379 /* check for possible sidecar files;
380 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
381 they have fd->ref set to 0 and file_data unref must chack and free them all together
382 (using fd->ref would cause loops and leaks)
386 gchar *ext = work->data;
390 if (g_ascii_strcasecmp(ext, fd->extension) == 0)
392 new_fd = fd; /* processing the original file */
397 g_string_truncate(fname, base_len);
399 if (!stat_utf8_case_insensitive_ext(fname, ext, &nst))
402 new_fd = file_data_new(fname->str, &nst, FALSE);
404 if (new_fd->disable_grouping)
406 file_data_unref(new_fd);
410 new_fd->ref--; /* do not use ref here */
414 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
416 file_data_merge_sidecar_files(parent_fd, new_fd);
418 g_string_free(fname, TRUE);
422 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
424 gchar *path_utf8 = path_to_utf8(path);
425 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
431 FileData *file_data_new_simple(const gchar *path_utf8)
435 if (!stat_utf8(path_utf8, &st))
441 return file_data_new(path_utf8, &st, TRUE);
444 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
446 sfd->parent = target;
447 if (!g_list_find(target->sidecar_files, sfd))
448 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
449 file_data_increment_version(sfd); /* increments both sfd and target */
454 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
458 file_data_add_sidecar_file(target, source);
460 work = source->sidecar_files;
463 FileData *sfd = work->data;
464 file_data_add_sidecar_file(target, sfd);
468 g_list_free(source->sidecar_files);
469 source->sidecar_files = NULL;
471 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
476 #ifdef DEBUG_FILEDATA
477 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
479 FileData *file_data_ref(FileData *fd)
482 if (fd == NULL) return NULL;
483 #ifdef DEBUG_FILEDATA
484 if (fd->magick != 0x12345678)
485 DEBUG_0("fd magick mismatch at %s:%d", file, line);
487 g_assert(fd->magick == 0x12345678);
490 #ifdef DEBUG_FILEDATA
491 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
493 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
498 static void file_data_free(FileData *fd)
500 g_assert(fd->magick == 0x12345678);
501 g_assert(fd->ref == 0);
503 g_hash_table_remove(file_data_pool, fd->original_path);
506 g_free(fd->original_path);
507 g_free(fd->collate_key_name);
508 g_free(fd->collate_key_name_nocase);
509 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
512 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
514 file_data_change_info_free(NULL, fd);
518 #ifdef DEBUG_FILEDATA
519 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
521 void file_data_unref(FileData *fd)
524 if (fd == NULL) return;
525 #ifdef DEBUG_FILEDATA
526 if (fd->magick != 0x12345678)
527 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
529 g_assert(fd->magick == 0x12345678);
532 #ifdef DEBUG_FILEDATA
533 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
536 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
541 FileData *parent = fd->parent ? fd->parent : fd;
546 work = parent->sidecar_files;
549 FileData *sfd = work->data;
555 /* none of parent/children is referenced, we can free everything */
557 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
559 work = parent->sidecar_files;
562 FileData *sfd = work->data;
567 g_list_free(parent->sidecar_files);
568 parent->sidecar_files = NULL;
570 file_data_free(parent);
574 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
576 sfd->parent = target;
577 g_assert(g_list_find(target->sidecar_files, sfd));
579 file_data_increment_version(sfd); /* increments both sfd and target */
581 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
593 /* disables / enables grouping for particular file, sends UPDATE notification */
594 void file_data_disable_grouping(FileData *fd, gboolean disable)
596 if (!fd->disable_grouping == !disable) return;
597 fd->disable_grouping = !!disable;
603 FileData *parent = file_data_ref(fd->parent);
604 file_data_disconnect_sidecar_file(parent, fd);
605 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
606 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
607 file_data_unref(parent);
609 else if (fd->sidecar_files)
611 GList *sidecar_files = filelist_copy(fd->sidecar_files);
612 GList *work = sidecar_files;
615 FileData *sfd = work->data;
617 file_data_disconnect_sidecar_file(fd, sfd);
618 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
620 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
621 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
622 filelist_free(sidecar_files);
627 file_data_check_sidecars(fd);
628 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
632 /* compare name without extension */
633 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
635 size_t len1 = fd1->extension - fd1->name;
636 size_t len2 = fd2->extension - fd2->name;
638 if (len1 < len2) return -1;
639 if (len1 > len2) return 1;
641 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
644 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
652 g_free(fdci->source);
661 static gboolean file_data_can_write_directly(FileData *fd)
663 return filter_name_is_writable(fd->extension);
666 static gboolean file_data_can_write_sidecar(FileData *fd)
668 return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
671 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
673 gchar *sidecar_path = NULL;
675 if (!file_data_can_write_sidecar(fd)) return NULL;
677 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
680 FileData *sfd = work->data;
682 if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
684 sidecar_path = g_strdup(sfd->path);
689 if (!existing_only && !sidecar_path)
691 gchar *base = remove_extension_from_path(fd->path);
692 sidecar_path = g_strconcat(base, ".xmp", NULL);
701 *-----------------------------------------------------------------------------
702 * sidecar file info struct
703 *-----------------------------------------------------------------------------
708 static gint sidecar_file_priority(const gchar *path)
710 const gchar *extension = extension_from_path(path);
714 if (extension == NULL)
717 work = sidecar_ext_get_list();
720 gchar *ext = work->data;
723 if (g_ascii_strcasecmp(extension, ext) == 0) return i;
731 *-----------------------------------------------------------------------------
733 *-----------------------------------------------------------------------------
736 static SortType filelist_sort_method = SORT_NONE;
737 static gint filelist_sort_ascend = TRUE;
740 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
742 if (!filelist_sort_ascend)
749 switch (filelist_sort_method)
754 if (fa->size < fb->size) return -1;
755 if (fa->size > fb->size) return 1;
756 /* fall back to name */
759 if (fa->date < fb->date) return -1;
760 if (fa->date > fb->date) return 1;
761 /* fall back to name */
763 #ifdef HAVE_STRVERSCMP
765 return strverscmp(fa->name, fb->name);
772 if (options->file_sort.case_sensitive)
773 return strcmp(fa->collate_key_name, fb->collate_key_name);
775 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
778 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
780 filelist_sort_method = method;
781 filelist_sort_ascend = ascend;
782 return filelist_sort_compare_filedata(fa, fb);
785 static gint filelist_sort_file_cb(gpointer a, gpointer b)
787 return filelist_sort_compare_filedata(a, b);
790 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
792 filelist_sort_method = method;
793 filelist_sort_ascend = ascend;
794 return g_list_sort(list, cb);
797 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
799 filelist_sort_method = method;
800 filelist_sort_ascend = ascend;
801 return g_list_insert_sorted(list, data, cb);
804 GList *filelist_sort(GList *list, SortType method, gint ascend)
806 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
809 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
811 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
815 static GList *filelist_filter_out_sidecars(GList *flist)
818 GList *flist_filtered = NULL;
822 FileData *fd = work->data;
825 if (fd->parent) /* remove fd's that are children */
828 flist_filtered = g_list_prepend(flist_filtered, fd);
832 return flist_filtered;
835 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
842 gint (*stat_func)(const gchar *path, struct stat *buf);
844 g_assert(files || dirs);
846 if (files) *files = NULL;
847 if (dirs) *dirs = NULL;
849 pathl = path_from_utf8(dir_fd->path);
850 if (!pathl) return FALSE;
864 while ((dir = readdir(dp)) != NULL)
866 struct stat ent_sbuf;
867 const gchar *name = dir->d_name;
870 if (!options->file_filter.show_hidden_files && ishidden(name))
873 filepath = g_build_filename(pathl, name, NULL);
874 if (stat_func(filepath, &ent_sbuf) >= 0)
876 if (S_ISDIR(ent_sbuf.st_mode))
878 /* we ignore the .thumbnails dir for cleanliness */
880 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
881 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
882 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
883 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
885 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
890 if (files && filter_name_exists(name))
892 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
903 if (dirs) *dirs = dlist;
904 if (files) *files = filelist_filter_out_sidecars(flist);
909 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
911 return filelist_read_real(dir_fd, files, dirs, TRUE);
914 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
916 return filelist_read_real(dir_fd, files, dirs, FALSE);
919 void filelist_free(GList *list)
926 file_data_unref((FileData *)work->data);
934 GList *filelist_copy(GList *list)
936 GList *new_list = NULL;
947 new_list = g_list_prepend(new_list, file_data_ref(fd));
950 return g_list_reverse(new_list);
953 GList *filelist_from_path_list(GList *list)
955 GList *new_list = NULL;
966 new_list = g_list_prepend(new_list, file_data_new_simple(path));
969 return g_list_reverse(new_list);
972 GList *filelist_to_path_list(GList *list)
974 GList *new_list = NULL;
985 new_list = g_list_prepend(new_list, g_strdup(fd->path));
988 return g_list_reverse(new_list);
991 GList *filelist_filter(GList *list, gint is_dir_list)
995 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1000 FileData *fd = (FileData *)(work->data);
1001 const gchar *name = fd->name;
1003 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1004 (!is_dir_list && !filter_name_exists(name)) ||
1005 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1006 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1010 list = g_list_remove_link(list, link);
1011 file_data_unref(fd);
1022 *-----------------------------------------------------------------------------
1023 * filelist recursive
1024 *-----------------------------------------------------------------------------
1027 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1029 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1032 GList *filelist_sort_path(GList *list)
1034 return g_list_sort(list, filelist_sort_path_cb);
1037 static void filelist_recursive_append(GList **list, GList *dirs)
1044 FileData *fd = (FileData *)(work->data);
1048 if (filelist_read(fd, &f, &d))
1050 f = filelist_filter(f, FALSE);
1051 f = filelist_sort_path(f);
1052 *list = g_list_concat(*list, f);
1054 d = filelist_filter(d, TRUE);
1055 d = filelist_sort_path(d);
1056 filelist_recursive_append(list, d);
1064 GList *filelist_recursive(FileData *dir_fd)
1069 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1070 list = filelist_filter(list, FALSE);
1071 list = filelist_sort_path(list);
1073 d = filelist_filter(d, TRUE);
1074 d = filelist_sort_path(d);
1075 filelist_recursive_append(&list, d);
1083 * marks and orientation
1086 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1087 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1088 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1090 gboolean file_data_get_mark(FileData *fd, gint n)
1092 gboolean valid = (fd->valid_marks & (1 << n));
1093 if (file_data_get_mark_func[n] && !valid)
1095 guint old = fd->marks;
1096 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1097 if (!value != !(fd->marks & (1 << n)))
1099 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;
1325 file_data_planned_change_remove(fd);
1327 g_free(fdci->source);
1336 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1340 if (fd->parent) fd = fd->parent;
1342 if (fd->change) return FALSE;
1344 work = fd->sidecar_files;
1347 FileData *sfd = work->data;
1349 if (sfd->change) return FALSE;
1353 file_data_add_ci(fd, type, NULL, NULL);
1355 work = fd->sidecar_files;
1358 FileData *sfd = work->data;
1360 file_data_add_ci(sfd, type, NULL, NULL);
1367 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1371 if (fd->parent) fd = fd->parent;
1373 if (!fd->change || fd->change->type != type) return FALSE;
1375 work = fd->sidecar_files;
1378 FileData *sfd = work->data;
1380 if (!sfd->change || sfd->change->type != type) return FALSE;
1388 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1390 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1391 file_data_sc_update_ci_copy(fd, dest_path);
1395 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1397 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1398 file_data_sc_update_ci_move(fd, dest_path);
1402 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1404 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1405 file_data_sc_update_ci_rename(fd, dest_path);
1409 gboolean file_data_sc_add_ci_delete(FileData *fd)
1411 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1414 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1416 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1417 file_data_sc_update_ci_unspecified(fd, dest_path);
1421 gboolean file_data_add_ci_write_metadata(FileData *fd)
1423 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1426 void file_data_sc_free_ci(FileData *fd)
1430 if (fd->parent) fd = fd->parent;
1432 file_data_free_ci(fd);
1434 work = fd->sidecar_files;
1437 FileData *sfd = work->data;
1439 file_data_free_ci(sfd);
1444 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1447 gboolean ret = TRUE;
1452 FileData *fd = work->data;
1454 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1461 static void file_data_sc_revert_ci_list(GList *fd_list)
1468 FileData *fd = work->data;
1470 file_data_sc_free_ci(fd);
1475 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1482 FileData *fd = work->data;
1484 if (!func(fd, dest))
1486 file_data_sc_revert_ci_list(work->prev);
1495 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1497 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1500 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1502 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1505 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1507 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1510 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1512 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1515 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1518 gboolean ret = TRUE;
1523 FileData *fd = work->data;
1525 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1532 void file_data_free_ci_list(GList *fd_list)
1539 FileData *fd = work->data;
1541 file_data_free_ci(fd);
1546 void file_data_sc_free_ci_list(GList *fd_list)
1553 FileData *fd = work->data;
1555 file_data_sc_free_ci(fd);
1561 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1562 * fails if fd->change does not exist or the change type does not match
1565 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1567 FileDataChangeType type = fd->change->type;
1569 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1573 if (!file_data_planned_change_hash)
1574 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1576 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1578 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1579 g_hash_table_remove(file_data_planned_change_hash, old_path);
1580 file_data_unref(fd);
1583 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1588 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1589 g_hash_table_remove(file_data_planned_change_hash, new_path);
1590 file_data_unref(ofd);
1593 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1595 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1600 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1602 gchar *old_path = fd->change->dest;
1604 fd->change->dest = g_strdup(dest_path);
1605 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1609 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1611 const gchar *extension = extension_from_path(fd->change->source);
1612 gchar *base = remove_extension_from_path(dest_path);
1613 gchar *old_path = fd->change->dest;
1615 fd->change->dest = g_strconcat(base, extension, NULL);
1616 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1622 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1625 gchar *dest_path_full = NULL;
1627 if (fd->parent) fd = fd->parent;
1631 dest_path = fd->path;
1633 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1635 gchar *dir = remove_level_from_path(fd->path);
1637 dest_path_full = g_build_filename(dir, dest_path, NULL);
1639 dest_path = dest_path_full;
1641 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1643 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1644 dest_path = dest_path_full;
1647 file_data_update_ci_dest(fd, dest_path);
1649 work = fd->sidecar_files;
1652 FileData *sfd = work->data;
1654 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1658 g_free(dest_path_full);
1661 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1663 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1664 file_data_sc_update_ci(fd, dest_path);
1668 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1670 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1673 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1675 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1678 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1680 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1683 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1685 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1688 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1690 gboolean (*func)(FileData *, const gchar *))
1693 gboolean ret = TRUE;
1698 FileData *fd = work->data;
1700 if (!func(fd, dest)) ret = FALSE;
1707 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1709 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1712 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1714 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1717 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1719 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1724 * verify source and dest paths - dest image exists, etc.
1725 * it should detect all possible problems with the planned operation
1728 gint file_data_verify_ci(FileData *fd)
1730 gint ret = CHANGE_OK;
1735 DEBUG_1("Change checked: no change info: %s", fd->path);
1739 if (!isname(fd->path))
1741 /* this probably should not happen */
1742 ret |= CHANGE_NO_SRC;
1743 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1747 dir = remove_level_from_path(fd->path);
1749 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1750 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1751 !access_file(fd->path, R_OK))
1753 ret |= CHANGE_NO_READ_PERM;
1754 DEBUG_1("Change checked: no read permission: %s", fd->path);
1756 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1757 !access_file(dir, W_OK))
1759 ret |= CHANGE_NO_WRITE_PERM_DIR;
1760 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1762 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1763 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1764 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1765 !access_file(fd->path, W_OK))
1767 ret |= CHANGE_WARN_NO_WRITE_PERM;
1768 DEBUG_1("Change checked: no write permission: %s", fd->path);
1770 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1771 - that means that there are no hard errors and warnings can be disabled
1772 - the destination is determined during the check
1774 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1776 /* determine destination file */
1777 gboolean have_dest = FALSE;
1778 gchar *dest_dir = NULL;
1780 if (options->metadata.save_in_image_file)
1782 if (file_data_can_write_directly(fd))
1784 /* we can write the file directly */
1785 if (access_file(fd->path, W_OK))
1791 if (options->metadata.warn_on_write_problems)
1793 ret |= CHANGE_WARN_NO_WRITE_PERM;
1794 DEBUG_1("Change checked: file is not writable: %s", fd->path);
1798 else if (file_data_can_write_sidecar(fd))
1800 /* we can write sidecar */
1801 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1802 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1804 file_data_update_ci_dest(fd, sidecar);
1809 if (options->metadata.warn_on_write_problems)
1811 ret |= CHANGE_WARN_NO_WRITE_PERM;
1812 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1821 /* write private metadata file under ~/.geeqie */
1823 /* If an existing metadata file exists, we will try writing to
1824 * it's location regardless of the user's preference.
1826 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1827 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1829 if (metadata_path && !access_file(metadata_path, W_OK))
1831 g_free(metadata_path);
1832 metadata_path = NULL;
1839 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1840 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1842 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1844 metadata_path = g_build_filename(dest_dir, filename, NULL);
1848 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1850 file_data_update_ci_dest(fd, metadata_path);
1855 ret |= CHANGE_NO_WRITE_PERM_DEST;
1856 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1858 g_free(metadata_path);
1863 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1868 same = (strcmp(fd->path, fd->change->dest) == 0);
1872 const gchar *dest_ext = extension_from_path(fd->change->dest);
1873 if (!dest_ext) dest_ext = "";
1875 if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
1877 ret |= CHANGE_WARN_CHANGED_EXT;
1878 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1883 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1885 ret |= CHANGE_WARN_SAME;
1886 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1890 dest_dir = remove_level_from_path(fd->change->dest);
1892 if (!isdir(dest_dir))
1894 ret |= CHANGE_NO_DEST_DIR;
1895 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1897 else if (!access_file(dest_dir, W_OK))
1899 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1900 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1904 if (isfile(fd->change->dest))
1906 if (!access_file(fd->change->dest, W_OK))
1908 ret |= CHANGE_NO_WRITE_PERM_DEST;
1909 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1913 ret |= CHANGE_WARN_DEST_EXISTS;
1914 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1917 else if (isdir(fd->change->dest))
1919 ret |= CHANGE_DEST_EXISTS;
1920 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1927 fd->change->error = ret;
1928 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1935 gint file_data_sc_verify_ci(FileData *fd)
1940 ret = file_data_verify_ci(fd);
1942 work = fd->sidecar_files;
1945 FileData *sfd = work->data;
1947 ret |= file_data_verify_ci(sfd);
1954 gchar *file_data_get_error_string(gint error)
1956 GString *result = g_string_new("");
1958 if (error & CHANGE_NO_SRC)
1960 if (result->len > 0) g_string_append(result, ", ");
1961 g_string_append(result, _("file or directory does not exist"));
1964 if (error & CHANGE_DEST_EXISTS)
1966 if (result->len > 0) g_string_append(result, ", ");
1967 g_string_append(result, _("destination already exists"));
1970 if (error & CHANGE_NO_WRITE_PERM_DEST)
1972 if (result->len > 0) g_string_append(result, ", ");
1973 g_string_append(result, _("destination can't be overwritten"));
1976 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1978 if (result->len > 0) g_string_append(result, ", ");
1979 g_string_append(result, _("destination directory is not writable"));
1982 if (error & CHANGE_NO_DEST_DIR)
1984 if (result->len > 0) g_string_append(result, ", ");
1985 g_string_append(result, _("destination directory does not exist"));
1988 if (error & CHANGE_NO_WRITE_PERM_DIR)
1990 if (result->len > 0) g_string_append(result, ", ");
1991 g_string_append(result, _("source directory is not writable"));
1994 if (error & CHANGE_NO_READ_PERM)
1996 if (result->len > 0) g_string_append(result, ", ");
1997 g_string_append(result, _("no read permission"));
2000 if (error & CHANGE_WARN_NO_WRITE_PERM)
2002 if (result->len > 0) g_string_append(result, ", ");
2003 g_string_append(result, _("file is readonly"));
2006 if (error & CHANGE_WARN_DEST_EXISTS)
2008 if (result->len > 0) g_string_append(result, ", ");
2009 g_string_append(result, _("destination already exists and will be overwritten"));
2012 if (error & CHANGE_WARN_SAME)
2014 if (result->len > 0) g_string_append(result, ", ");
2015 g_string_append(result, _("source and destination are the same"));
2018 if (error & CHANGE_WARN_CHANGED_EXT)
2020 if (result->len > 0) g_string_append(result, ", ");
2021 g_string_append(result, _("source and destination have different extension"));
2024 return g_string_free(result, FALSE);
2027 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2030 gint all_errors = 0;
2031 gint common_errors = ~0;
2036 if (!list) return 0;
2038 num = g_list_length(list);
2039 errors = g_new(int, num);
2050 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2051 all_errors |= error;
2052 common_errors &= error;
2059 if (desc && all_errors)
2062 GString *result = g_string_new("");
2066 gchar *str = file_data_get_error_string(common_errors);
2067 g_string_append(result, str);
2068 g_string_append(result, "\n");
2082 error = errors[i] & ~common_errors;
2086 gchar *str = file_data_get_error_string(error);
2087 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2092 *desc = g_string_free(result, FALSE);
2101 * perform the change described by FileFataChangeInfo
2102 * it is used for internal operations,
2103 * this function actually operates with files on the filesystem
2104 * it should implement safe delete
2107 static gboolean file_data_perform_move(FileData *fd)
2109 g_assert(!strcmp(fd->change->source, fd->path));
2110 return move_file(fd->change->source, fd->change->dest);
2113 static gboolean file_data_perform_copy(FileData *fd)
2115 g_assert(!strcmp(fd->change->source, fd->path));
2116 return copy_file(fd->change->source, fd->change->dest);
2119 static gboolean file_data_perform_delete(FileData *fd)
2121 if (isdir(fd->path) && !islink(fd->path))
2122 return rmdir_utf8(fd->path);
2124 if (options->file_ops.safe_delete_enable)
2125 return file_util_safe_unlink(fd->path);
2127 return unlink_file(fd->path);
2130 gboolean file_data_perform_ci(FileData *fd)
2132 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 gint 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 gint 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 gint 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 gint 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 gint 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 gint 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: */