cache_maint_removed(fd);
break;
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
}
collect_manager_moved(fd);
break;
case FILEDATA_CHANGE_DELETE:
- break;
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
while (collection_remove(cd, fd));
break;
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
while (dupe_item_remove_by_path(dw, fd->path));
break;
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
return fd->exif;
}
-gint exif_write_fd(FileData *fd)
-{
- gint success;
- ExifData *exif;
-
- /* exif_read_fd 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 = exif_write(exif); /* write modified metadata */
- exif_free_fd(fd, exif);
- return success;
-}
void exif_free_fd(FileData *fd, ExifData *exif)
{
fprintf(f, "----------------------------------------------------\n");
}
-gint exif_write(ExifData *exif)
+gboolean exif_write(ExifData *exif)
{
log_printf("Not compiled with EXIF write support");
- return 0;
+ return FALSE;
+}
+
+gboolean exif_write_sidecar(ExifData *exif, gchar *path)
+{
+ log_printf("Not compiled with EXIF write support");
+ return FALSE;
}
ExifData *exif_read_fd(FileData *fd);
void exif_free_fd(FileData *fd, ExifData *exif);
-gint exif_write_fd(FileData *fd);
/* exif_read returns processed data (merged from image and sidecar, etc.)
this function gives access to the original data from the image.
original data are part of the processed data and should not be freed separately */
ExifData *exif_get_original(ExifData *processed);
-gint exif_write(ExifData *exif);
+
+gboolean exif_write(ExifData *exif);
+gboolean exif_write_sidecar(ExifData *exif, gchar *path);
+
void exif_free(ExifData *exif);
gchar *exif_get_data_as_text(ExifData *exif, const gchar *key);
{
}
- virtual void writeMetadata()
+ virtual void writeMetadata(gchar *path = NULL)
{
g_critical("Unsupported method of writing metadata");
}
return imageData_;
}
- virtual void writeMetadata()
+ virtual void writeMetadata(gchar *path = NULL)
{
#if EXIV2_TEST_VERSION(0,17,0)
syncExifWithXmp(exifData_, xmpData_);
- copyXmpToIptc(xmpData_, iptcData_); //FIXME it should be configurable
#endif
- if (sidecarData_)
+ if (!path)
{
- sidecarData_->image()->setXmpData(xmpData_);
- //Exiv2 sidecar converts xmp to exif and iptc, we don't want it.
- sidecarData_->image()->clearExifData();
- sidecarData_->image()->clearIptcData();
- sidecarData_->image()->writeMetadata();
- }
+#if EXIV2_TEST_VERSION(0,17,0)
+ if (options->metadata.save_legacy_IPTC) copyXmpToIptc(xmpData_, iptcData_);
+#endif
+ imageData_->image()->setExifData(exifData_);
+ imageData_->image()->setIptcData(iptcData_);
+ imageData_->image()->setXmpData(xmpData_);
+ imageData_->image()->writeMetadata();
+ }
else
{
- try {
- imageData_->image()->setExifData(exifData_);
- imageData_->image()->setIptcData(iptcData_);
- imageData_->image()->setXmpData(xmpData_);
- imageData_->image()->writeMetadata();
- }
- catch (Exiv2::AnyError& e)
- {
- // can't write into the image, create sidecar
#if EXIV2_TEST_VERSION(0,17,0)
- gchar *base = remove_extension_from_path(imageData_->image()->io().path().c_str());
- gchar *pathl = g_strconcat(base, ".xmp", NULL);
+ gchar *pathl = path_from_utf8(path);;
- Exiv2::Image::AutoPtr sidecar = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, pathl);
+ Exiv2::Image::AutoPtr sidecar = Exiv2::ImageFactory::create(Exiv2::ImageType::xmp, pathl);
- g_free(base);
- g_free(pathl);
-
- sidecarData_ = new _ExifDataOriginal(sidecar);
- sidecarData_->image()->setXmpData(xmpData_);
- sidecarData_->image()->writeMetadata();
+ g_free(pathl);
+
+ sidecar->setXmpData(xmpData_);
+ sidecar->writeMetadata();
+#else
+ throw Exiv2::Error(3, "xmp");
#endif
- }
}
}
}
-int exif_write(ExifData *exif)
+gboolean exif_write(ExifData *exif)
{
try {
exif->writeMetadata();
- return 1;
+ return TRUE;
}
catch (Exiv2::AnyError& e) {
std::cout << "Caught Exiv2 exception '" << e << "'\n";
- return 0;
+ return FALSE;
+ }
+}
+
+gboolean exif_write_sidecar(ExifData *exif, gchar *path)
+{
+ try {
+ exif->writeMetadata(path);
+ return TRUE;
+ }
+ catch (Exiv2::AnyError& e) {
+ std::cout << "Caught Exiv2 exception '" << e << "'\n";
+ return FALSE;
}
}
}
-
/*
* file_data - operates on the given fd
* file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
return ret;
}
- if (!isname(fd->path))
+ if (!isname(fd->path) &&
+ !filter_file_class(fd->extension, FORMAT_CLASS_META)) /* xmp sidecar can be eventually created from scratch */
{
/* this probably should not happen */
ret |= CHANGE_NO_SRC;
dir = remove_level_from_path(fd->path);
if (fd->change->type != FILEDATA_CHANGE_DELETE &&
+ fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
!access_file(fd->path, R_OK))
{
ret |= CHANGE_NO_READ_PERM;
}
else if (fd->change->type != FILEDATA_CHANGE_COPY &&
fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
+ fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
!access_file(fd->path, W_OK))
{
ret |= CHANGE_WARN_NO_WRITE_PERM;
DEBUG_1("Change checked: no write permission: %s", fd->path);
}
-
+ /* 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
+ */
+ else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA &&
+ options->metadata.save_in_image_file && options->metadata.warn_on_write_problems)
+ {
+ if (isname(fd->path) && !access_file(fd->path, W_OK))
+ {
+ ret |= CHANGE_WARN_NO_WRITE_PERM;
+ DEBUG_1("Change checked: file is readonly: %s", fd->path);
+ }
+
+ else if (!access_file(dir, W_OK))
+ {
+ ret |= CHANGE_WARN_NO_WRITE_PERM;
+ DEBUG_1("Change checked: dir is readonly: %s", fd->path);
+ }
+ }
+
if (fd->change->dest)
{
gboolean same;
view_real_removed(vw, fd);
break;
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
}
break;
case FILEDATA_CHANGE_COPY:
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
#include "ui_fileops.h"
#include "ui_misc.h"
#include "utilops.h"
+#include "filefilter.h"
typedef enum {
MK_NONE,
static gint metadata_legacy_delete(FileData *fd);
+
+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)
{
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;
metadata_write_queue = g_list_remove(metadata_write_queue, fd);
+
+ file_data_increment_version(fd);
+ file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
+
file_data_unref(fd);
return TRUE;
}
+gboolean metadata_write_queue_remove_list(GList *list)
+{
+ GList *work;
+ gboolean ret = TRUE;
+
+ work = list;
+ while (work)
+ {
+ FileData *fd = work->data;
+ work = work->next;
+ ret = ret && metadata_write_queue_remove(fd);
+ }
+ return ret;
+}
+
static gboolean metadata_write_queue_idle_cb(gpointer data)
{
- /* TODO: the queue should not be passed to file_util_write_metadata directly:
- metatata under .geeqie/ can be written immediately,
- for others we can decide if we write to the image file or to sidecar */
+ GList *work;
+ GList *to_approve = NULL;
+ work = metadata_write_queue;
+ while (work)
+ {
+ FileData *fd = work->data;
+ work = work->next;
+
+ 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 */
-// if (metadata_write_queue) return TRUE;
+ to_approve = g_list_prepend(to_approve, to_approve_fd);
+ }
- /* confirm writting */
- file_util_write_metadata(NULL, metadata_write_queue, NULL);
+ file_util_write_metadata(NULL, to_approve, NULL);
+
+ filelist_free(to_approve);
metadata_write_idle_id = -1;
return FALSE;
}
+gboolean metadata_write_exif(FileData *fd, FileData *sfd)
+{
+ gboolean success;
+ ExifData *exif;
+
+ /* 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;
+ }
+
if (options->metadata.save_in_image_file &&
- exif_write_fd(fd))
+ metadata_write_exif(fd, sfd))
{
metadata_legacy_delete(fd);
+ if (sfd) metadata_legacy_delete(sfd);
+ }
+ else
+ {
+ metadata_legacy_write(fd);
}
- else metadata_legacy_write(fd);
return TRUE;
}
if (!fd) return FALSE;
if (write_comment) success = success && metadata_write_string(fd, COMMENT_KEY, comment);
-
if (keywords) success = success && metadata_write_list(fd, KEYWORD_KEY, string_list_copy(keywords));
+
+ if (options->metadata.sync_grouped_files)
+ {
+ GList *work = fd->sidecar_files;
+
+ while (work)
+ {
+ FileData *sfd = work->data;
+ work = work->next;
+
+ if (filter_file_class(sfd->extension, FORMAT_CLASS_META)) continue;
+
+ if (write_comment) success = success && metadata_write_string(sfd, COMMENT_KEY, comment);
+ if (keywords) success = success && metadata_write_list(sfd, KEYWORD_KEY, string_list_copy(keywords));
+ }
+ }
return success;
}
#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);
break;
case FILEDATA_CHANGE_COPY:
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
}
}
}
-static GtkWidget *file_util_dialog_add_list(GtkWidget *box, GList *list, gint full_paths)
+static GtkWidget *file_util_dialog_add_list(GtkWidget *box, GList *list, gint full_paths, gboolean with_sidecars)
{
GtkWidget *scrolled;
GtkWidget *view;
GtkTreeIter iter;
gchar *sidecars;
- sidecars = file_data_sc_list_to_string(fd);
+ sidecars = with_sidecars ? file_data_sc_list_to_string(fd) : NULL;
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
box = pref_group_new(box, TRUE, ud->messages.desc_flist, GTK_ORIENTATION_HORIZONTAL);
- ud->listview = file_util_dialog_add_list(box, ud->flist, FALSE);
- file_util_dialog_add_list_column(ud->listview, _("Sidecars"), FALSE, UTILITY_COLUMN_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);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ud->listview));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
box = pref_group_new(box, TRUE, ud->messages.desc_flist, GTK_ORIENTATION_HORIZONTAL);
- ud->listview = file_util_dialog_add_list(box, ud->flist, FALSE);
+ ud->listview = file_util_dialog_add_list(box, ud->flist, FALSE, ud->with_sidecars);
file_util_dialog_add_list_column(ud->listview, _("Sidecars"), FALSE, UTILITY_COLUMN_SIDECARS);
file_util_dialog_add_list_column(ud->listview, _("New name"), FALSE, UTILITY_COLUMN_DEST_NAME);
break;
case UTILITY_PHASE_CANCEL:
case UTILITY_PHASE_DONE:
+
+ /* FIXME: put it here for now */
+ if (ud->type == UTILITY_TYPE_WRITE_METADATA)
+ {
+ metadata_write_queue_remove_list(ud->flist);
+ }
if (ud->with_sidecars)
file_data_sc_free_ci_list(ud->flist);
else
box = pref_group_new(box, TRUE, _("Subfolders:"), GTK_ORIENTATION_VERTICAL);
rlist = filelist_sort_path(rlist);
- file_util_dialog_add_list(box, rlist, FALSE);
+ file_util_dialog_add_list(box, rlist, FALSE, FALSE);
gtk_widget_show(gd->dialog);
}
void file_util_write_metadata(FileData *source_fd, GList *source_list, GtkWidget *parent)
{
- file_util_write_metadata_full(source_fd, source_list, parent, UTILITY_PHASE_START);
+ file_util_write_metadata_full(source_fd, source_list, parent,
+ (options->metadata.save_in_image_file && options->metadata.confirm_write) ? UTILITY_PHASE_START : UTILITY_PHASE_ENTERING);
}
void file_util_copy(FileData *source_fd, GList *source_list, const gchar *dest_path, GtkWidget *parent)