changeset 1224:ebfd305d902e

improved sidecar writting private metadata can be saved in xmp format
author nadvornik
date Fri, 26 Dec 2008 14:12:36 +0000
parents 18d08b10b80e
children 3e5f52382573
files src/cache.c src/cache.h src/exif-common.c src/exiv2.cc src/filedata.c src/filedata.h src/metadata.c src/metadata.h src/utilops.c
diffstat 9 files changed, 198 insertions(+), 191 deletions(-) [+]
line wrap: on
line diff
--- a/src/cache.c	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/cache.c	Fri Dec 26 14:12:36 2008 +0000
@@ -603,6 +603,11 @@
 			*cache_local = GQ_CACHE_LOCAL_METADATA;
 			*cache_ext = GQ_CACHE_EXT_METADATA;
 			break;
+		case CACHE_TYPE_XMP_METADATA:
+			*cache_rc = get_metadata_cache_dir();
+			*cache_local = GQ_CACHE_LOCAL_METADATA;
+			*cache_ext = GQ_CACHE_EXT_XMP_METADATA;
+			break;
 		}
 }
 
@@ -625,8 +630,8 @@
 		name = g_strconcat(filename_from_path(source), cache_ext, NULL);
 		}
 
-	if (((type != CACHE_TYPE_METADATA && options->thumbnails.cache_into_dirs) ||
-	     (type == CACHE_TYPE_METADATA && options->metadata.enable_metadata_dirs)) &&
+	if (((type != CACHE_TYPE_METADATA && type != CACHE_TYPE_XMP_METADATA && options->thumbnails.cache_into_dirs) ||
+	     ((type == CACHE_TYPE_METADATA || type == CACHE_TYPE_XMP_METADATA) && options->metadata.enable_metadata_dirs)) &&
 	    access_file(base, W_OK))
 		{
 		path = g_build_filename(base, cache_local, name, NULL);
@@ -679,7 +684,7 @@
 
 	cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
 
-	if (type == CACHE_TYPE_METADATA)
+	if (type == CACHE_TYPE_METADATA || type == CACHE_TYPE_XMP_METADATA)
 		{
 		prefer_local = options->metadata.enable_metadata_dirs;
 		}
--- a/src/cache.h	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/cache.h	Fri Dec 26 14:12:36 2008 +0000
@@ -27,12 +27,14 @@
 #define GQ_CACHE_EXT_THUMB      ".png"
 #define GQ_CACHE_EXT_SIM        ".sim"
 #define GQ_CACHE_EXT_METADATA   ".meta"
+#define GQ_CACHE_EXT_XMP_METADATA   ".gq.xmp"
 
 
 typedef enum {
 	CACHE_TYPE_THUMB,
 	CACHE_TYPE_SIM,
-	CACHE_TYPE_METADATA
+	CACHE_TYPE_METADATA,
+	CACHE_TYPE_XMP_METADATA
 } CacheType;
 
 typedef struct _CacheData CacheData;
--- a/src/exif-common.c	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/exif-common.c	Fri Dec 26 14:12:36 2008 +0000
@@ -40,6 +40,7 @@
 #include "filecache.h"
 #include "format_raw.h"
 #include "ui_fileops.h"
+#include "cache.h"
 
 
 static gdouble exif_rational_to_double(ExifRational *r, gint sign)
@@ -602,32 +603,23 @@
 
 ExifData *exif_read_fd(FileData *fd)
 {
-	gchar *sidecar_path = NULL;
+	gchar *sidecar_path;
 
 	if (!fd) return NULL;
 	
 	if (!exif_cache) exif_cache = file_cache_new(exif_release_cb, 4);
 	
 	if (file_cache_get(exif_cache, fd)) return fd->exif;
+	
+	/* CACHE_TYPE_XMP_METADATA file should exist only if the metadata are
+	 * not writable directly, thus it should contain the most up-to-date version */
+	sidecar_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
 
-	if (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE))
-		{
-		GList *work;
-		
-		work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
-		while (work)
-			{
-			FileData *sfd = work->data;
-			work = work->next;
-			if (strcasecmp(sfd->extension, ".xmp") == 0)
-				{
-				sidecar_path = sfd->path;
-				break;
-				}
-			}
-		}
+	if (!sidecar_path) sidecar_path = file_data_get_sidecar_path(fd, TRUE);
 
 	fd->exif = exif_read(fd->path, sidecar_path, fd->modified_xmp);
+
+	g_free(sidecar_path);
 	return fd->exif;
 }
 
--- a/src/exiv2.cc	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/exiv2.cc	Fri Dec 26 14:12:36 2008 +0000
@@ -224,13 +224,16 @@
 		imageData_ = new _ExifDataOriginal(path);
 		sidecarData_ = NULL;
 #if EXIV2_TEST_VERSION(0,16,0)
-		xmpData_ = imageData_->xmpData();
-		DEBUG_2("xmp count %li", xmpData_.count());
-		if (sidecar_path && xmpData_.empty())
+		if (sidecar_path)
 			{
 			sidecarData_ = new _ExifDataOriginal(sidecar_path);
 			xmpData_ = sidecarData_->xmpData();
 			}
+		else
+			{
+			xmpData_ = imageData_->xmpData();
+			}
+
 #endif
 		exifData_ = imageData_->exifData();
 		iptcData_ = imageData_->iptcData();
--- a/src/filedata.c	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/filedata.c	Fri Dec 26 14:12:36 2008 +0000
@@ -661,7 +661,45 @@
 		fd->change = NULL;
 }
 
+static gboolean file_data_can_write_directly(FileData *fd)
+{
+	return (filter_file_class(fd->extension, FORMAT_CLASS_IMAGE));
+/* FIXME: detect what exiv2 really supports */
+}
 
+static gboolean file_data_can_write_sidecar(FileData *fd)
+{
+	return (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE));
+/* FIXME: detect what exiv2 really supports */
+}
+
+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 (strcasecmp(sfd->extension, ".xmp") == 0)
+			{
+			sidecar_path = g_strdup(sfd->path);
+			break;
+			}
+		}
+	
+	if (!existing_only && !sidecar_path)
+		{
+		gchar *base = remove_extension_from_path(fd->path);
+		sidecar_path = g_strconcat(base, ".xmp", NULL);
+		g_free(base);
+		}
+
+	return sidecar_path;
+}
 
 
 /*
@@ -1687,8 +1725,7 @@
 		return ret;
 		}
 
-	if (!isname(fd->path) && 
-	    !filter_file_class(fd->extension, FORMAT_CLASS_META)) /* xmp sidecar can be eventually created from scratch */
+	if (!isname(fd->path))
 		{
 		/* this probably should not happen */
 		ret |= CHANGE_NO_SRC;
@@ -1721,24 +1758,98 @@
 		}
 	/* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
 	   - that means that there are no hard errors and warnings can be disabled
+	   - the destination is determined during the check
 	*/
-	else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA && 
-	         options->metadata.save_in_image_file && options->metadata.warn_on_write_problems)
+	else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA)
 		{
-		if (isname(fd->path) && !access_file(fd->path, W_OK))
+		/* determine destination file */
+		gboolean have_dest = FALSE;
+		gchar *dest_dir = NULL;
+		
+		if (options->metadata.save_in_image_file)
 			{
-			ret |= CHANGE_WARN_NO_WRITE_PERM;
-			DEBUG_1("Change checked: file is readonly: %s", fd->path);
+			if (file_data_can_write_directly(fd)) 
+				{
+				/* we can write the file directly */
+				if (access_file(fd->path, W_OK))
+					{
+					have_dest = TRUE;
+					}
+				else
+					{
+					if (options->metadata.warn_on_write_problems)
+						{
+						ret |= CHANGE_WARN_NO_WRITE_PERM;
+						DEBUG_1("Change checked: file is not writable: %s", fd->path);
+						}
+					}
+				}
+			else if (file_data_can_write_sidecar(fd)) 
+				{
+				/* we can write sidecar */
+				gchar *sidecar = file_data_get_sidecar_path(fd, FALSE);
+				if (access_file(sidecar, W_OK) || (!isname(sidecar) && access_file(dir, W_OK)))
+					{
+					file_data_update_ci_dest(fd, sidecar);
+					have_dest = TRUE;
+					}
+				else
+					{
+					if (options->metadata.warn_on_write_problems)
+						{
+						ret |= CHANGE_WARN_NO_WRITE_PERM;
+						DEBUG_1("Change checked: file is not writable: %s", sidecar);
+						}
+					}
+				g_free(sidecar);
+				}
 			}
 		
-		else if (!access_file(dir, W_OK))
+		if (!have_dest)
 			{
-			ret |= CHANGE_WARN_NO_WRITE_PERM;
-			DEBUG_1("Change checked: dir is readonly: %s", fd->path);
+			/* write private metadata file under ~/.geeqie */
+
+			/* If an existing metadata file exists, we will try writing to
+			 * it's location regardless of the user's preference.
+			 */
+			gchar *metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
+			if (!metadata_path) metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
+			
+			if (metadata_path && !access_file(metadata_path, W_OK))
+				{
+				g_free(metadata_path);
+				metadata_path = NULL;
+				}
+
+			if (!metadata_path)
+				{
+				mode_t mode = 0755;
+
+				dest_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
+				if (recursive_mkdir_if_not_exists(dest_dir, mode))
+					{
+					gchar *filename = g_strconcat(fd->name, options->metadata.save_legacy_format ? GQ_CACHE_EXT_METADATA : GQ_CACHE_EXT_XMP_METADATA, NULL);
+			
+					metadata_path = g_build_filename(dest_dir, filename, NULL);
+					g_free(filename);
+					}
+				}
+			if (access_file(metadata_path, W_OK) || (!isname(metadata_path) && access_file(dest_dir, W_OK)))
+				{
+				file_data_update_ci_dest(fd, metadata_path);
+				have_dest = TRUE;
+				}
+			else
+				{
+				ret |= CHANGE_NO_WRITE_PERM_DEST;
+				DEBUG_1("Change checked: file is not writable: %s", metadata_path);
+				}
+			g_free(metadata_path);
 			}
+		g_free(dest_dir);
 		}
 		
-	if (fd->change->dest)
+	if (fd->change->dest && fd->change->type != FILEDATA_CHANGE_WRITE_METADATA)
 		{
 		gboolean same;
 		gchar *dest_dir;
--- a/src/filedata.h	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/filedata.h	Fri Dec 26 14:12:36 2008 +0000
@@ -79,6 +79,10 @@
 void file_data_set_user_orientation(FileData *fd, gint value);
 
 gchar *file_data_sc_list_to_string(FileData *fd);
+
+gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only);
+
+
 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest);
 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path);
 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path);
--- a/src/metadata.c	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/metadata.c	Fri Dec 26 14:12:36 2008 +0000
@@ -35,21 +35,8 @@
 
 static gboolean metadata_write_queue_idle_cb(gpointer data);
 static gint metadata_legacy_write(FileData *fd);
-static gint metadata_legacy_delete(FileData *fd);
-
-
+static void metadata_legacy_delete(FileData *fd, const gchar *except);
 
-gboolean metadata_can_write_directly(FileData *fd)
-{
-	return (filter_file_class(fd->extension, FORMAT_CLASS_IMAGE));
-/* FIXME: detect what exiv2 really supports */
-}
-
-gboolean metadata_can_write_sidecar(FileData *fd)
-{
-	return (filter_file_class(fd->extension, FORMAT_CLASS_RAWIMAGE));
-/* FIXME: detect what exiv2 really supports */
-}
 
 
 /*
@@ -61,61 +48,6 @@
 static GList *metadata_write_queue = NULL;
 static gint metadata_write_idle_id = -1;
 
-static FileData *metadata_xmp_sidecar_fd(FileData *fd)
-{
-	GList *work;
-	gchar *base, *new_name;
-	FileData *ret;
-	
-	if (!metadata_can_write_sidecar(fd)) return NULL;
-		
-	
-	if (fd->parent) fd = fd->parent;
-	
-	if (filter_file_class(fd->extension, FORMAT_CLASS_META))
-		return file_data_ref(fd);
-	
-	work = fd->sidecar_files;
-	while (work)
-		{
-		FileData *sfd = work->data;
-		work = work->next;
-		if (filter_file_class(sfd->extension, FORMAT_CLASS_META))
-			return file_data_ref(sfd);
-		}
-	
-	/* sidecar does not exist yet */
-	base = remove_extension_from_path(fd->path);
-	new_name = g_strconcat(base, ".xmp", NULL);
-	g_free(base);
-	ret = file_data_new_simple(new_name);
-	g_free(new_name);
-	return ret;
-}
-
-static FileData *metadata_xmp_main_fd(FileData *fd)
-{
-	if (filter_file_class(fd->extension, FORMAT_CLASS_META) && !g_list_find(metadata_write_queue, fd))
-		{
-		/* fd is a sidecar, we have to find the original file */
-		
-		GList *work = metadata_write_queue;
-		while (work)
-			{
-			FileData *ofd = work->data;
-			FileData *osfd = metadata_xmp_sidecar_fd(ofd);
-			work = work->next;
-			file_data_unref(osfd);
-			if (fd == osfd)
-				{
-				return ofd; /* this is the main file */
-				}
-			}
-		}
-	return NULL;
-}
-
-
 static void metadata_write_queue_add(FileData *fd)
 {
 	if (!g_list_find(metadata_write_queue, fd))
@@ -139,10 +71,6 @@
 
 gboolean metadata_write_queue_remove(FileData *fd)
 {
-	FileData *main_fd = metadata_xmp_main_fd(fd);
-
-	if (main_fd) fd = main_fd;
-
 	g_hash_table_destroy(fd->modified_xmp);
 	fd->modified_xmp = NULL;
 
@@ -184,11 +112,7 @@
 		
 		if (fd->change) continue; /* another operation in progress, skip this file for now */
 		
-		FileData *to_approve_fd = metadata_xmp_sidecar_fd(fd);
-		
-		if (!to_approve_fd) to_approve_fd = file_data_ref(fd); /* this is not a sidecar */
-
-		to_approve = g_list_prepend(to_approve, to_approve_fd);
+		to_approve = g_list_prepend(to_approve, fd);
 		}
 
 	file_util_write_metadata(NULL, to_approve, NULL);
@@ -205,44 +129,33 @@
 	return FALSE;
 }
 
-
-gboolean metadata_write_exif(FileData *fd, FileData *sfd)
+gboolean metadata_write_perform(FileData *fd)
 {
 	gboolean success;
 	ExifData *exif;
 	
+	g_assert(fd->change);
+	
+	if (fd->change->dest && 
+	    strcmp(extension_from_path(fd->path), GQ_CACHE_EXT_METADATA) == 0)
+		{
+		success = metadata_legacy_write(fd);
+		if (success) metadata_legacy_delete(fd, fd->change->dest);
+		return success;
+		}
+
+	/* write via exiv2 */
 	/*  we can either use cached metadata which have fd->modified_xmp already applied 
 	                             or read metadata from file and apply fd->modified_xmp
 	    metadata are read also if the file was modified meanwhile */
 	exif = exif_read_fd(fd); 
 	if (!exif) return FALSE;
-	success = sfd ? exif_write_sidecar(exif, sfd->path) : exif_write(exif); /* write modified metadata */
-	exif_free_fd(fd, exif);
-	return success;
-}
-
-gboolean metadata_write_perform(FileData *fd)
-{
-	FileData *sfd = NULL;
-	FileData *main_fd = metadata_xmp_main_fd(fd);
 
-	if (main_fd)
-		{
-		sfd = fd;
-		fd = main_fd;
-		}
+	success = (fd->change->dest) ? exif_write_sidecar(exif, fd->change->dest) : exif_write(exif); /* write modified metadata */
+	exif_free_fd(fd, exif);
 
-	if (options->metadata.save_in_image_file &&
-	    metadata_write_exif(fd, sfd))
-		{
-		metadata_legacy_delete(fd);
-		if (sfd) metadata_legacy_delete(sfd);
-		}
-	else
-		{
-		metadata_legacy_write(fd);
-		}
-	return TRUE;
+	if (success) metadata_legacy_delete(fd, fd->change->dest);
+	return success;
 }
 
 gint metadata_write_list(FileData *fd, const gchar *key, GList *values)
@@ -307,48 +220,18 @@
 
 static gint metadata_legacy_write(FileData *fd)
 {
-	gchar *metadata_path;
 	gint success = FALSE;
 
-	/* If an existing metadata file exists, we will try writing to
-	 * it's location regardless of the user's preference.
-	 */
-	metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
-	if (metadata_path && !access_file(metadata_path, W_OK))
-		{
-		g_free(metadata_path);
-		metadata_path = NULL;
-		}
+	g_assert(fd->change && fd->change->dest);
+	gchar *metadata_pathl;
 
-	if (!metadata_path)
-		{
-		gchar *metadata_dir;
-		mode_t mode = 0755;
+	DEBUG_1("Saving comment: %s", fd->change->dest);
 
-		metadata_dir = cache_get_location(CACHE_TYPE_METADATA, fd->path, FALSE, &mode);
-		if (recursive_mkdir_if_not_exists(metadata_dir, mode))
-			{
-			gchar *filename = g_strconcat(fd->name, GQ_CACHE_EXT_METADATA, NULL);
-			
-			metadata_path = g_build_filename(metadata_dir, filename, NULL);
-			g_free(filename);
-			}
-		g_free(metadata_dir);
-		}
+	metadata_pathl = path_from_utf8(fd->change->dest);
 
-	if (metadata_path)
-		{
-		gchar *metadata_pathl;
-
-		DEBUG_1("Saving comment: %s", metadata_path);
+	success = metadata_file_write(metadata_pathl, fd->modified_xmp);
 
-		metadata_pathl = path_from_utf8(metadata_path);
-
-		success = metadata_file_write(metadata_pathl, fd->modified_xmp);
-
-		g_free(metadata_pathl);
-		g_free(metadata_path);
-		}
+	g_free(metadata_pathl);
 
 	return success;
 }
@@ -439,24 +322,28 @@
 	return TRUE;
 }
 
-static gint metadata_legacy_delete(FileData *fd)
+static void metadata_legacy_delete(FileData *fd, const gchar *except)
 {
 	gchar *metadata_path;
 	gchar *metadata_pathl;
-	gint success = FALSE;
-	if (!fd) return FALSE;
+	if (!fd) return;
 
 	metadata_path = cache_find_location(CACHE_TYPE_METADATA, fd->path);
-	if (!metadata_path) return FALSE;
-
-	metadata_pathl = path_from_utf8(metadata_path);
-
-	success = !unlink(metadata_pathl);
-
-	g_free(metadata_pathl);
-	g_free(metadata_path);
-
-	return success;
+	if (metadata_path && (!except || strcmp(metadata_path, except) != 0)) 
+		{
+		metadata_pathl = path_from_utf8(metadata_path);
+		unlink(metadata_pathl);
+		g_free(metadata_pathl);
+		g_free(metadata_path);
+		}
+	metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
+	if (metadata_path && (!except || strcmp(metadata_path, except) != 0)) 
+		{
+		metadata_pathl = path_from_utf8(metadata_path);
+		unlink(metadata_pathl);
+		g_free(metadata_pathl);
+		g_free(metadata_path);
+		}
 }
 
 static gint metadata_legacy_read(FileData *fd, GList **keywords, gchar **comment)
--- a/src/metadata.h	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/metadata.h	Fri Dec 26 14:12:36 2008 +0000
@@ -13,6 +13,7 @@
 
 #ifndef METADATA_H
 #define METADATA_H
+
 gboolean metadata_write_queue_remove(FileData *fd);
 gboolean metadata_write_queue_remove_list(GList *list);
 gboolean metadata_write_perform(FileData *fd);
--- a/src/utilops.c	Thu Dec 25 12:39:34 2008 +0000
+++ b/src/utilops.c	Fri Dec 26 14:12:36 2008 +0000
@@ -448,11 +448,11 @@
 		gchar *sidecars;
 		
 		sidecars = with_sidecars ? file_data_sc_list_to_string(fd) : NULL;
-
+		GdkPixbuf *icon = file_util_get_error_icon(fd, view);
 		gtk_list_store_append(store, &iter);
 		gtk_list_store_set(store, &iter,
 				   UTILITY_COLUMN_FD, fd,
-				   UTILITY_COLUMN_PIXBUF, file_util_get_error_icon(fd, view),
+				   UTILITY_COLUMN_PIXBUF, icon,
 				   UTILITY_COLUMN_PATH, fd->path,
 				   UTILITY_COLUMN_NAME, fd->name,
 				   UTILITY_COLUMN_SIDECARS, sidecars,
@@ -1316,6 +1316,8 @@
 	ud->listview = file_util_dialog_add_list(box, ud->flist, FALSE, ud->with_sidecars);
 	if (ud->with_sidecars) file_util_dialog_add_list_column(ud->listview, _("Sidecars"), FALSE, UTILITY_COLUMN_SIDECARS);
 
+	if (ud->type == UTILITY_TYPE_WRITE_METADATA) file_util_dialog_add_list_column(ud->listview, _("Write to file"), FALSE, UTILITY_COLUMN_DEST_NAME);
+
 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ud->listview));
 	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
 	gtk_tree_selection_set_select_function(selection, file_util_preview_cb, ud, NULL);