# HG changeset patch # User nadvornik # Date 1230300756 0 # Node ID ebfd305d902e2d293b8c816901dc717498039a0f # Parent 18d08b10b80e2d18e2847d6bf8bcefdc867df295 improved sidecar writting private metadata can be saved in xmp format diff -r 18d08b10b80e -r ebfd305d902e src/cache.c --- 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; } diff -r 18d08b10b80e -r ebfd305d902e src/cache.h --- 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; diff -r 18d08b10b80e -r ebfd305d902e src/exif-common.c --- 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; } diff -r 18d08b10b80e -r ebfd305d902e src/exiv2.cc --- 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(); diff -r 18d08b10b80e -r ebfd305d902e src/filedata.c --- 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; diff -r 18d08b10b80e -r ebfd305d902e src/filedata.h --- 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); diff -r 18d08b10b80e -r ebfd305d902e src/metadata.c --- 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) diff -r 18d08b10b80e -r ebfd305d902e src/metadata.h --- 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); diff -r 18d08b10b80e -r ebfd305d902e src/utilops.c --- 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);