4 * Copyright (C) 2008 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
17 #include "filefilter.h"
19 #include "thumb_standard.h"
20 #include "ui_fileops.h"
25 static GHashTable *file_data_pool = NULL;
26 static GHashTable *file_data_planned_change_hash = NULL;
28 static gint sidecar_file_priority(const gchar *path);
32 *-----------------------------------------------------------------------------
33 * text conversion utils
34 *-----------------------------------------------------------------------------
37 gchar *text_from_size(gint64 size)
43 /* what I would like to use is printf("%'d", size)
44 * BUT: not supported on every libc :(
48 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
49 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
53 a = g_strdup_printf("%d", (guint)size);
59 b = g_new(gchar, l + n + 1);
84 gchar *text_from_size_abrev(gint64 size)
86 if (size < (gint64)1024)
88 return g_strdup_printf(_("%d bytes"), (gint)size);
90 if (size < (gint64)1048576)
92 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
94 if (size < (gint64)1073741824)
96 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
99 /* to avoid overflowing the gdouble, do division in two steps */
101 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
104 /* note: returned string is valid until next call to text_from_time() */
105 const gchar *text_from_time(time_t t)
107 static gchar *ret = NULL;
111 GError *error = NULL;
113 btime = localtime(&t);
115 /* the %x warning about 2 digit years is not an error */
116 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
117 if (buflen < 1) return "";
120 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
123 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
132 *-----------------------------------------------------------------------------
134 *-----------------------------------------------------------------------------
137 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
138 static void file_data_check_sidecars(FileData *fd);
139 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
142 void file_data_increment_version(FileData *fd)
145 if (fd->parent) fd->parent->version++;
148 static void file_data_set_collate_keys(FileData *fd)
150 gchar *caseless_name;
152 caseless_name = g_utf8_casefold(fd->name, -1);
154 g_free(fd->collate_key_name);
155 g_free(fd->collate_key_name_nocase);
157 #if GLIB_CHECK_VERSION(2, 8, 0)
158 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
159 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
161 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
162 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
164 g_free(caseless_name);
167 static void file_data_set_path(FileData *fd, const gchar *path)
169 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
170 g_assert(file_data_pool);
174 if (fd->original_path)
176 g_hash_table_remove(file_data_pool, fd->original_path);
177 g_free(fd->original_path);
180 g_assert(!g_hash_table_lookup(file_data_pool, path));
182 fd->original_path = g_strdup(path);
183 g_hash_table_insert(file_data_pool, fd->original_path, fd);
185 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
187 fd->path = g_strdup(path);
189 fd->extension = fd->name + 1;
190 file_data_set_collate_keys(fd);
194 fd->path = g_strdup(path);
195 fd->name = filename_from_path(fd->path);
197 if (strcmp(fd->name, "..") == 0)
199 gchar *dir = remove_level_from_path(path);
201 fd->path = remove_level_from_path(dir);
204 fd->extension = fd->name + 2;
205 file_data_set_collate_keys(fd);
208 else if (strcmp(fd->name, ".") == 0)
211 fd->path = remove_level_from_path(path);
213 fd->extension = fd->name + 1;
214 file_data_set_collate_keys(fd);
218 fd->extension = extension_from_path(fd->path);
219 if (fd->extension == NULL)
220 fd->extension = fd->name + strlen(fd->name);
222 file_data_set_collate_keys(fd);
225 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
227 gboolean ret = FALSE;
230 if (fd->size != st->st_size ||
231 fd->date != st->st_mtime)
233 fd->size = st->st_size;
234 fd->date = st->st_mtime;
235 fd->mode = st->st_mode;
236 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
237 fd->thumb_pixbuf = NULL;
238 file_data_increment_version(fd);
239 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
243 work = fd->sidecar_files;
246 FileData *sfd = work->data;
250 if (!stat_utf8(sfd->path, &st))
254 file_data_disconnect_sidecar_file(fd, sfd);
259 ret |= file_data_check_changed_files_recursive(sfd, &st);
265 gboolean file_data_check_changed_files(FileData *fd)
267 gboolean ret = FALSE;
270 if (fd->parent) fd = fd->parent;
272 if (!stat_utf8(fd->path, &st))
275 FileData *sfd = NULL;
277 /* parent is missing, we have to rebuild whole group */
282 work = fd->sidecar_files;
288 file_data_disconnect_sidecar_file(fd, sfd);
290 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
291 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
295 ret |= file_data_check_changed_files_recursive(fd, &st);
301 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
305 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
308 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
310 fd = g_hash_table_lookup(file_data_pool, path_utf8);
316 if (!fd && file_data_planned_change_hash)
318 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
321 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
323 file_data_apply_ci(fd);
332 changed = file_data_check_changed_files(fd);
334 changed = file_data_check_changed_files_recursive(fd, st);
335 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
336 file_data_check_sidecars(fd);
337 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
342 fd = g_new0(FileData, 1);
346 fd->collate_key_name = NULL;
347 fd->collate_key_name_nocase = NULL;
348 fd->original_path = NULL;
350 fd->size = st->st_size;
351 fd->date = st->st_mtime;
352 fd->mode = st->st_mode;
353 fd->thumb_pixbuf = NULL;
354 fd->sidecar_files = NULL;
356 fd->magick = 0x12345678;
358 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
361 file_data_check_sidecars(fd);
366 static void file_data_check_sidecars(FileData *fd)
370 FileData *parent_fd = NULL;
373 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
376 base_len = fd->extension - fd->path;
377 fname = g_string_new_len(fd->path, base_len);
378 work = sidecar_ext_get_list();
382 /* check for possible sidecar files;
383 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
384 they have fd->ref set to 0 and file_data unref must chack and free them all together
385 (using fd->ref would cause loops and leaks)
389 gchar *ext = work->data;
393 if (strcmp(ext, fd->extension) == 0)
395 new_fd = fd; /* processing the original file */
400 g_string_truncate(fname, base_len);
401 g_string_append(fname, ext);
403 if (!stat_utf8(fname->str, &nst))
406 new_fd = file_data_new(fname->str, &nst, FALSE);
408 if (new_fd->disable_grouping)
410 file_data_unref(new_fd);
414 new_fd->ref--; /* do not use ref here */
418 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
420 file_data_merge_sidecar_files(parent_fd, new_fd);
422 g_string_free(fname, TRUE);
426 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
428 gchar *path_utf8 = path_to_utf8(path);
429 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
435 FileData *file_data_new_simple(const gchar *path_utf8)
439 if (!stat_utf8(path_utf8, &st))
445 return file_data_new(path_utf8, &st, TRUE);
448 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
450 sfd->parent = target;
451 if (!g_list_find(target->sidecar_files, sfd))
452 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
453 file_data_increment_version(sfd); /* increments both sfd and target */
458 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
462 file_data_add_sidecar_file(target, source);
464 work = source->sidecar_files;
467 FileData *sfd = work->data;
468 file_data_add_sidecar_file(target, sfd);
472 g_list_free(source->sidecar_files);
473 source->sidecar_files = NULL;
475 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
480 #ifdef DEBUG_FILEDATA
481 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
483 FileData *file_data_ref(FileData *fd)
486 if (fd == NULL) return NULL;
487 #ifdef DEBUG_FILEDATA
488 if (fd->magick != 0x12345678)
489 DEBUG_0("fd magick mismatch at %s:%d", file, line);
491 g_assert(fd->magick == 0x12345678);
494 #ifdef DEBUG_FILEDATA
495 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
497 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
502 static void file_data_free(FileData *fd)
504 g_assert(fd->magick == 0x12345678);
505 g_assert(fd->ref == 0);
507 g_hash_table_remove(file_data_pool, fd->original_path);
510 g_free(fd->original_path);
511 g_free(fd->collate_key_name);
512 g_free(fd->collate_key_name_nocase);
513 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
515 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
517 file_data_change_info_free(NULL, fd);
521 #ifdef DEBUG_FILEDATA
522 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
524 void file_data_unref(FileData *fd)
527 if (fd == NULL) return;
528 #ifdef DEBUG_FILEDATA
529 if (fd->magick != 0x12345678)
530 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
532 g_assert(fd->magick == 0x12345678);
535 #ifdef DEBUG_FILEDATA
536 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
539 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
544 FileData *parent = fd->parent ? fd->parent : fd;
549 work = parent->sidecar_files;
552 FileData *sfd = work->data;
558 /* none of parent/children is referenced, we can free everything */
560 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
562 work = parent->sidecar_files;
565 FileData *sfd = work->data;
570 g_list_free(parent->sidecar_files);
571 parent->sidecar_files = NULL;
573 file_data_free(parent);
577 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
579 sfd->parent = target;
580 g_assert(g_list_find(target->sidecar_files, sfd));
582 file_data_increment_version(sfd); /* increments both sfd and target */
584 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
596 /* disables / enables grouping for particular file, sends UPDATE notification */
597 void file_data_disable_grouping(FileData *fd, gboolean disable)
599 if (!fd->disable_grouping == !disable) return;
600 fd->disable_grouping = !!disable;
606 FileData *parent = file_data_ref(fd->parent);
607 file_data_disconnect_sidecar_file(parent, fd);
608 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
609 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
610 file_data_unref(parent);
612 else if (fd->sidecar_files)
614 GList *sidecar_files = filelist_copy(fd->sidecar_files);
615 GList *work = sidecar_files;
618 FileData *sfd = work->data;
620 file_data_disconnect_sidecar_file(fd, sfd);
621 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
623 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
624 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
625 filelist_free(sidecar_files);
630 file_data_check_sidecars(fd);
631 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
635 /* compare name without extension */
636 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
638 size_t len1 = fd1->extension - fd1->name;
639 size_t len2 = fd2->extension - fd2->name;
641 if (len1 < len2) return -1;
642 if (len1 > len2) return 1;
644 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
647 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
655 g_free(fdci->source);
668 *-----------------------------------------------------------------------------
669 * sidecar file info struct
670 *-----------------------------------------------------------------------------
675 static gint sidecar_file_priority(const gchar *path)
677 const gchar *extension = extension_from_path(path);
681 if (extension == NULL)
684 work = sidecar_ext_get_list();
687 gchar *ext = work->data;
690 if (strcmp(extension, ext) == 0) return i;
698 *-----------------------------------------------------------------------------
700 *-----------------------------------------------------------------------------
703 static SortType filelist_sort_method = SORT_NONE;
704 static gint filelist_sort_ascend = TRUE;
707 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
709 if (!filelist_sort_ascend)
716 switch (filelist_sort_method)
721 if (fa->size < fb->size) return -1;
722 if (fa->size > fb->size) return 1;
723 /* fall back to name */
726 if (fa->date < fb->date) return -1;
727 if (fa->date > fb->date) return 1;
728 /* fall back to name */
730 #ifdef HAVE_STRVERSCMP
732 return strverscmp(fa->name, fb->name);
739 if (options->file_sort.case_sensitive)
740 return strcmp(fa->collate_key_name, fb->collate_key_name);
742 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
745 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
747 filelist_sort_method = method;
748 filelist_sort_ascend = ascend;
749 return filelist_sort_compare_filedata(fa, fb);
752 static gint filelist_sort_file_cb(gpointer a, gpointer b)
754 return filelist_sort_compare_filedata(a, b);
757 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
759 filelist_sort_method = method;
760 filelist_sort_ascend = ascend;
761 return g_list_sort(list, cb);
764 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
766 filelist_sort_method = method;
767 filelist_sort_ascend = ascend;
768 return g_list_insert_sorted(list, data, cb);
771 GList *filelist_sort(GList *list, SortType method, gint ascend)
773 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
776 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
778 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
782 static GList *filelist_filter_out_sidecars(GList *flist)
785 GList *flist_filtered = NULL;
789 FileData *fd = work->data;
792 if (fd->parent) /* remove fd's that are children */
795 flist_filtered = g_list_prepend(flist_filtered, fd);
799 return flist_filtered;
802 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
809 gint (*stat_func)(const gchar *path, struct stat *buf);
811 g_assert(files || dirs);
813 if (files) *files = NULL;
814 if (dirs) *dirs = NULL;
816 pathl = path_from_utf8(dir_fd->path);
817 if (!pathl) return FALSE;
831 while ((dir = readdir(dp)) != NULL)
833 struct stat ent_sbuf;
834 const gchar *name = dir->d_name;
837 if (!options->file_filter.show_hidden_files && ishidden(name))
840 filepath = g_build_filename(pathl, name, NULL);
841 if (stat_func(filepath, &ent_sbuf) >= 0)
843 if (S_ISDIR(ent_sbuf.st_mode))
845 /* we ignore the .thumbnails dir for cleanliness */
847 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
848 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
849 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
850 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
852 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
857 if (files && filter_name_exists(name))
859 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
870 if (dirs) *dirs = dlist;
871 if (files) *files = filelist_filter_out_sidecars(flist);
876 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
878 return filelist_read_real(dir_fd, files, dirs, TRUE);
881 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
883 return filelist_read_real(dir_fd, files, dirs, FALSE);
886 void filelist_free(GList *list)
893 file_data_unref((FileData *)work->data);
901 GList *filelist_copy(GList *list)
903 GList *new_list = NULL;
914 new_list = g_list_prepend(new_list, file_data_ref(fd));
917 return g_list_reverse(new_list);
920 GList *filelist_from_path_list(GList *list)
922 GList *new_list = NULL;
933 new_list = g_list_prepend(new_list, file_data_new_simple(path));
936 return g_list_reverse(new_list);
939 GList *filelist_to_path_list(GList *list)
941 GList *new_list = NULL;
952 new_list = g_list_prepend(new_list, g_strdup(fd->path));
955 return g_list_reverse(new_list);
958 GList *filelist_filter(GList *list, gint is_dir_list)
962 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
967 FileData *fd = (FileData *)(work->data);
968 const gchar *name = fd->name;
970 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
971 (!is_dir_list && !filter_name_exists(name)) ||
972 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
973 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
977 list = g_list_remove_link(list, link);
989 *-----------------------------------------------------------------------------
991 *-----------------------------------------------------------------------------
994 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
996 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
999 GList *filelist_sort_path(GList *list)
1001 return g_list_sort(list, filelist_sort_path_cb);
1004 static void filelist_recursive_append(GList **list, GList *dirs)
1011 FileData *fd = (FileData *)(work->data);
1015 if (filelist_read(fd, &f, &d))
1017 f = filelist_filter(f, FALSE);
1018 f = filelist_sort_path(f);
1019 *list = g_list_concat(*list, f);
1021 d = filelist_filter(d, TRUE);
1022 d = filelist_sort_path(d);
1023 filelist_recursive_append(list, d);
1031 GList *filelist_recursive(FileData *dir_fd)
1036 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1037 list = filelist_filter(list, FALSE);
1038 list = filelist_sort_path(list);
1040 d = filelist_filter(d, TRUE);
1041 d = filelist_sort_path(d);
1042 filelist_recursive_append(&list, d);
1050 * marks and orientation
1053 static FileDataGetMarkFunc file_data_get_mark_func[FILEDATA_MARKS_SIZE];
1054 static FileDataSetMarkFunc file_data_set_mark_func[FILEDATA_MARKS_SIZE];
1055 static gpointer file_data_mark_func_data[FILEDATA_MARKS_SIZE];
1057 gboolean file_data_get_mark(FileData *fd, gint n)
1059 if (file_data_get_mark_func[n])
1061 gboolean value = (file_data_get_mark_func[n])(fd, n, file_data_mark_func_data[n]);
1062 if (!value != !(fd->marks & (1 << n))) fd->marks = fd->marks ^ (1 << n);
1065 return !!(fd->marks & (1 << n));
1068 guint file_data_get_marks(FileData *fd)
1071 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) file_data_get_mark(fd, i);
1075 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1077 guint old = fd->marks;
1078 if (!value == !(fd->marks & (1 << n))) return;
1080 if (file_data_set_mark_func[n])
1082 (file_data_set_mark_func[n])(fd, n, value, file_data_mark_func_data[n]);
1085 fd->marks = fd->marks ^ (1 << n);
1087 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1089 file_data_unref(fd);
1091 else if (!old && fd->marks)
1096 file_data_increment_version(fd);
1097 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1100 gboolean file_data_filter_marks(FileData *fd, guint filter)
1103 for (i = 0; i < FILEDATA_MARKS_SIZE; i++) if (filter & (1 << i)) file_data_get_mark(fd, i);
1104 return ((fd->marks & filter) == filter);
1107 GList *file_data_filter_marks_list(GList *list, guint filter)
1114 FileData *fd = work->data;
1118 if (!file_data_filter_marks(fd, filter))
1120 list = g_list_remove_link(list, link);
1121 file_data_unref(fd);
1129 static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
1131 FileData *fd = value;
1132 file_data_increment_version(fd);
1133 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1136 gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func, FileDataSetMarkFunc set_mark_func, gpointer data)
1138 if (n < 0 || n >= FILEDATA_MARKS_SIZE) return FALSE;
1140 file_data_get_mark_func[n] = get_mark_func;
1141 file_data_set_mark_func[n] = set_mark_func;
1142 file_data_mark_func_data[n] = data;
1146 /* this effectively changes all known files */
1147 g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
1153 void file_data_get_registered_mark_func(gint n, FileDataGetMarkFunc *get_mark_func, FileDataSetMarkFunc *set_mark_func, gpointer *data)
1155 if (get_mark_func) *get_mark_func = file_data_get_mark_func[n];
1156 if (set_mark_func) *set_mark_func = file_data_set_mark_func[n];
1157 if (data) *data = file_data_mark_func_data[n];
1160 gint file_data_get_user_orientation(FileData *fd)
1162 return fd->user_orientation;
1165 void file_data_set_user_orientation(FileData *fd, gint value)
1167 if (fd->user_orientation == value) return;
1169 fd->user_orientation = value;
1170 file_data_increment_version(fd);
1171 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1176 * file_data - operates on the given fd
1177 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1181 /* return list of sidecar file extensions in a string */
1182 gchar *file_data_sc_list_to_string(FileData *fd)
1185 GString *result = g_string_new("");
1187 work = fd->sidecar_files;
1190 FileData *sfd = work->data;
1192 result = g_string_append(result, "+ ");
1193 result = g_string_append(result, sfd->extension);
1195 if (work) result = g_string_append_c(result, ' ');
1198 return g_string_free(result, FALSE);
1204 * add FileDataChangeInfo (see typedefs.h) for the given operation
1205 * uses file_data_add_change_info
1207 * fails if the fd->change already exists - change operations can't run in parallel
1208 * fd->change_info works as a lock
1210 * dest can be NULL - in this case the current name is used for now, it will
1215 FileDataChangeInfo types:
1217 MOVE - path is changed, name may be changed too
1218 RENAME - path remains unchanged, name is changed
1219 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1220 sidecar names are changed too, extensions are not changed
1222 UPDATE - file size, date or grouping has been changed
1225 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1227 FileDataChangeInfo *fdci;
1229 if (fd->change) return FALSE;
1231 fdci = g_new0(FileDataChangeInfo, 1);
1236 fdci->source = g_strdup(src);
1238 fdci->source = g_strdup(fd->path);
1241 fdci->dest = g_strdup(dest);
1248 static void file_data_planned_change_remove(FileData *fd)
1250 if (file_data_planned_change_hash &&
1251 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1253 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1255 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1256 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1257 file_data_unref(fd);
1258 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1260 g_hash_table_destroy(file_data_planned_change_hash);
1261 file_data_planned_change_hash = NULL;
1262 DEBUG_1("planned change: empty");
1269 void file_data_free_ci(FileData *fd)
1271 FileDataChangeInfo *fdci = fd->change;
1276 file_data_planned_change_remove(fd);
1278 g_free(fdci->source);
1287 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1291 if (fd->parent) fd = fd->parent;
1293 if (fd->change) return FALSE;
1295 work = fd->sidecar_files;
1298 FileData *sfd = work->data;
1300 if (sfd->change) return FALSE;
1304 file_data_add_ci(fd, type, NULL, NULL);
1306 work = fd->sidecar_files;
1309 FileData *sfd = work->data;
1311 file_data_add_ci(sfd, type, NULL, NULL);
1318 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1322 if (fd->parent) fd = fd->parent;
1324 if (!fd->change || fd->change->type != type) return FALSE;
1326 work = fd->sidecar_files;
1329 FileData *sfd = work->data;
1331 if (!sfd->change || sfd->change->type != type) return FALSE;
1339 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1341 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1342 file_data_sc_update_ci_copy(fd, dest_path);
1346 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1348 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1349 file_data_sc_update_ci_move(fd, dest_path);
1353 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1355 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1356 file_data_sc_update_ci_rename(fd, dest_path);
1360 gboolean file_data_sc_add_ci_delete(FileData *fd)
1362 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1365 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1367 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1368 file_data_sc_update_ci_unspecified(fd, dest_path);
1372 gboolean file_data_add_ci_write_metadata(FileData *fd)
1374 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1377 void file_data_sc_free_ci(FileData *fd)
1381 if (fd->parent) fd = fd->parent;
1383 file_data_free_ci(fd);
1385 work = fd->sidecar_files;
1388 FileData *sfd = work->data;
1390 file_data_free_ci(sfd);
1395 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1398 gboolean ret = TRUE;
1403 FileData *fd = work->data;
1405 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1412 static void file_data_sc_revert_ci_list(GList *fd_list)
1419 FileData *fd = work->data;
1421 file_data_sc_free_ci(fd);
1426 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1433 FileData *fd = work->data;
1435 if (!func(fd, dest))
1437 file_data_sc_revert_ci_list(work->prev);
1446 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1448 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1451 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1453 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1456 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1458 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1461 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1463 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1466 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1469 gboolean ret = TRUE;
1474 FileData *fd = work->data;
1476 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1483 void file_data_free_ci_list(GList *fd_list)
1490 FileData *fd = work->data;
1492 file_data_free_ci(fd);
1497 void file_data_sc_free_ci_list(GList *fd_list)
1504 FileData *fd = work->data;
1506 file_data_sc_free_ci(fd);
1512 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1513 * fails if fd->change does not exist or the change type does not match
1516 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1518 FileDataChangeType type = fd->change->type;
1520 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1524 if (!file_data_planned_change_hash)
1525 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1527 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1529 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1530 g_hash_table_remove(file_data_planned_change_hash, old_path);
1531 file_data_unref(fd);
1534 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1539 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1540 g_hash_table_remove(file_data_planned_change_hash, new_path);
1541 file_data_unref(ofd);
1544 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1546 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1551 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1553 gchar *old_path = fd->change->dest;
1555 fd->change->dest = g_strdup(dest_path);
1556 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1560 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1562 const gchar *extension = extension_from_path(fd->change->source);
1563 gchar *base = remove_extension_from_path(dest_path);
1564 gchar *old_path = fd->change->dest;
1566 fd->change->dest = g_strconcat(base, extension, NULL);
1567 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1573 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1576 gchar *dest_path_full = NULL;
1578 if (fd->parent) fd = fd->parent;
1582 dest_path = fd->path;
1584 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1586 gchar *dir = remove_level_from_path(fd->path);
1588 dest_path_full = g_build_filename(dir, dest_path, NULL);
1590 dest_path = dest_path_full;
1592 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1594 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1595 dest_path = dest_path_full;
1598 file_data_update_ci_dest(fd, dest_path);
1600 work = fd->sidecar_files;
1603 FileData *sfd = work->data;
1605 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1609 g_free(dest_path_full);
1612 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1614 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1615 file_data_sc_update_ci(fd, dest_path);
1619 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1621 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1624 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1626 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1629 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1631 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1634 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1636 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1639 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1641 gboolean (*func)(FileData *, const gchar *))
1644 gboolean ret = TRUE;
1649 FileData *fd = work->data;
1651 if (!func(fd, dest)) ret = FALSE;
1658 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1660 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1663 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1665 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1668 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1670 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1675 * verify source and dest paths - dest image exists, etc.
1676 * it should detect all possible problems with the planned operation
1679 gint file_data_verify_ci(FileData *fd)
1681 gint ret = CHANGE_OK;
1686 DEBUG_1("Change checked: no change info: %s", fd->path);
1690 if (!isname(fd->path) &&
1691 !filter_file_class(fd->extension, FORMAT_CLASS_META)) /* xmp sidecar can be eventually created from scratch */
1693 /* this probably should not happen */
1694 ret |= CHANGE_NO_SRC;
1695 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1699 dir = remove_level_from_path(fd->path);
1701 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1702 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1703 !access_file(fd->path, R_OK))
1705 ret |= CHANGE_NO_READ_PERM;
1706 DEBUG_1("Change checked: no read permission: %s", fd->path);
1708 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1709 !access_file(dir, W_OK))
1711 ret |= CHANGE_NO_WRITE_PERM_DIR;
1712 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1714 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1715 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1716 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1717 !access_file(fd->path, W_OK))
1719 ret |= CHANGE_WARN_NO_WRITE_PERM;
1720 DEBUG_1("Change checked: no write permission: %s", fd->path);
1722 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1723 - that means that there are no hard errors and warnings can be disabled
1725 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA &&
1726 options->metadata.save_in_image_file && options->metadata.warn_on_write_problems)
1728 if (isname(fd->path) && !access_file(fd->path, W_OK))
1730 ret |= CHANGE_WARN_NO_WRITE_PERM;
1731 DEBUG_1("Change checked: file is readonly: %s", fd->path);
1734 else if (!access_file(dir, W_OK))
1736 ret |= CHANGE_WARN_NO_WRITE_PERM;
1737 DEBUG_1("Change checked: dir is readonly: %s", fd->path);
1741 if (fd->change->dest)
1746 same = (strcmp(fd->path, fd->change->dest) == 0);
1750 const gchar *dest_ext = extension_from_path(fd->change->dest);
1751 if (!dest_ext) dest_ext = "";
1753 if (strcasecmp(fd->extension, dest_ext) != 0)
1755 ret |= CHANGE_WARN_CHANGED_EXT;
1756 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1761 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1763 ret |= CHANGE_WARN_SAME;
1764 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1768 dest_dir = remove_level_from_path(fd->change->dest);
1770 if (!isdir(dest_dir))
1772 ret |= CHANGE_NO_DEST_DIR;
1773 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1775 else if (!access_file(dest_dir, W_OK))
1777 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1778 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1782 if (isfile(fd->change->dest))
1784 if (!access_file(fd->change->dest, W_OK))
1786 ret |= CHANGE_NO_WRITE_PERM_DEST;
1787 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1791 ret |= CHANGE_WARN_DEST_EXISTS;
1792 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1795 else if (isdir(fd->change->dest))
1797 ret |= CHANGE_DEST_EXISTS;
1798 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1805 fd->change->error = ret;
1806 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1813 gint file_data_sc_verify_ci(FileData *fd)
1818 ret = file_data_verify_ci(fd);
1820 work = fd->sidecar_files;
1823 FileData *sfd = work->data;
1825 ret |= file_data_verify_ci(sfd);
1832 gchar *file_data_get_error_string(gint error)
1834 GString *result = g_string_new("");
1836 if (error & CHANGE_NO_SRC)
1838 if (result->len > 0) g_string_append(result, ", ");
1839 g_string_append(result, _("file or directory does not exist"));
1842 if (error & CHANGE_DEST_EXISTS)
1844 if (result->len > 0) g_string_append(result, ", ");
1845 g_string_append(result, _("destination already exists"));
1848 if (error & CHANGE_NO_WRITE_PERM_DEST)
1850 if (result->len > 0) g_string_append(result, ", ");
1851 g_string_append(result, _("destination can't be overwritten"));
1854 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1856 if (result->len > 0) g_string_append(result, ", ");
1857 g_string_append(result, _("destination directory is not writable"));
1860 if (error & CHANGE_NO_DEST_DIR)
1862 if (result->len > 0) g_string_append(result, ", ");
1863 g_string_append(result, _("destination directory does not exist"));
1866 if (error & CHANGE_NO_WRITE_PERM_DIR)
1868 if (result->len > 0) g_string_append(result, ", ");
1869 g_string_append(result, _("source directory is not writable"));
1872 if (error & CHANGE_NO_READ_PERM)
1874 if (result->len > 0) g_string_append(result, ", ");
1875 g_string_append(result, _("no read permission"));
1878 if (error & CHANGE_WARN_NO_WRITE_PERM)
1880 if (result->len > 0) g_string_append(result, ", ");
1881 g_string_append(result, _("file is readonly"));
1884 if (error & CHANGE_WARN_DEST_EXISTS)
1886 if (result->len > 0) g_string_append(result, ", ");
1887 g_string_append(result, _("destination already exists and will be overwritten"));
1890 if (error & CHANGE_WARN_SAME)
1892 if (result->len > 0) g_string_append(result, ", ");
1893 g_string_append(result, _("source and destination are the same"));
1896 if (error & CHANGE_WARN_CHANGED_EXT)
1898 if (result->len > 0) g_string_append(result, ", ");
1899 g_string_append(result, _("source and destination have different extension"));
1902 return g_string_free(result, FALSE);
1905 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
1908 gint all_errors = 0;
1909 gint common_errors = ~0;
1914 if (!list) return 0;
1916 num = g_list_length(list);
1917 errors = g_new(int, num);
1928 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
1929 all_errors |= error;
1930 common_errors &= error;
1937 if (desc && all_errors)
1940 GString *result = g_string_new("");
1944 gchar *str = file_data_get_error_string(common_errors);
1945 g_string_append(result, str);
1946 g_string_append(result, "\n");
1960 error = errors[i] & ~common_errors;
1964 gchar *str = file_data_get_error_string(error);
1965 g_string_append_printf(result, "%s: %s\n", fd->name, str);
1970 *desc = g_string_free(result, FALSE);
1979 * perform the change described by FileFataChangeInfo
1980 * it is used for internal operations,
1981 * this function actually operates with files on the filesystem
1982 * it should implement safe delete
1985 static gboolean file_data_perform_move(FileData *fd)
1987 g_assert(!strcmp(fd->change->source, fd->path));
1988 return move_file(fd->change->source, fd->change->dest);
1991 static gboolean file_data_perform_copy(FileData *fd)
1993 g_assert(!strcmp(fd->change->source, fd->path));
1994 return copy_file(fd->change->source, fd->change->dest);
1997 static gboolean file_data_perform_delete(FileData *fd)
1999 if (isdir(fd->path) && !islink(fd->path))
2000 return rmdir_utf8(fd->path);
2002 if (options->file_ops.safe_delete_enable)
2003 return file_util_safe_unlink(fd->path);
2005 return unlink_file(fd->path);
2008 gboolean file_data_perform_ci(FileData *fd)
2010 FileDataChangeType type = fd->change->type;
2013 case FILEDATA_CHANGE_MOVE:
2014 return file_data_perform_move(fd);
2015 case FILEDATA_CHANGE_COPY:
2016 return file_data_perform_copy(fd);
2017 case FILEDATA_CHANGE_RENAME:
2018 return file_data_perform_move(fd); /* the same as move */
2019 case FILEDATA_CHANGE_DELETE:
2020 return file_data_perform_delete(fd);
2021 case FILEDATA_CHANGE_WRITE_METADATA:
2022 return metadata_write_perform(fd);
2023 case FILEDATA_CHANGE_UNSPECIFIED:
2024 /* nothing to do here */
2032 gboolean file_data_sc_perform_ci(FileData *fd)
2035 gboolean ret = TRUE;
2036 FileDataChangeType type = fd->change->type;
2038 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2040 work = fd->sidecar_files;
2043 FileData *sfd = work->data;
2045 if (!file_data_perform_ci(sfd)) ret = FALSE;
2049 if (!file_data_perform_ci(fd)) ret = FALSE;
2055 * updates FileData structure according to FileDataChangeInfo
2058 gint file_data_apply_ci(FileData *fd)
2060 FileDataChangeType type = fd->change->type;
2063 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2065 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2066 file_data_planned_change_remove(fd);
2068 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2070 /* this change overwrites another file which is already known to other modules
2071 renaming fd would create duplicate FileData structure
2072 the best thing we can do is nothing
2073 FIXME: maybe we could copy stuff like marks
2075 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2079 file_data_set_path(fd, fd->change->dest);
2082 file_data_increment_version(fd);
2083 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2088 gint file_data_sc_apply_ci(FileData *fd)
2091 FileDataChangeType type = fd->change->type;
2093 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2095 work = fd->sidecar_files;
2098 FileData *sfd = work->data;
2100 file_data_apply_ci(sfd);
2104 file_data_apply_ci(fd);
2110 * notify other modules about the change described by FileFataChangeInfo
2113 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2114 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2115 implementation in view_file_list.c */
2120 typedef struct _NotifyData NotifyData;
2122 struct _NotifyData {
2123 FileDataNotifyFunc func;
2125 NotifyPriority priority;
2128 static GList *notify_func_list = NULL;
2130 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2132 NotifyData *nda = (NotifyData *)a;
2133 NotifyData *ndb = (NotifyData *)b;
2135 if (nda->priority < ndb->priority) return -1;
2136 if (nda->priority > ndb->priority) return 1;
2140 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2144 nd = g_new(NotifyData, 1);
2147 nd->priority = priority;
2149 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2150 DEBUG_1("Notify func registered: %p", nd);
2155 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2157 GList *work = notify_func_list;
2161 NotifyData *nd = (NotifyData *)work->data;
2163 if (nd->func == func && nd->data == data)
2165 notify_func_list = g_list_delete_link(notify_func_list, work);
2167 DEBUG_1("Notify func unregistered: %p", nd);
2177 void file_data_send_notification(FileData *fd, NotifyType type)
2179 GList *work = notify_func_list;
2183 NotifyData *nd = (NotifyData *)work->data;
2185 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2186 nd->func(fd, type, nd->data);
2191 static GHashTable *file_data_monitor_pool = NULL;
2192 static gint realtime_monitor_id = -1;
2194 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2198 file_data_check_changed_files(fd);
2200 DEBUG_1("monitor %s", fd->path);
2203 static gboolean realtime_monitor_cb(gpointer data)
2205 if (!options->update_on_time_change) return TRUE;
2206 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2210 gint file_data_register_real_time_monitor(FileData *fd)
2216 if (!file_data_monitor_pool)
2217 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2219 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2221 DEBUG_1("Register realtime %d %s", count, fd->path);
2224 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2226 if (realtime_monitor_id == -1)
2228 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2234 gint file_data_unregister_real_time_monitor(FileData *fd)
2238 g_assert(file_data_monitor_pool);
2240 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2242 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2244 g_assert(count > 0);
2249 g_hash_table_remove(file_data_monitor_pool, fd);
2251 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2253 file_data_unref(fd);
2255 if (g_hash_table_size(file_data_monitor_pool) == 0)
2257 g_source_remove(realtime_monitor_id);
2258 realtime_monitor_id = -1;
2264 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */