changeset 1936:ed6aa14b66c9

rename file_data_new_simple to file_data_new_group, filedata.c cleanup
author Vladimir Nadvornik <nadvornik@suse.cz>
date Tue, 04 Oct 2011 22:09:55 +0200
parents 5fab16d12a54
children b7ecaec17b74 1fa011010438
files src/bar_sort.c src/collect-io.c src/filedata.c src/filedata.h src/layout.c src/main.c src/metadata.c src/pan-timeline.c src/pan-view.c src/remote.c src/search.c src/thumb_standard.c src/utilops.c src/view_file_list.c
diffstat 14 files changed, 380 insertions(+), 352 deletions(-) [+]
line wrap: on
line diff
--- a/src/bar_sort.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/bar_sort.c	Tue Oct 04 22:09:55 2011 +0200
@@ -205,18 +205,18 @@
 			GList *list;
 			gchar *src_dir;
 
-			list = g_list_append(NULL, file_data_new_simple(sd->undo_dest));
+			list = g_list_append(NULL, file_data_new_group(sd->undo_dest));
 			src_dir = remove_level_from_path(sd->undo_src);
 			file_util_move_simple(list, src_dir, sd->lw->window);
 			g_free(src_dir);
 			}
 			break;
 		case BAR_SORT_COPY:
-			file_util_delete(file_data_new_simple(sd->undo_dest), NULL, button);
+			file_util_delete(file_data_new_group(sd->undo_dest), NULL, button);
 			break;
 		default:
 			/* undo external command */
-			file_util_delete(file_data_new_simple(sd->undo_dest), NULL, button);
+			file_util_delete(file_data_new_group(sd->undo_dest), NULL, button);
 			break;
 		}
 
@@ -224,7 +224,7 @@
 
 	if (isfile(sd->undo_src))
 		{
-		layout_image_set_fd(sd->lw, file_data_new_simple(sd->undo_src));
+		layout_image_set_fd(sd->lw, file_data_new_group(sd->undo_src));
 		}
 
 	bar_sort_undo_set(sd, NULL, NULL, NULL);
@@ -241,7 +241,7 @@
 
 		source = work->data;
 		work = work->next;
-		collect_manager_remove(file_data_new_simple(source), sd->undo_dest);
+		collect_manager_remove(file_data_new_group(source), sd->undo_dest);
 		}
 
 	bar_sort_undo_set(sd, NULL, NULL, NULL);
--- a/src/collect-io.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/collect-io.c	Tue Oct 04 22:09:55 2011 +0200
@@ -160,7 +160,7 @@
 			if (!flush)
 				changed |= collect_manager_process_action(entry, &buf);
 
-			valid = (buf[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE));
+			valid = (buf[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_group(buf), FALSE, TRUE));
 			if (!valid) DEBUG_1("collection invalid file: %s", buf);
 
 			total++;
@@ -190,7 +190,7 @@
 		gchar *buf = NULL;
 		while (collect_manager_process_action(entry, &buf))
 			{
-			collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE);
+			collection_add_check(cd, file_data_new_group(buf), FALSE, TRUE);
 			changed = TRUE;
 			g_free(buf);
 			buf = NULL;
--- a/src/filedata.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/filedata.c	Tue Oct 04 22:09:55 2011 +0200
@@ -28,7 +28,9 @@
 static GHashTable *file_data_planned_change_hash = NULL;
 
 static gint sidecar_file_priority(const gchar *extension);
-static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean disable_sidecars);
+static void file_data_check_sidecars(const GList *basename_list);
+static FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
+
 
 
 /*
@@ -133,15 +135,10 @@
 
 /*
  *-----------------------------------------------------------------------------
- * file info struct
+ * changed files detection and notification 
  *-----------------------------------------------------------------------------
  */
 
-static FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
-static void file_data_check_sidecars(const GList *basename_list);
-FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
-
-
 void file_data_increment_version(FileData *fd)
 {
 	fd->version++;
@@ -153,76 +150,99 @@
 		}
 }
 
