4 * Copyright (C) 2008 - 2009 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
17 #include "filefilter.h"
19 #include "thumb_standard.h"
20 #include "ui_fileops.h"
25 static GHashTable *file_data_pool = NULL;
26 static GHashTable *file_data_planned_change_hash = NULL;
28 static gint sidecar_file_priority(const gchar *path);
32 *-----------------------------------------------------------------------------
33 * text conversion utils
34 *-----------------------------------------------------------------------------
37 gchar *text_from_size(gint64 size)
43 /* what I would like to use is printf("%'d", size)
44 * BUT: not supported on every libc :(
48 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
49 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
53 a = g_strdup_printf("%d", (guint)size);
59 b = g_new(gchar, l + n + 1);
84 gchar *text_from_size_abrev(gint64 size)
86 if (size < (gint64)1024)
88 return g_strdup_printf(_("%d bytes"), (gint)size);
90 if (size < (gint64)1048576)
92 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
94 if (size < (gint64)1073741824)
96 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
99 /* to avoid overflowing the gdouble, do division in two steps */
101 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
104 /* note: returned string is valid until next call to text_from_time() */
105 const gchar *text_from_time(time_t t)
107 static gchar *ret = NULL;
111 GError *error = NULL;
113 btime = localtime(&t);
115 /* the %x warning about 2 digit years is not an error */
116 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
117 if (buflen < 1) return "";
120 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
123 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
132 *-----------------------------------------------------------------------------
134 *-----------------------------------------------------------------------------
137 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
138 static void file_data_check_sidecars(FileData *fd);
139 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
142 void file_data_increment_version(FileData *fd)
148 fd->parent->version++;
149 fd->parent->valid_marks = 0;
153 static void file_data_set_collate_keys(FileData *fd)
155 gchar *caseless_name;
157 caseless_name = g_utf8_casefold(fd->name, -1);
159 g_free(fd->collate_key_name);
160 g_free(fd->collate_key_name_nocase);
162 #if GLIB_CHECK_VERSION(2, 8, 0)
163 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
164 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
166 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
167 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
169 g_free(caseless_name);
172 static void file_data_set_path(FileData *fd, const gchar *path)
174 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
175 g_assert(file_data_pool);
179 if (fd->original_path)
181 g_hash_table_remove(file_data_pool, fd->original_path);
182 g_free(fd->original_path);
185 g_assert(!g_hash_table_lookup(file_data_pool, path));
187 fd->original_path = g_strdup(path);
188 g_hash_table_insert(file_data_pool, fd->original_path, fd);
190 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
192 fd->path = g_strdup(path);
194 fd->extension = fd->name + 1;
195 file_data_set_collate_keys(fd);
199 fd->path = g_strdup(path);
200 fd->name = filename_from_path(fd->path);
202 if (strcmp(fd->name, "..") == 0)
204 gchar *dir = remove_level_from_path(path);
206 fd->path = remove_level_from_path(dir);
209 fd->extension = fd->name + 2;
210 file_data_set_collate_keys(fd);
213 else if (strcmp(fd->name, ".") == 0)
216 fd->path = remove_level_from_path(path);
218 fd->extension = fd->name + 1;
219 file_data_set_collate_keys(fd);
223 fd->extension = extension_from_path(fd->path);
224 if (fd->extension == NULL)
225 fd->extension = fd->name + strlen(fd->name);
227 file_data_set_collate_keys(fd);
230 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
232 gboolean ret = FALSE;
235 if (fd->size != st->st_size ||
236 fd->date != st->st_mtime)
238 fd->size = st->st_size;
239 fd->date = st->st_mtime;
240 fd->mode = st->st_mode;
241 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
242 fd->thumb_pixbuf = NULL;
243 file_data_increment_version(fd);
244 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
248 work = fd->sidecar_files;
251 FileData *sfd = work->data;
255 if (!stat_utf8(sfd->path, &st))
259 file_data_disconnect_sidecar_file(fd, sfd);
264 ret |= file_data_check_changed_files_recursive(sfd, &st);
270 gboolean file_data_check_changed_files(FileData *fd)
272 gboolean ret = FALSE;
275 if (fd->parent) fd = fd->parent;
277 if (!stat_utf8(fd->path, &st))
280 FileData *sfd = NULL;
282 /* parent is missing, we have to rebuild whole group */
287 work = fd->sidecar_files;
293 file_data_disconnect_sidecar_file(fd, sfd);
295 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
296 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
300 ret |= file_data_check_changed_files_recursive(fd, &st);
306 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
310 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
313 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
315 fd = g_hash_table_lookup(file_data_pool, path_utf8);
321 if (!fd && file_data_planned_change_hash)
323 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
326 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
328 file_data_apply_ci(fd);
337 changed = file_data_check_changed_files(fd);
339 changed = file_data_check_changed_files_recursive(fd, st);
340 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
341 file_data_check_sidecars(fd);
342 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
347 fd = g_new0(FileData, 1);
351 fd->collate_key_name = NULL;
352 fd->collate_key_name_nocase = NULL;
353 fd->original_path = NULL;
355 fd->size = st->st_size;
356 fd->date = st->st_mtime;
357 fd->mode = st->st_mode;
358 fd->thumb_pixbuf = NULL;
359 fd->sidecar_files = NULL;
361 fd->magick = 0x12345678;
363 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
366 file_data_check_sidecars(fd);
371 static void file_data_check_sidecars(FileData *fd)
375 FileData *parent_fd = NULL;
378 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
381 base_len = fd->extension - fd->path;
382 fname = g_string_new_len(fd->path, base_len);
383 work = sidecar_ext_get_list();
387 /* check for possible sidecar files;
388 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
389 they have fd->ref set to 0 and file_data unref must chack and free them all together
390 (using fd->ref would cause loops and leaks)
394 gchar *ext = work->data;
398 if (strcasecmp(ext, fd->extension) == 0)
400 new_fd = fd; /* processing the original file */
405 g_string_truncate(fname, base_len);
407 if (!stat_utf8_case_insensitive_ext(fname, ext, &nst))
410 new_fd = file_data_new(fname->str, &nst, FALSE);
412 if (new_fd->disable_grouping)
414 file_data_unref(new_fd);
418 new_fd->ref--; /* do not use ref here */
422 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
424 file_data_merge_sidecar_files(parent_fd, new_fd);
426 g_string_free(fname, TRUE);
430 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
432 gchar *path_utf8 = path_to_utf8(path);
433 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
439 FileData *file_data_new_simple(const gchar *path_utf8)
443 if (!stat_utf8(path_utf8, &st))
449 return file_data_new(path_utf8, &st, TRUE);
452 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
454 sfd->parent = target;
455 if (!g_list_find(target->sidecar_files, sfd))
456 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
457 file_data_increment_version(sfd); /* increments both sfd and target */
462 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
466 file_data_add_sidecar_file(target, source);
468 work = source->sidecar_files;
471 FileData *sfd = work->data;
472 file_data_add_sidecar_file(target, sfd);
476 g_list_free(source->sidecar_files);
477 source->sidecar_files = NULL;
479 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
484 #ifdef DEBUG_FILEDATA
485 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
487 FileData *file_data_ref(FileData *fd)
490 if (fd == NULL) return NULL;
491 #ifdef DEBUG_FILEDATA
492 if (fd->magick != 0x12345678)
493 DEBUG_0("fd magick mismatch at %s:%d", file, line);
495 g_assert(fd->magick == 0x12345678);
498 #ifdef DEBUG_FILEDATA
499 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
501 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
506 static void file_data_free(FileData *fd)
508 g_assert(fd->magick == 0x12345678);
509 g_assert(fd->ref == 0);
511 g_hash_table_remove(file_data_pool, fd->original_path);
514 g_free(fd->original_path);
515 g_free(fd->collate_key_name);
516 g_free(fd->collate_key_name_nocase);
517 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
519 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
521 file_data_change_info_free(NULL, fd);
525 #ifdef DEBUG_FILEDATA
526 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
528 void file_data_unref(FileData *fd)
531 if (fd == NULL) return;
532 #ifdef DEBUG_FILEDATA
533 if (fd->magick != 0x12345678)
534 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
536 g_assert(fd->magick == 0x12345678);
539 #ifdef DEBUG_FILEDATA
540 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
543 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
548 FileData *parent = fd->parent ? fd->parent : fd;
553 work = parent->sidecar_files;
556 FileData *sfd = work->data;
562 /* none of parent/children is referenced, we can free everything */
564 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
566 work = parent->sidecar_files;
569 FileData *sfd = work->data;
574 g_list_free(parent->sidecar_files);
575 parent->sidecar_files = NULL;
577 file_data_free(parent);
581 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
583 sfd->parent = target;
584 g_assert(g_list_find(target->sidecar_files, sfd));
586 file_data_increment_version(sfd); /* increments both sfd and target */
588 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
600 /* disables / enables grouping for particular file, sends UPDATE notification */
601 void file_data_disable_grouping(FileData *fd, gboolean disable)
603 if (!fd->disable_grouping == !disable) return;
604 fd->disable_grouping = !!disable;
610 FileData *parent = file_data_ref(fd->parent);
611 file_data_disconnect_sidecar_file(parent, fd);
612 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
613 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
614 file_data_unref(parent);
616 else if (fd->sidecar_files)
618 GList *sidecar_files = filelist_copy(fd->sidecar_files);
619 GList *work = sidecar_files;
622 FileData *sfd = work->data;
624 file_data_disconnect_sidecar_file(fd, sfd);
625 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
627 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
628 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
629 filelist_free(sidecar_files);
634 file_data_check_sidecars(fd);
635 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
639 /* compare name without extension */
640 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
642 size_t len1 = fd1->extension - fd1->name;
643 size_t len2 = fd2->extension - fd2->name;
645 if (len1 < len2) return -1;
646 if (len1 > len2) return 1;
648 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
651 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
659 g_free(fdci->source);
668 static gboolean file_data_can_write_directly(FileData *fd)
670 return filter_name_is_writable(fd->extension);
673 static gboolean file_data_can_write_sidecar(FileData *fd)
675 return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
678 gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
680 gchar *sidecar_path = NULL;
682 if (!file_data_can_write_sidecar(fd)) return NULL;
684 work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
687 FileData *sfd = work->data;
689 if (strcasecmp(sfd->extension, ".xmp") == 0)
691 sidecar_path = g_strdup(sfd->path);
696 if (!existing_only && !sidecar_path)
698 gchar *base = remove_extension_from_path(fd->path);
699 sidecar_path = g_strconcat(base, ".xmp", NULL);
708 *-----------------------------------------------------------------------------
709 * sidecar file info struct
710 *-----------------------------------------------------------------------------
715 static gint sidecar_file_priority(const gchar *path)
717 const gchar *extension = extension_from_path(path);
721 if (extension == NULL)
724 work = sidecar_ext_get_list();
727 gchar *ext = work->data;
730 if (strcasecmp(extension, ext) == 0) return i;
738 *-----------------------------------------------------------------------------
740 *-----------------------------------------------------------------------------
743 static SortType filelist_sort_method = SORT_NONE;
744 static gint filelist_sort_ascend = TRUE;
747 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
749 if (!filelist_sort_ascend)
756 switch (filelist_sort_method)
761 if (fa->size < fb->size) return -1;
762 if (fa->size > fb->size) return 1;
763 /* fall back to name */
766 if (fa->date < fb->date) return -1;
767 if (fa->date > fb->date) return 1;
768 /* fall back to name */
770 #ifdef HAVE_STRVERSCMP
772 return strverscmp(fa->name, fb->name);
779 if (options->file_sort.case_sensitive)
780 return strcmp(fa->collate_key_name, fb->collate_key_name);
782 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
785 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
787 filelist_sort_method = method;
788 filelist_sort_ascend = ascend;
789 return filelist_sort_compare_filedata(fa, fb);
792 static gint filelist_sort_file_cb(gpointer a, gpointer b)
794 return filelist_sort_compare_filedata(a, b);
797 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
799 filelist_sort_method = method;
800 filelist_sort_ascend = ascend;
801 return g_list_sort(list, cb);
804 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
806 filelist_sort_method = method;
807 filelist_sort_ascend = ascend;
808 return g_list_insert_sorted(list, data, cb);
811 GList *filelist_sort(GList *list, SortType method, gint ascend)
813 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
816 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
818 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
822 static GList *filelist_filter_out_sidecars(GList *flist)
825 GList *flist_filtered = NULL;
829 FileData *fd = work->data;
832 if (fd->parent) /* remove fd's that are children */
835 flist_filtered = g_list_prepend(flist_filtered, fd);
839 return flist_filtered;
842 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
849 gint (*stat_func)(const gchar *path, struct stat *buf);
851 g_assert(files || dirs);
853 if (files) *files = NULL;
854 if (dirs) *dirs = NULL;
856 pathl = path_from_utf8(dir_fd->path);
857 if (!pathl) return FALSE;
871 while ((dir = readdir(dp)) != NULL)
873 struct stat ent_sbuf;
874 const gchar *name = dir->d_name;
877 if (!options->file_filter.show_hidden_files && ishidden(name))
880 filepath = g_build_filename(pathl, name, NULL);
881 if (stat_func(filepath, &ent_sbuf) >= 0)
883 if (S_ISDIR(ent_sbuf.st_mode))
885 /* we ignore the .thumbnails dir for cleanliness */
887 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
888 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
889 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
890 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
892 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
897 if (files && filter_name_exists(name))
899 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
910 if (dirs) *dirs = dlist;
911 if (files) *files = filelist_filter_out_sidecars(flist);
916 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
918 return filelist_read_real(dir_fd, files, dirs, TRUE);
921 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
923 return filelist_read_real(dir_fd, files, dirs, FALSE);
926 void filelist_free(GList *list)
933 file_data_unref((FileData *)work->data);
941 GList *filelist_copy(GList *list)
943 GList *new_list = NULL;
954 new_list = g_list_prepend(new_list, file_data_ref(fd));
957 return g_list_reverse(new_list);
960 GList *filelist_from_path_list(GList *list)
962 GList *new_list = NULL;
973 new_list = g_list_prepend(new_list, file_data_new_simple(path));
976 return g_list_reverse(new_list);
979 GList *filelist_to_path_list(GList *list)
981 GList *new_list = NULL;
992 new_list = g_list_prepend(new_list, g_strdup(fd->path));
995 return g_list_reverse(new_list);
998 GList *filelist_filter(GList *list, gint is_dir_list)
1002 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
1007 FileData *fd = (FileData *)(work->data);
1008 const gchar *name = fd->name;
1010 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
1011 (!is_dir_list && !filter_name_exists(name)) ||
1012 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
1013 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
1017 list = g_list_remove_link(list, link);
1018 file_data_unref(fd);
1029 *-----------------------------------------------------------------------------
1030 * filelist recursive
1031 *-----------------------------------------------------------------------------
1034 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
1036 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
1039 GList *filelist_sort_path(GList *list)
1041 return g_list_sort(list, filelist_sort_path_cb);
1044 static void filelist_recursive_append(GList **list, GList *dirs)
1051 FileData *fd = (FileData *)(work->data);
1055 if (filelist_read(fd, &f, &d))
1057 f = filelist_filter(f, FALSE);
1058 f = filelist_sort_path(f);
1059 *list = g_list_concat(*list, f);
1061 d = filelist_filter(d, TRUE);
1062 d = filelist_sort_path(d);
1063 filelist_recursive_append(list, d);
1071 GList *filelist_recursive(FileData *dir_fd)
1076 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1077 list = filelist_filter(list, FALSE);
1078 list = filelist_sort_path(list);
1080 d = filelist_filter(d, TRUE);
1081 d = filelist_sort_path(d);
1082 filelist_recursive_append(&list, d);
1090 * marks and orientation
1093 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1094 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1095 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1097 gboolean file_data_get_mark(FileData *fd, gint n)
1099 gboolean valid = (fd->valid_marks & (1 << n));
1100 if (file_data_get_mark_func[n] && !valid)
1102 guint old = fd->marks;
1103 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1104 if (!value != !(fd->marks & (1 << n)))
1106 fd->marks = fd->marks ^ (1 << n);
1108 fd->valid_marks |= (1 << n);
1109 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1111 file_data_unref(fd);
1113 else if (!old && fd->marks)
1119 return !!(fd->marks & (1 << n));
1122 guint file_data_get_marks(FileData *fd)
1125 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1129 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1132 if (!value == !file_data_get_mark(fd, n)) return;
1134 if (file_data_set_mark_func[n])
1136 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1141 fd->marks = fd->marks ^ (1 << n);
1143 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1145 file_data_unref(fd);
1147 else if (!old && fd->marks)
1152 file_data_increment_version(fd);
1153 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1156 gboolean file_data_filter_marks(FileData *fd, guint filter)
1159 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1160 return ((fd->marks & filter) == filter);
1163 GList *file_data_filter_marks_list(GList *list, guint filter)
1170 FileData *fd = work->data;
1174 if (!file_data_filter_marks(fd, filter))
1176 list = g_list_remove_link(list, link);
1177 file_data_unref(fd);
1185 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1187 FileData *fd = value;
1188 file_data_increment_version(fd);
1189 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1192 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1194 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1196 file_data_get_mark_func[n] = get_mark_func;
1197 file_data_set_mark_func[n] = set_mark_func;
1198 file_data_mark_func_data[n] = data;
1202 /* this effectively changes all known files */
1203 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1209 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1211 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1212 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1213 if (data) *data = file_data_mark_func_data[n];
1216 gint file_data_get_user_orientation(FileData *fd)
1218 return fd->user_orientation;
1221 void file_data_set_user_orientation(FileData *fd, gint value)
1223 if (fd->user_orientation == value) return;
1225 fd->user_orientation = value;
1226 file_data_increment_version(fd);
1227 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1232 * file_data - operates on the given fd
1233 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1237 /* return list of sidecar file extensions in a string */
1238 gchar *file_data_sc_list_to_string(FileData *fd)
1241 GString *result = g_string_new("");
1243 work = fd->sidecar_files;
1246 FileData *sfd = work->data;
1248 result = g_string_append(result, "+ ");
1249 result = g_string_append(result, sfd->extension);
1251 if (work) result = g_string_append_c(result, ' ');
1254 return g_string_free(result, FALSE);
1260 * add FileDataChangeInfo (see typedefs.h) for the given operation
1261 * uses file_data_add_change_info
1263 * fails if the fd->change already exists - change operations can't run in parallel
1264 * fd->change_info works as a lock
1266 * dest can be NULL - in this case the current name is used for now, it will
1271 FileDataChangeInfo types:
1273 MOVE - path is changed, name may be changed too
1274 RENAME - path remains unchanged, name is changed
1275 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1276 sidecar names are changed too, extensions are not changed
1278 UPDATE - file size, date or grouping has been changed
1281 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1283 FileDataChangeInfo *fdci;
1285 if (fd->change) return FALSE;
1287 fdci = g_new0(FileDataChangeInfo, 1);
1292 fdci->source = g_strdup(src);
1294 fdci->source = g_strdup(fd->path);
1297 fdci->dest = g_strdup(dest);
1304 static void file_data_planned_change_remove(FileData *fd)
1306 if (file_data_planned_change_hash &&
1307 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1309 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1311 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1312 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1313 file_data_unref(fd);
1314 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1316 g_hash_table_destroy(file_data_planned_change_hash);
1317 file_data_planned_change_hash = NULL;
1318 DEBUG_1("planned change: empty");
1325 void file_data_free_ci(FileData *fd)
1327 FileDataChangeInfo *fdci = fd->change;
1332 file_data_planned_change_remove(fd);
1334 g_free(fdci->source);
1343 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1347 if (fd->parent) fd = fd->parent;
1349 if (fd->change) return FALSE;
1351 work = fd->sidecar_files;
1354 FileData *sfd = work->data;
1356 if (sfd->change) return FALSE;
1360 file_data_add_ci(fd, type, NULL, NULL);
1362 work = fd->sidecar_files;
1365 FileData *sfd = work->data;
1367 file_data_add_ci(sfd, type, NULL, NULL);
1374 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1378 if (fd->parent) fd = fd->parent;
1380 if (!fd->change || fd->change->type != type) return FALSE;
1382 work = fd->sidecar_files;
1385 FileData *sfd = work->data;
1387 if (!sfd->change || sfd->change->type != type) return FALSE;
1395 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1397 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1398 file_data_sc_update_ci_copy(fd, dest_path);
1402 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1404 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1405 file_data_sc_update_ci_move(fd, dest_path);
1409 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1411 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1412 file_data_sc_update_ci_rename(fd, dest_path);
1416 gboolean file_data_sc_add_ci_delete(FileData *fd)
1418 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1421 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1423 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1424 file_data_sc_update_ci_unspecified(fd, dest_path);
1428 gboolean file_data_add_ci_write_metadata(FileData *fd)
1430 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1433 void file_data_sc_free_ci(FileData *fd)
1437 if (fd->parent) fd = fd->parent;
1439 file_data_free_ci(fd);
1441 work = fd->sidecar_files;
1444 FileData *sfd = work->data;
1446 file_data_free_ci(sfd);
1451 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1454 gboolean ret = TRUE;
1459 FileData *fd = work->data;
1461 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1468 static void file_data_sc_revert_ci_list(GList *fd_list)
1475 FileData *fd = work->data;
1477 file_data_sc_free_ci(fd);
1482 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1489 FileData *fd = work->data;
1491 if (!func(fd, dest))
1493 file_data_sc_revert_ci_list(work->prev);
1502 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1504 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1507 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1509 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1512 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1514 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1517 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1519 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1522 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1525 gboolean ret = TRUE;
1530 FileData *fd = work->data;
1532 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1539 void file_data_free_ci_list(GList *fd_list)
1546 FileData *fd = work->data;
1548 file_data_free_ci(fd);
1553 void file_data_sc_free_ci_list(GList *fd_list)
1560 FileData *fd = work->data;
1562 file_data_sc_free_ci(fd);
1568 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1569 * fails if fd->change does not exist or the change type does not match
1572 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1574 FileDataChangeType type = fd->change->type;
1576 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1580 if (!file_data_planned_change_hash)
1581 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1583 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1585 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1586 g_hash_table_remove(file_data_planned_change_hash, old_path);
1587 file_data_unref(fd);
1590 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1595 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1596 g_hash_table_remove(file_data_planned_change_hash, new_path);
1597 file_data_unref(ofd);
1600 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1602 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1607 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1609 gchar *old_path = fd->change->dest;
1611 fd->change->dest = g_strdup(dest_path);
1612 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1616 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1618 const gchar *extension = extension_from_path(fd->change->source);
1619 gchar *base = remove_extension_from_path(dest_path);
1620 gchar *old_path = fd->change->dest;
1622 fd->change->dest = g_strconcat(base, extension, NULL);
1623 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1629 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1632 gchar *dest_path_full = NULL;
1634 if (fd->parent) fd = fd->parent;
1638 dest_path = fd->path;
1640 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1642 gchar *dir = remove_level_from_path(fd->path);
1644 dest_path_full = g_build_filename(dir, dest_path, NULL);
1646 dest_path = dest_path_full;
1648 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1650 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1651 dest_path = dest_path_full;
1654 file_data_update_ci_dest(fd, dest_path);
1656 work = fd->sidecar_files;
1659 FileData *sfd = work->data;
1661 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1665 g_free(dest_path_full);
1668 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1670 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1671 file_data_sc_update_ci(fd, dest_path);
1675 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1677 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1680 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1682 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1685 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1687 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1690 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1692 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1695 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1697 gboolean (*func)(FileData *, const gchar *))
1700 gboolean ret = TRUE;
1705 FileData *fd = work->data;
1707 if (!func(fd, dest)) ret = FALSE;
1714 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1716 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1719 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1721 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1724 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1726 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1731 * verify source and dest paths - dest image exists, etc.
1732 * it should detect all possible problems with the planned operation
1735 gint file_data_verify_ci(FileData *fd)
1737 gint ret = CHANGE_OK;
1742 DEBUG_1("Change checked: no change info: %s", fd->path);
1746 if (!isname(fd->path))
1748 /* this probably should not happen */
1749 ret |= CHANGE_NO_SRC;
1750 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1754 dir = remove_level_from_path(fd->path);
1756 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1757 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1758 !access_file(fd->path, R_OK))
1760 ret |= CHANGE_NO_READ_PERM;
1761 DEBUG_1("Change checked: no read permission: %s", fd->path);
1763 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1764 !access_file(dir, W_OK))
1766 ret |= CHANGE_NO_WRITE_PERM_DIR;
1767 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1769 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1770 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1771 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1772 !access_file(fd->path, W_OK))
1774 ret |= CHANGE_WARN_NO_WRITE_PERM;
1775 DEBUG_1("Change checked: no write permission: %s", fd->path);
1777 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1778 - that means that there are no hard errors and warnings can be disabled
1779 - the destination is determined during the check
1781 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
1783 /* determine destination file */
1784 gboolean have_dest = FALSE;
1785 gchar *dest_dir = NULL;
1787 if (options->metadata.save_in_image_file)
1789 if (file_data_can_write_directly(fd))
1791 /* we can write the file directly */
1792 if (access_file(fd->path, W_OK))
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", fd->path);
1805 else if (file_data_can_write_sidecar(fd))
1807 /* we can write sidecar */
1808 gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
1809 if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
1811 file_data_update_ci_dest(fd, sidecar);
1816 if (options->metadata.warn_on_write_problems)
1818 ret |= CHANGE_WARN_NO_WRITE_PERM;
1819 DEBUG_1("Change checked: file is not writable: %s", sidecar);
1828 /* write private metadata file under ~/.geeqie */
1830 /* If an existing metadata file exists, we will try writing to
1831 * it's location regardless of the user's preference.
1833 gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
1834 if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
1836 if (metadata_path && !access_file(metadata_path, W_OK))
1838 g_free(metadata_path);
1839 metadata_path = NULL;
1846 dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
1847 if (recursive_mkdir_if_not_exists(dest_dir, mode))
1849 gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
1851 metadata_path = g_build_filename(dest_dir, filename, NULL);
1855 if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
1857 file_data_update_ci_dest(fd, metadata_path);
1862 ret |= CHANGE_NO_WRITE_PERM_DEST;
1863 DEBUG_1("Change checked: file is not writable: %s", metadata_path);
1865 g_free(metadata_path);
1870 if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
1875 same = (strcmp(fd->path, fd->change->dest) == 0);
1879 const gchar *dest_ext = extension_from_path(fd->change->dest);
1880 if (!dest_ext) dest_ext = "";
1882 if (strcasecmp(fd->extension, dest_ext) != 0)
1884 ret |= CHANGE_WARN_CHANGED_EXT;
1885 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1890 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1892 ret |= CHANGE_WARN_SAME;
1893 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1897 dest_dir = remove_level_from_path(fd->change->dest);
1899 if (!isdir(dest_dir))
1901 ret |= CHANGE_NO_DEST_DIR;
1902 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1904 else if (!access_file(dest_dir, W_OK))
1906 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1907 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1911 if (isfile(fd->change->dest))
1913 if (!access_file(fd->change->dest, W_OK))
1915 ret |= CHANGE_NO_WRITE_PERM_DEST;
1916 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1920 ret |= CHANGE_WARN_DEST_EXISTS;
1921 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1924 else if (isdir(fd->change->dest))
1926 ret |= CHANGE_DEST_EXISTS;
1927 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1934 fd->change->error = ret;
1935 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1942 gint file_data_sc_verify_ci(FileData *fd)
1947 ret = file_data_verify_ci(fd);
1949 work = fd->sidecar_files;
1952 FileData *sfd = work->data;
1954 ret |= file_data_verify_ci(sfd);
1961 gchar *file_data_get_error_string(gint error)
1963 GString *result = g_string_new("");
1965 if (error & CHANGE_NO_SRC)
1967 if (result->len > 0) g_string_append(result, ", ");
1968 g_string_append(result, _("file or directory does not exist"));
1971 if (error & CHANGE_DEST_EXISTS)
1973 if (result->len > 0) g_string_append(result, ", ");
1974 g_string_append(result, _("destination already exists"));
1977 if (error & CHANGE_NO_WRITE_PERM_DEST)
1979 if (result->len > 0) g_string_append(result, ", ");
1980 g_string_append(result, _("destination can't be overwritten"));
1983 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1985 if (result->len > 0) g_string_append(result, ", ");
1986 g_string_append(result, _("destination directory is not writable"));
1989 if (error & CHANGE_NO_DEST_DIR)
1991 if (result->len > 0) g_string_append(result, ", ");
1992 g_string_append(result, _("destination directory does not exist"));
1995 if (error & CHANGE_NO_WRITE_PERM_DIR)
1997 if (result->len > 0) g_string_append(result, ", ");
1998 g_string_append(result, _("source directory is not writable"));
2001 if (error & CHANGE_NO_READ_PERM)
2003 if (result->len > 0) g_string_append(result, ", ");
2004 g_string_append(result, _("no read permission"));
2007 if (error & CHANGE_WARN_NO_WRITE_PERM)
2009 if (result->len > 0) g_string_append(result, ", ");
2010 g_string_append(result, _("file is readonly"));
2013 if (error & CHANGE_WARN_DEST_EXISTS)
2015 if (result->len > 0) g_string_append(result, ", ");
2016 g_string_append(result, _("destination already exists and will be overwritten"));
2019 if (error & CHANGE_WARN_SAME)
2021 if (result->len > 0) g_string_append(result, ", ");
2022 g_string_append(result, _("source and destination are the same"));
2025 if (error & CHANGE_WARN_CHANGED_EXT)
2027 if (result->len > 0) g_string_append(result, ", ");
2028 g_string_append(result, _("source and destination have different extension"));
2031 return g_string_free(result, FALSE);
2034 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
2037 gint all_errors = 0;
2038 gint common_errors = ~0;
2043 if (!list) return 0;
2045 num = g_list_length(list);
2046 errors = g_new(int, num);
2057 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
2058 all_errors |= error;
2059 common_errors &= error;
2066 if (desc && all_errors)
2069 GString *result = g_string_new("");
2073 gchar *str = file_data_get_error_string(common_errors);
2074 g_string_append(result, str);
2075 g_string_append(result, "\n");
2089 error = errors[i] & ~common_errors;
2093 gchar *str = file_data_get_error_string(error);
2094 g_string_append_printf(result, "%s: %s\n", fd->name, str);
2099 *desc = g_string_free(result, FALSE);
2108 * perform the change described by FileFataChangeInfo
2109 * it is used for internal operations,
2110 * this function actually operates with files on the filesystem
2111 * it should implement safe delete
2114 static gboolean file_data_perform_move(FileData *fd)
2116 g_assert(!strcmp(fd->change->source, fd->path));
2117 return move_file(fd->change->source, fd->change->dest);
2120 static gboolean file_data_perform_copy(FileData *fd)
2122 g_assert(!strcmp(fd->change->source, fd->path));
2123 return copy_file(fd->change->source, fd->change->dest);
2126 static gboolean file_data_perform_delete(FileData *fd)
2128 if (isdir(fd->path) && !islink(fd->path))
2129 return rmdir_utf8(fd->path);
2131 if (options->file_ops.safe_delete_enable)
2132 return file_util_safe_unlink(fd->path);
2134 return unlink_file(fd->path);
2137 gboolean file_data_perform_ci(FileData *fd)
2139 FileDataChangeType type = fd->change->type;
2142 case FILEDATA_CHANGE_MOVE:
2143 return file_data_perform_move(fd);
2144 case FILEDATA_CHANGE_COPY:
2145 return file_data_perform_copy(fd);
2146 case FILEDATA_CHANGE_RENAME:
2147 return file_data_perform_move(fd); /* the same as move */
2148 case FILEDATA_CHANGE_DELETE:
2149 return file_data_perform_delete(fd);
2150 case FILEDATA_CHANGE_WRITE_METADATA:
2151 return metadata_write_perform(fd);
2152 case FILEDATA_CHANGE_UNSPECIFIED:
2153 /* nothing to do here */
2161 gboolean file_data_sc_perform_ci(FileData *fd)
2164 gboolean ret = TRUE;
2165 FileDataChangeType type = fd->change->type;
2167 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2169 work = fd->sidecar_files;
2172 FileData *sfd = work->data;
2174 if (!file_data_perform_ci(sfd)) ret = FALSE;
2178 if (!file_data_perform_ci(fd)) ret = FALSE;
2184 * updates FileData structure according to FileDataChangeInfo
2187 gint file_data_apply_ci(FileData *fd)
2189 FileDataChangeType type = fd->change->type;
2192 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2194 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2195 file_data_planned_change_remove(fd);
2197 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2199 /* this change overwrites another file which is already known to other modules
2200 renaming fd would create duplicate FileData structure
2201 the best thing we can do is nothing
2202 FIXME: maybe we could copy stuff like marks
2204 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2208 file_data_set_path(fd, fd->change->dest);
2211 file_data_increment_version(fd);
2212 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2217 gint file_data_sc_apply_ci(FileData *fd)
2220 FileDataChangeType type = fd->change->type;
2222 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2224 work = fd->sidecar_files;
2227 FileData *sfd = work->data;
2229 file_data_apply_ci(sfd);
2233 file_data_apply_ci(fd);
2239 * notify other modules about the change described by FileFataChangeInfo
2242 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2243 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2244 implementation in view_file_list.c */
2249 typedef struct _NotifyData NotifyData;
2251 struct _NotifyData {
2252 FileDataNotifyFunc func;
2254 NotifyPriority priority;
2257 static GList *notify_func_list = NULL;
2259 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2261 NotifyData *nda = (NotifyData *)a;
2262 NotifyData *ndb = (NotifyData *)b;
2264 if (nda->priority < ndb->priority) return -1;
2265 if (nda->priority > ndb->priority) return 1;
2269 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2273 nd = g_new(NotifyData, 1);
2276 nd->priority = priority;
2278 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2279 DEBUG_1("Notify func registered: %p", nd);
2284 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2286 GList *work = notify_func_list;
2290 NotifyData *nd = (NotifyData *)work->data;
2292 if (nd->func == func && nd->data == data)
2294 notify_func_list = g_list_delete_link(notify_func_list, work);
2296 DEBUG_1("Notify func unregistered: %p", nd);
2306 void file_data_send_notification(FileData *fd, NotifyType type)
2308 GList *work = notify_func_list;
2312 NotifyData *nd = (NotifyData *)work->data;
2314 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2315 nd->func(fd, type, nd->data);
2320 static GHashTable *file_data_monitor_pool = NULL;
2321 static gint realtime_monitor_id = -1;
2323 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2327 file_data_check_changed_files(fd);
2329 DEBUG_1("monitor %s", fd->path);
2332 static gboolean realtime_monitor_cb(gpointer data)
2334 if (!options->update_on_time_change) return TRUE;
2335 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2339 gint file_data_register_real_time_monitor(FileData *fd)
2345 if (!file_data_monitor_pool)
2346 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2348 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2350 DEBUG_1("Register realtime %d %s", count, fd->path);
2353 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2355 if (realtime_monitor_id == -1)
2357 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2363 gint file_data_unregister_real_time_monitor(FileData *fd)
2367 g_assert(file_data_monitor_pool);
2369 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2371 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2373 g_assert(count > 0);
2378 g_hash_table_remove(file_data_monitor_pool, fd);
2380 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2382 file_data_unref(fd);
2384 if (g_hash_table_size(file_data_monitor_pool) == 0)
2386 g_source_remove(realtime_monitor_id);
2387 realtime_monitor_id = -1;
2393 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */