improved sidecar writting
authorVladimir Nadvornik <nadvornik@suse.cz>
Fri, 26 Dec 2008 14:12:36 +0000 (14:12 +0000)
committerVladimir Nadvornik <nadvornik@suse.cz>
Fri, 26 Dec 2008 14:12:36 +0000 (14:12 +0000)
private metadata can be saved in xmp format

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

index 13e3d06..53798c1 100644 (file)
@@ -603,6 +603,11 @@ static void cache_path_parts(CacheType type,
                        *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 @@ gchar *cache_get_location(CacheType type, const gchar *source, gint include_name
                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 @@ gchar *cache_find_location(CacheType type, const gchar *source)
 
        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;
                }
index d52d74d..b5a2969 100644 (file)
 #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;
index 5966506..84904f1 100644 (file)
@@ -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 @@ void exif_release_cb(FileData *fd)
 
 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;
 }
 
index 975e8be..3ac42a3 100644 (file)
@@ -224,13 +224,16 @@ public:
                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();
index b5d3d28..401499f 100644 (file)
@@ -661,7 +661,45 @@ void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
                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 @@ gint file_data_verify_ci(FileData *fd)
                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 @@ gint file_data_verify_ci(FileData *fd)
                }
        /* 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;
index 3f72d75..43e4857 100644 (file)
@@ -79,6 +79,10 @@ gint file_data_get_user_orientation(FileData *fd);
 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);
index a79ef22..c23f2a0 100644 (file)
@@ -35,23 +35,10 @@ typedef enum {
 
 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 */
-}
-
-
 /*
  *-------------------------------------------------------------------
  * write queue
@@ -61,61 +48,6 @@ gboolean metadata_can_write_sidecar(FileData *fd)
 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 @@ static void metadata_write_queue_add(FileData *fd)
 
 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 @@ gboolean metadata_write_queue_confirm()
                
                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 @@ static gboolean metadata_write_queue_idle_cb(gpointer data)
        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_file_write(gchar *path, GHashTable *modified_xmp)
 
 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;
-               }
-
-       if (!metadata_path)
-               {
-               gchar *metadata_dir;
-               mode_t mode = 0755;
-
-               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);
-               }
-
-       if (metadata_path)
-               {
-               gchar *metadata_pathl;
+       g_assert(fd->change && fd->change->dest);
+       gchar *metadata_pathl;
 
-               DEBUG_1("Saving comment: %s", metadata_path);
+       DEBUG_1("Saving comment: %s", fd->change->dest);
 
-               metadata_pathl = path_from_utf8(metadata_path);
+       metadata_pathl = path_from_utf8(fd->change->dest);
 
-               success = metadata_file_write(metadata_pathl, fd->modified_xmp);
+       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 @@ static gint metadata_file_read(gchar *path, GList **keywords, gchar **comment)
        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)
index 957b7b2..2a176fb 100644 (file)
@@ -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);
index bd8322e..e237654 100644 (file)
@@ -448,11 +448,11 @@ static GtkWidget *file_util_dialog_add_list(GtkWidget *box, GList *list, gint fu
                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 @@ static void file_util_dialog_init_simple_list(UtilityData *ud)
        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);