-static gint file_data_sort_by_ext(gconstpointer a, gconstpointer b)
+static gboolean file_data_check_changed_single_file(FileData *fd, struct stat *st)
 {
-	const FileData *fda = a;
-	const FileData *fdb = b;
-	
-	if (fda->sidecar_priority < fdb->sidecar_priority) return -1;
-	if (fda->sidecar_priority > fdb->sidecar_priority) return 1;
-	
-	return strcmp(fdb->extension, fda->extension);
-}
-
-static GHashTable *file_data_basename_hash_new(void)
-{
-	return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+	if (fd->size != st->st_size ||
+	    fd->date != st->st_mtime)
+		{
+		fd->size = st->st_size;
+		fd->date = st->st_mtime;
+		fd->mode = st->st_mode;
+		if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
+		fd->thumb_pixbuf = NULL;
+		file_data_increment_version(fd);
+		file_data_send_notification(fd, NOTIFY_REREAD);
+		return TRUE;
+		}
+	return FALSE;
 }
 
-static GList * file_data_basename_hash_insert(GHashTable *basename_hash, FileData *fd)
+static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
 {
-	GList *list;
-	gchar *basename = g_strndup(fd->path, fd->extension - fd->path);
-
-	list = g_hash_table_lookup(basename_hash, basename);
+	gboolean ret = FALSE;
+	GList *work;
 	
-	if (!g_list_find(list, fd))
+	ret = file_data_check_changed_single_file(fd, st);
+
+	work = fd->sidecar_files;
+	while (work)
 		{
-		list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext);
-		g_hash_table_insert(basename_hash, basename, list);
+		FileData *sfd = work->data;
+		struct stat st;
+		work = work->next;
+
+		if (!stat_utf8(sfd->path, &st))
+			{
+			fd->size = 0;
+			fd->date = 0;
+			file_data_disconnect_sidecar_file(fd, sfd);
+			ret = TRUE;
+			continue;
+			}
+
+		ret |= file_data_check_changed_files_recursive(sfd, &st);
 		}
-	else 
-		{
-		g_free(basename);
-		}
-	return list;
+	return ret;
 }
 
-#if 0
-static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd)
+
+gboolean file_data_check_changed_files(FileData *fd)
 {
-	GList *list;
-	gchar *basename = g_strndup(fd->path, fd->extension - fd->path);
-	
-	list = g_hash_table_lookup(basename_hash, basename);
+	gboolean ret = FALSE;
+	struct stat st;
 	
-	if (!g_list_find(list, fd)) return;
-	
-	list = g_list_remove(list, fd);
-	file_data_unref(fd);
-	
-	if (list)
+	if (fd->parent) fd = fd->parent;
+
+	if (!stat_utf8(fd->path, &st))
 		{
-		g_hash_table_insert(basename_hash, basename, list);
+		GList *sidecars;
+		GList *work;
+		FileData *sfd = NULL;
+
+		/* parent is missing, we have to rebuild whole group */
+		ret = TRUE;
+		fd->size = 0;
+		fd->date = 0;
+		
+		/* file_data_disconnect_sidecar_file might delete the file,
+		   we have to keep the reference to prevent this */
+		sidecars = filelist_copy(fd->sidecar_files);
+		work = sidecars;
+		while (work)
+			{
+			sfd = work->data;
+			work = work->next;
+		
+			file_data_disconnect_sidecar_file(fd, sfd);
+			}
+		file_data_check_sidecars(sidecars); /* this will group the sidecars back together */
+		/* now we can release the sidecars */
+		filelist_free(sidecars);
+		file_data_send_notification(fd, NOTIFY_REREAD);
 		}
-	else 
+	else
 		{
-		g_hash_table_remove(basename_hash, basename);
-		g_free(basename);
+		ret |= file_data_check_changed_files_recursive(fd, &st);
 		}
+
+	return ret;
 }
