*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;
}
}
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);
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;
}
#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;
#include "filecache.h"
#include "format_raw.h"
#include "ui_fileops.h"
+#include "cache.h"
static gdouble exif_rational_to_double(ExifRational *r, gint sign)
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;
}
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();
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;
+}
/*
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;
}
/* 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;
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);
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
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))
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;
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);
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)
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;
}
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)
#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);
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,
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);