-#endif
-
-static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data)
-{
-	filelist_free((GList *)value);
-}
-
-static void file_data_basename_hash_free(GHashTable *basename_hash)
-{
-	g_hash_table_foreach(basename_hash, file_data_basename_hash_remove_list, NULL); 
-	g_hash_table_destroy(basename_hash);
-}
+
+/*
+ *-----------------------------------------------------------------------------
+ * file name, extension, sorting, ... 
+ *-----------------------------------------------------------------------------
+ */
 
 static void file_data_set_collate_keys(FileData *fd)
 {
@@ -304,93 +324,11 @@
 	file_data_set_collate_keys(fd);
 }
 
-static gboolean file_data_check_changed(FileData *fd, struct stat *st)
-{
-	if (fd->size != st->st_size ||
-	    fd->date != st->st_mtime)
-		{
-		fd->size = st->st_size;
-		fd->date = st->st_mtime;
-		fd->mode = st->st_mode;
-		if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
-		fd->thumb_pixbuf = NULL;
-		file_data_increment_version(fd);
-		file_data_send_notification(fd, NOTIFY_REREAD);
-		return TRUE;
-		}
-	return FALSE;
-}
-
-static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
-{
-	gboolean ret = FALSE;
-	GList *work;
-	
-	ret = file_data_check_changed(fd, st);
-
-	work = fd->sidecar_files;
-	while (work)
-		{
-		FileData *sfd = work->data;
-		struct stat st;
-		work = work->next;
-
-		if (!stat_utf8(sfd->path, &st))
-			{
-			fd->size = 0;
-			fd->date = 0;
-			file_data_disconnect_sidecar_file(fd, sfd);
-			ret = TRUE;
-			continue;
-			}
-
-		ret |= file_data_check_changed_files_recursive(sfd, &st);
-		}
-	return ret;
-}
-
-
-gboolean file_data_check_changed_files(FileData *fd)
-{
-	gboolean ret = FALSE;
-	struct stat st;
-	
-	if (fd->parent) fd = fd->parent;
-
-	if (!stat_utf8(fd->path, &st))
-		{
-		GList *sidecars;
-		GList *work;
-		FileData *sfd = NULL;
-
-		/* parent is missing, we have to rebuild whole group */
-		ret = TRUE;
-		fd->size = 0;
-		fd->date = 0;
-		
-		/* file_data_disconnect_sidecar_file might delete the file,
-		   we have to keep the reference to prevent this */
-		sidecars = filelist_copy(fd->sidecar_files);
-		work = sidecars;
-		while (work)
-			{
-			sfd = work->data;
-			work = work->next;
-		
-			file_data_disconnect_sidecar_file(fd, sfd);
-			}
-		file_data_check_sidecars(sidecars); /* this will group the sidecars back together */
-		/* now we can release the sidecars */
-		filelist_free(sidecars);
-		file_data_send_notification(fd, NOTIFY_REREAD);
-		}
-	else
-		{
-		ret |= file_data_check_changed_files_recursive(fd, &st);
-		}
-
-	return ret;
-}
+/*
+ *-----------------------------------------------------------------------------
+ * create or reuse Filedata
+ *-----------------------------------------------------------------------------
+ */
 
 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean disable_sidecars)
 {
@@ -427,7 +365,7 @@
 		if (disable_sidecars) file_data_disable_grouping(fd, TRUE);
 		
 		
-		changed = file_data_check_changed(fd, st);
+		changed = file_data_check_changed_single_file(fd, st);
 
 		DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
 		
@@ -449,41 +387,6 @@
 	return fd;
 }
 
-
-static void file_data_check_sidecars(const GList *basename_list)
-{
-	GList *work;
-	FileData *parent_fd;
-	if (!basename_list) return;
-	/* process the group list - the first one is the parent file, others are sidecars */
-	parent_fd = basename_list->data;
-	work = basename_list->next;
-	while (work)
-		{
-		FileData *sfd = work->data;
-		work = work->next;
-
-		file_data_merge_sidecar_files(parent_fd, sfd);
-		}
-		
-	/* there may be some sidecars that are already deleted - disconnect them */
-	work = parent_fd->sidecar_files;
-	while (work)
-		{
-		FileData *sfd = work->data;
-		work = work->next;
-		
-		if (!g_list_find((GList *)basename_list, sfd)) 
-			{
-			printf("removing unknown %s: %s \n", parent_fd->path, sfd->path);
-			file_data_disconnect_sidecar_file(parent_fd, sfd);
-			file_data_send_notification(sfd, NOTIFY_REREAD);
-			file_data_send_notification(parent_fd, NOTIFY_REREAD);
-			}
-		}
-}
-
-
 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean disable_sidecars)
 {
 	gchar *path_utf8 = path_to_utf8(path);
@@ -493,36 +396,41 @@
 	return ret;
 }
 
-static FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
+FileData *file_data_new_no_grouping(const gchar *path_utf8)
 {
-	sfd->parent = target;
-	if (!g_list_find(target->sidecar_files, sfd))
-		target->sidecar_files = g_list_insert_sorted(target->sidecar_files, sfd, file_data_sort_by_ext);
-	file_data_increment_version(sfd); /* increments both sfd and target */
-	return target;
+	struct stat st;
+
+	if (!stat_utf8(path_utf8, &st))
+		{
+		st.st_size = 0;
+		st.st_mtime = 0;
+		}
+
+	return file_data_new(path_utf8, &st, TRUE);
 }
 
-
-static FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
+FileData *file_data_new_dir(const gchar *path_utf8)
 {
-	GList *work;
-	
-	file_data_add_sidecar_file(target, source);
-
-	work = source->sidecar_files;
-	while (work)
+	struct stat st;
+
+	if (!stat_utf8(path_utf8, &st))
 		{
-		FileData *sfd = work->data;
-		file_data_add_sidecar_file(target, sfd);
-		work = work->next;
+		st.st_size = 0;
+		st.st_mtime = 0;
 		}
-
-	g_list_free(source->sidecar_files);
-	source->sidecar_files = NULL;
-
-	return target;
+	else
+		/* dir or non-existing yet */
+		g_assert(S_ISDIR(st.st_mode));
+		
+	return file_data_new(path_utf8, &st, TRUE);
 }
 
+/*
+ *-----------------------------------------------------------------------------
+ * reference counting
+ *-----------------------------------------------------------------------------
+ */
+
 #ifdef DEBUG_FILEDATA
 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
 #else
@@ -619,7 +527,110 @@
 		}
 }
 
-FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * sidecar file info struct
+ *-----------------------------------------------------------------------------
+ */
+
+static gint file_data_sort_by_ext(gconstpointer a, gconstpointer b)
+{
+	const FileData *fda = a;
+	const FileData *fdb = b;
+	
+	if (fda->sidecar_priority < fdb->sidecar_priority) return -1;
+	if (fda->sidecar_priority > fdb->sidecar_priority) return 1;
+	
+	return strcmp(fdb->extension, fda->extension);
+}
+
+
+static gint sidecar_file_priority(const gchar *extension)
+{
+	gint i = 1;
+	GList *work;
+
+	if (extension == NULL)
+		return 0;
+
+	work = sidecar_ext_get_list();
+
+	while (work) {
+		gchar *ext = work->data;
+		
+		work = work->next;
+		if (g_ascii_strcasecmp(extension, ext) == 0) return i;
+		i++;
+	}
+	return 0;
+}
+
+static FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
+{
+	sfd->parent = target;
+	if (!g_list_find(target->sidecar_files, sfd))
+		target->sidecar_files = g_list_insert_sorted(target->sidecar_files, sfd, file_data_sort_by_ext);
+	file_data_increment_version(sfd); /* increments both sfd and target */
+	return target;
+}
+
+
+static FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
+{
+	GList *work;
+	
+	file_data_add_sidecar_file(target, source);
+
+	work = source->sidecar_files;
+	while (work)
+		{
+		FileData *sfd = work->data;
+		file_data_add_sidecar_file(target, sfd);
+		work = work->next;
+		}
+
+	g_list_free(source->sidecar_files);
+	source->sidecar_files = NULL;
+
+	return target;
+}
+
+static void file_data_check_sidecars(const GList *basename_list)
+{
+	GList *work;
+	FileData *parent_fd;
+	if (!basename_list) return;
+	/* process the group list - the first one is the parent file, others are sidecars */
+	parent_fd = basename_list->data;
+	work = basename_list->next;
+	while (work)
+		{
+		FileData *sfd = work->data;
+		work = work->next;
+
+		file_data_merge_sidecar_files(parent_fd, sfd);
+		}
+		
+	/* there may be some sidecars that are already deleted - disconnect them */
+	work = parent_fd->sidecar_files;
+	while (work)
+		{
+		FileData *sfd = work->data;
+		work = work->next;
+		
+		if (!g_list_find((GList *)basename_list, sfd)) 
+			{
+			printf("removing unknown %s: %s \n", parent_fd->path, sfd->path);
+			file_data_disconnect_sidecar_file(parent_fd, sfd);
+			file_data_send_notification(sfd, NOTIFY_REREAD);
+			file_data_send_notification(parent_fd, NOTIFY_REREAD);
+			}
+		}
+}
+
+static FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
 {
 	sfd->parent = target;
 	g_assert(g_list_find(target->sidecar_files, sfd));
@@ -696,104 +707,10 @@
 }
 
 
-/* compare name without extension */
-gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
-{
-	size_t len1 = fd1->extension - fd1->name;
-	size_t len2 = fd2->extension - fd2->name;
-
-	if (len1 < len2) return -1;
-	if (len1 > len2) return 1;
-
-	return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
-}
-
-void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
-{
-	if (!fdci && fd) fdci = fd->change;
-
-	if (!fdci) return;
-
-	g_free(fdci->source);
-	g_free(fdci->dest);
-
-	g_free(fdci);
-
-	if (fd) fd->change = NULL;
-}
-
-static gboolean file_data_can_write_directly(FileData *fd)
-{
-	return filter_name_is_writable(fd->extension);
-}
-
-static gboolean file_data_can_write_sidecar(FileData *fd)
-{
-	return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
-}
-
-gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
-{
-	gchar *sidecar_path = NULL;
-	GList *work;
-	
-	if (!file_data_can_write_sidecar(fd)) return NULL;
-	
-	work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
-	while (work)
-		{
-		FileData *sfd = work->data;
-		work = work->next;
-		if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
-			{
-			sidecar_path = g_strdup(sfd->path);
-			break;
-			}
-		}
-	
-	if (!existing_only && !sidecar_path)
-		{
-		gchar *base = g_strndup(fd->path, fd->extension - fd->path);
-		sidecar_path = g_strconcat(base, ".xmp", NULL);
-		g_free(base);
-		}
-
-	return sidecar_path;
-}
-
 
 /*
  *-----------------------------------------------------------------------------
- * sidecar file info struct
- *-----------------------------------------------------------------------------
- */
-
-
-
-static gint sidecar_file_priority(const gchar *extension)
-{
-	gint i = 1;
-	GList *work;
-
-	if (extension == NULL)
-		return 0;
-
-	work = sidecar_ext_get_list();
-
-	while (work) {
-		gchar *ext = work->data;
-		
-		work = work->next;
-		if (g_ascii_strcasecmp(extension, ext) == 0) return i;
-		i++;
-	}
-	return 0;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- * load file list
+ * filelist sorting
  *-----------------------------------------------------------------------------
  */
 
@@ -884,6 +801,78 @@
 	return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
 }
 
+/*
+ *-----------------------------------------------------------------------------
+ * basename hash - grouping of sidecars in filelist
+ *-----------------------------------------------------------------------------
+ */
+
+
+static GHashTable *file_data_basename_hash_new(void)
+{
+	return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+}
+
+static GList * file_data_basename_hash_insert(GHashTable *basename_hash, FileData *fd)
+{
+	GList *list;
+	gchar *basename = g_strndup(fd->path, fd->extension - fd->path);
+
+	list = g_hash_table_lookup(basename_hash, basename);
+	
+	if (!g_list_find(list, fd))
+		{
+		list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext);
+		g_hash_table_insert(basename_hash, basename, list);
+		}
+	else 
+		{
+		g_free(basename);
+		}
+	return list;
+}
+
+#if 0
+static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd)
+{
+	GList *list;
+	gchar *basename = g_strndup(fd->path, fd->extension - fd->path);
+	
+	list = g_hash_table_lookup(basename_hash, basename);
+	
+	if (!g_list_find(list, fd)) return;
+	
+	list = g_list_remove(list, fd);
+	file_data_unref(fd);
+	
+	if (list)
+		{
+		g_hash_table_insert(basename_hash, basename, list);
+		}
+	else 
+		{
+		g_hash_table_remove(basename_hash, basename);
+		g_free(basename);
+		}
+}
+#endif
+
+static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data)
+{
+	filelist_free((GList *)value);
+}
+
+static void file_data_basename_hash_free(GHashTable *basename_hash)
+{
+	g_hash_table_foreach(basename_hash, file_data_basename_hash_remove_list, NULL); 
+	g_hash_table_destroy(basename_hash);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * handling sidecars in filelist
+ *-----------------------------------------------------------------------------
+ */
 
 static GList *filelist_filter_out_sidecars(GList *flist)
 {
@@ -919,6 +908,12 @@
 	return TRUE;
 }
 
+/*
+ *-----------------------------------------------------------------------------
+ * the main filelist function
+ *-----------------------------------------------------------------------------
+ */
+
 static gboolean filelist_read_real(const gchar *dir_path, GList **files, GList **dirs, gboolean follow_symlinks)
 {
 	DIR *dp;
@@ -1024,7 +1019,7 @@
 	return filelist_read_real(dir_fd->path, files, dirs, FALSE);
 }
 
-FileData *file_data_new_simple(const gchar *path_utf8)
+FileData *file_data_new_group(const gchar *path_utf8)
 {
 	gchar *dir;
 	struct stat st;
@@ -1053,32 +1048,6 @@
 	return fd;
 }
 
-FileData *file_data_new_no_grouping(const gchar *path_utf8)
-{
-	struct stat st;
-
-	if (!stat_utf8(path_utf8, &st))
-		{
-		st.st_size = 0;
-		st.st_mtime = 0;
-		}
-
-	return file_data_new(path_utf8, &st, TRUE);
-}
-
-FileData *file_data_new_dir(const gchar *path_utf8)
-{
-	struct stat st;
-
-	if (!stat_utf8(path_utf8, &st))
-		{
-		st.st_size = 0;
-		st.st_mtime = 0;
-		}
-
-	g_assert(S_ISDIR(st.st_mode));
-	return file_data_new(path_utf8, &st, TRUE);
-}
 
 void filelist_free(GList *list)
 {
@@ -1127,7 +1096,7 @@
 		path = work->data;
 		work = work->next;
 
-		new_list = g_list_prepend(new_list, file_data_new_simple(path));
+		new_list = g_list_prepend(new_list, file_data_new_group(path));
 		}
 
 	return g_list_reverse(new_list);
@@ -1242,6 +1211,65 @@
 	return list;
 }
 
+/*
+ *-----------------------------------------------------------------------------
+ * file modification support
+ *-----------------------------------------------------------------------------
+ */
+
+
+void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
+{
+	if (!fdci && fd) fdci = fd->change;
+
+	if (!fdci) return;
+
+	g_free(fdci->source);
+	g_free(fdci->dest);
+
+	g_free(fdci);
+
+	if (fd) fd->change = NULL;
+}
+
+static gboolean file_data_can_write_directly(FileData *fd)
+{
+	return filter_name_is_writable(fd->extension);
+}
+
+static gboolean file_data_can_write_sidecar(FileData *fd)
+{
+	return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension);
+}
+
+gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
+{
+	gchar *sidecar_path = NULL;
+	GList *work;
+	
+	if (!file_data_can_write_sidecar(fd)) return NULL;
+	
+	work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
+	while (work)
+		{
+		FileData *sfd = work->data;
+		work = work->next;
+		if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
+			{
+			sidecar_path = g_strdup(sfd->path);
+			break;
+			}
+		}
+	
+	if (!existing_only && !sidecar_path)
+		{
+		gchar *base = g_strndup(fd->path, fd->extension - fd->path);
+		sidecar_path = g_strconcat(base, ".xmp", NULL);
+		g_free(base);
+		}
+
+	return sidecar_path;
+}
 
 /*
  * marks and orientation
--- a/src/filedata.h	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/filedata.h	Tue Oct 04 22:09:55 2011 +0200
@@ -23,7 +23,7 @@
 const gchar *text_from_time(time_t t);
 
 /* scan for sidecar files - expensive */
-FileData *file_data_new_simple(const gchar *path_utf8);
+FileData *file_data_new_group(const gchar *path_utf8);
 
 /* should be used on helper files which can't have sidecars */
 FileData *file_data_new_no_grouping(const gchar *path_utf8);
--- a/src/layout.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/layout.c	Tue Oct 04 22:09:55 2011 +0200
@@ -851,7 +851,7 @@
 
 	if (!path) return FALSE;
 	
-	fd = file_data_new_simple(path);
+	fd = file_data_new_group(path);
 	ret = layout_set_fd(lw, fd);
 	file_data_unref(fd);
 	return ret;
--- a/src/main.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/main.c	Tue Oct 04 22:09:55 2011 +0200
@@ -125,7 +125,7 @@
 		{
 		if (!*path) *path = remove_level_from_path(path_parsed);
 		if (!*file) *file = g_strdup(path_parsed);
-		*list = g_list_prepend(*list, file_data_new_simple(path_parsed));
+		*list = g_list_prepend(*list, file_data_new_group(path_parsed));
 		}
 }
 
--- a/src/metadata.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/metadata.c	Tue Oct 04 22:09:55 2011 +0200
@@ -318,7 +318,7 @@
 		    store the metadata in the cache)
 		    FIXME: this does not catch new sidecars created by independent external programs
 		*/
-		file_data_unref(file_data_new_simple(fd->change->dest)); 
+		file_data_unref(file_data_new_group(fd->change->dest)); 
 		
 	if (success) metadata_legacy_delete(fd, fd->change->dest);
 	return success;
--- a/src/pan-timeline.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/pan-timeline.c	Tue Oct 04 22:09:55 2011 +0200
@@ -91,7 +91,7 @@
 				g_free(buf);
 				y += pi->height;
 
-				pi_month = pan_item_box_new(pw, file_data_new_simple(fd->path),
+				pi_month = pan_item_box_new(pw, file_data_ref(fd),
 							    x, y, 0, 0,
 							    PAN_BOX_OUTLINE_THICKNESS,
 							    PAN_BOX_COLOR, PAN_BOX_ALPHA,
--- a/src/pan-view.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/pan-view.c	Tue Oct 04 22:09:55 2011 +0200
@@ -1604,7 +1604,7 @@
 						PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA);
 			pan_item_set_key(pbox, "info");
 
-			p = pan_item_image_new(pw, file_data_new_simple(pi->fd->path),
+			p = pan_item_image_new(pw, file_data_new_group(pi->fd->path),
 					       pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih);
 			pan_item_set_key(p, "info");
 			pan_item_size_by_item(pbox, p, PREF_PAD_BORDER);
--- a/src/remote.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/remote.c	Tue Oct 04 22:09:55 2011 +0200
@@ -532,7 +532,7 @@
 static void gr_get_sidecars(const gchar *text, GIOChannel *channel, gpointer data)
 {
 	gchar *filename = expand_tilde(text);
-	FileData *fd = file_data_new_simple(filename);
+	FileData *fd = file_data_new_group(filename);
 	
 	GList *work;
 	if (fd->parent) fd = fd->parent;
@@ -555,7 +555,7 @@
 static void gr_get_destination(const gchar *text, GIOChannel *channel, gpointer data)
 {
 	gchar *filename = expand_tilde(text);
-	FileData *fd = file_data_new_simple(filename);
+	FileData *fd = file_data_new_group(filename);
 	
 	if (fd->change && fd->change->dest)
 		{
@@ -569,7 +569,7 @@
 {
 	gchar *filename = expand_tilde(text);
 
-	view_window_new(file_data_new_simple(filename));
+	view_window_new(file_data_new_group(filename));
 	g_free(filename);
 }
 
@@ -607,7 +607,7 @@
 		new = (!collection_get_first(remote_data->command_collection));
 		}
 
-	if (collection_add(remote_data->command_collection, file_data_new_simple(text), FALSE) && new)
+	if (collection_add(remote_data->command_collection, file_data_new_group(text), FALSE) && new)
 		{
 		layout_image_set_collection(NULL, remote_data->command_collection,
 					    collection_get_first(remote_data->command_collection));
--- a/src/search.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/search.c	Tue Oct 04 22:09:55 2011 +0200
@@ -2124,7 +2124,7 @@
 				sd->search_similarity_cd = cache_sim_data_new();
 				}
 
-			sd->img_loader = image_loader_new(file_data_new_simple(sd->search_similarity_path));
+			sd->img_loader = image_loader_new(file_data_new_group(sd->search_similarity_path));
 			g_signal_connect(G_OBJECT(sd->img_loader), "error", (GCallback)search_similarity_load_done_cb, sd);
 			g_signal_connect(G_OBJECT(sd->img_loader), "done", (GCallback)search_similarity_load_done_cb, sd);
 			if (image_loader_start(sd->img_loader))
--- a/src/thumb_standard.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/thumb_standard.c	Tue Oct 04 22:09:55 2011 +0200
@@ -983,7 +983,7 @@
 			tm->tl->cache_hit = FALSE;
 			tm->tl->cache_local = FALSE;
 			file_data_unref(tm->tl->fd);
-			tm->tl->fd = file_data_new_simple(tm->dest);
+			tm->tl->fd = file_data_new_group(tm->dest);
 			tm->tl->source_mtime = strtol(mtime_str, NULL, 10);
 
 			pathl = path_from_utf8(tm->tl->fd->path);
--- a/src/utilops.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/utilops.c	Tue Oct 04 22:09:55 2011 +0200
@@ -1074,7 +1074,7 @@
 			break;
 		case UTILITY_TYPE_CREATE_FOLDER:
 			file_data_unref(ud->dir_fd);
-			ud->dir_fd = file_data_new_simple(ud->dest_path);
+			ud->dir_fd = file_data_new_dir(ud->dest_path);
 			break;
 		case UTILITY_TYPE_DELETE:
 		case UTILITY_TYPE_DELETE_LINK:
@@ -2676,7 +2676,7 @@
 		g_free(buf);
 		}
 	
-	ud->dir_fd = file_data_new_simple(ud->dest_path);
+	ud->dir_fd = file_data_new_dir(ud->dest_path);
 
 	ud->done_func = done_func;
 	ud->done_data = done_data;
--- a/src/view_file_list.c	Sun Oct 02 11:55:34 2011 +0200
+++ b/src/view_file_list.c	Tue Oct 04 22:09:55 2011 +0200
@@ -516,7 +516,7 @@
 	else
 		{
 		gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL);
-		FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */
+		FileData *fd = file_data_new_group(old_path); /* get the fd from cache */
 		file_util_rename_simple(fd, new_path, vf->listview);
 		file_data_unref(fd);
 		g_free(old_path);