* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "metadata.h"
+
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
#include <clocale>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
-#include "main.h"
-#include "metadata.h"
+#include <glib-object.h>
+
+#include <config.h>
#include "cache.h"
+#include "debug.h"
#include "exif.h"
#include "filedata.h"
+#include "intl.h"
+#include "layout-util.h"
+#include "main-defines.h"
#include "misc.h"
+#include "options.h"
+#include "rcfile.h"
#include "secure-save.h"
#include "ui-fileops.h"
#include "utilops.h"
-#include "layout-util.h"
-#include "rcfile.h"
+
+struct ExifData;
+
+namespace
+{
enum MetadataKey {
MK_NONE,
MK_COMMENT
};
+struct MetadataCacheEntry {
+ gchar *key;
+ GList *values;
+};
+
/* If contents change, keep GuideOptionsMetadata.xml up to date */
/**
* @brief Tags that will be written to all files in a group - selected by: options->metadata.sync_grouped_files, Preferences/Metadata/Write The Same Description Tags To All Grouped Sidecars
*/
-static const gchar *group_keys[] = {
+constexpr std::array<const gchar *, 22> group_keys{
"Xmp.dc.title",
"Xmp.photoshop.Urgency",
"Xmp.photoshop.Category",
"Xmp.dc.rights",
"Xmp.dc.description",
"Xmp.photoshop.CaptionWriter",
- nullptr};
+ "Xmp.xmp.Rating",
+};
+
+gint metadata_cache_entry_compare_key(const MetadataCacheEntry *entry, const gchar *key)
+{
+ return strcmp(entry->key, key);
+}
+
+void metadata_cache_entry_free(MetadataCacheEntry *entry)
+{
+ if (!entry) return;
+
+ g_free(entry->key);
+ g_list_free_full(entry->values, g_free);
+ g_free(entry);
+}
+
+inline gboolean is_keywords_separator(gchar c)
+{
+ return c == ','
+ || c == ';'
+ || c == '\n'
+ || c == '\r'
+ || c == '\b';
+}
+
+} // namespace
static gboolean metadata_write_queue_idle_cb(gpointer data);
static gboolean metadata_legacy_write(FileData *fd);
*-------------------------------------------------------------------
*/
-/* fd->cached metadata list of lists
- each particular list contains key as a first entry, then the values
-*/
+/* fd->cached_metadata list of MetadataCacheEntry */
static void metadata_cache_update(FileData *fd, const gchar *key, const GList *values)
{
GList *work;
- work = fd->cached_metadata;
- while (work)
+ work = g_list_find_custom(fd->cached_metadata, key, reinterpret_cast<GCompareFunc>(metadata_cache_entry_compare_key));
+ if (work)
{
- auto entry = static_cast<GList *>(work->data);
- auto entry_key = static_cast<gchar *>(entry->data);
+ /* key found - just replace values */
+ auto *entry = static_cast<MetadataCacheEntry *>(work->data);
- if (strcmp(entry_key, key) == 0)
- {
- /* key found - just replace values */
- GList *old_values = entry->next;
- entry->next = nullptr;
- old_values->prev = nullptr;
- g_list_free_full(old_values, g_free);
- work->data = g_list_append(entry, string_list_copy(values));
- DEBUG_1("updated %s %s\n", key, fd->path);
- return;
- }
- work = work->next;
+ g_list_free_full(entry->values, g_free);
+ entry->values = string_list_copy(values);
+ DEBUG_1("updated %s %s\n", key, fd->path);
+ return;
}
/* key not found - prepend new entry */
- fd->cached_metadata = g_list_prepend(fd->cached_metadata,
- g_list_prepend(string_list_copy(values), g_strdup(key)));
- DEBUG_1("added %s %s\n", key, fd->path);
+ auto *entry = g_new0(MetadataCacheEntry, 1);
+ entry->key = g_strdup(key);
+ entry->values = string_list_copy(values);
+ fd->cached_metadata = g_list_prepend(fd->cached_metadata, entry);
+ DEBUG_1("added %s %s\n", key, fd->path);
}
static const GList *metadata_cache_get(FileData *fd, const gchar *key)
{
GList *work;
- work = fd->cached_metadata;
- while (work)
+ work = g_list_find_custom(fd->cached_metadata, key, reinterpret_cast<GCompareFunc>(metadata_cache_entry_compare_key));
+ if (work)
{
- auto entry = static_cast<GList *>(work->data);
- auto entry_key = static_cast<gchar *>(entry->data);
+ /* key found */
+ auto *entry = static_cast<MetadataCacheEntry *>(work->data);
- if (strcmp(entry_key, key) == 0)
- {
- /* key found */
- DEBUG_1("found %s %s\n", key, fd->path);
- return entry;
- }
- work = work->next;
+ DEBUG_1("found %s %s\n", key, fd->path);
+ return entry->values;
}
- return nullptr;
DEBUG_1("not found %s %s\n", key, fd->path);
+ return nullptr;
}
static void metadata_cache_remove(FileData *fd, const gchar *key)
{
GList *work;
- work = fd->cached_metadata;
- while (work)
+ work = g_list_find_custom(fd->cached_metadata, key, reinterpret_cast<GCompareFunc>(metadata_cache_entry_compare_key));
+ if (work)
{
- auto entry = static_cast<GList *>(work->data);
- auto entry_key = static_cast<gchar *>(entry->data);
+ /* key found */
+ auto *entry = static_cast<MetadataCacheEntry *>(work->data);
- if (strcmp(entry_key, key) == 0)
- {
- /* key found */
- g_list_free_full(entry, g_free);
- fd->cached_metadata = g_list_delete_link(fd->cached_metadata, work);
- DEBUG_1("removed %s %s\n", key, fd->path);
- return;
- }
- work = work->next;
+ metadata_cache_entry_free(entry);
+ fd->cached_metadata = g_list_delete_link(fd->cached_metadata, work);
+ DEBUG_1("removed %s %s\n", key, fd->path);
+ return;
}
DEBUG_1("not removed %s %s\n", key, fd->path);
}
{
if (fd->cached_metadata) DEBUG_1("freed %s\n", fd->path);
- g_list_free_full(fd->cached_metadata, [](gpointer data)
- {
- auto entry = static_cast<GList *>(data);
- g_list_free_full(entry, g_free);
- });
+ g_list_free_full(fd->cached_metadata, reinterpret_cast<GDestroyNotify>(metadata_cache_entry_free));
fd->cached_metadata = nullptr;
}
-
-
-
-
/*
*-------------------------------------------------------------------
* write queue
work = list;
while (work)
{
- FileData *fd = static_cast<FileData *>(work->data);
+ auto *fd = static_cast<FileData *>(work->data);
work = work->next;
ret = ret && metadata_write_queue_remove(fd);
}
}
#pragma GCC diagnostic pop
-void metadata_notify_cb(FileData *fd, NotifyType type, gpointer UNUSED(data))
+void metadata_notify_cb(FileData *fd, NotifyType type, gpointer)
{
if (type & (NOTIFY_REREAD | NOTIFY_CHANGE))
{
return (metadata_write_queue != nullptr);
}
-static gboolean metadata_write_queue_idle_cb(gpointer UNUSED(data))
+static gboolean metadata_write_queue_idle_cb(gpointer)
{
metadata_write_queue_confirm(FALSE, nullptr, nullptr);
metadata_write_idle_id = 0;
return g_list_length(metadata_write_queue);
}
-static gboolean metadata_check_key(const gchar *keys[], const gchar *key)
-{
- const gchar **k = keys;
-
- while (*k)
- {
- if (strcmp(key, *k) == 0) return TRUE;
- k++;
- }
- return FALSE;
-}
-
gboolean metadata_write_revert(FileData *fd, const gchar *key)
{
if (!fd->modified_xmp) return FALSE;
file_data_increment_version(fd);
file_data_send_notification(fd, NOTIFY_METADATA);
- if (options->metadata.sync_grouped_files && metadata_check_key(group_keys, key))
+ auto metadata_check_key = [key](const gchar *k) { return strcmp(key, k) == 0; };
+ if (options->metadata.sync_grouped_files && std::any_of(group_keys.cbegin(), group_keys.cend(), metadata_check_key))
{
GList *work = fd->sidecar_files;
g_free(metadata_path);
}
-#ifdef HAVE_EXIV2
+#if HAVE_EXIV2
/* without exiv2: do not delete xmp metadata because we are not able to convert it,
just ignore it */
metadata_path = cache_find_location(CACHE_TYPE_XMP_METADATA, fd->path);
{
ExifData *exif;
GList *list = nullptr;
- const GList *cache_entry;
+ const GList *cache_values;
if (!fd) return nullptr;
/* unwritten data override everything */
if (fd->modified_xmp && format == METADATA_PLAIN)
{
- list = static_cast<GList *>(g_hash_table_lookup(fd->modified_xmp, key));
+ list = static_cast<GList *>(g_hash_table_lookup(fd->modified_xmp, key));
if (list) return string_list_copy(list);
}
if (format == METADATA_PLAIN && strcmp(key, KEYWORD_KEY) == 0
- && (cache_entry = metadata_cache_get(fd, key)))
+ && (cache_values = metadata_cache_get(fd, key)))
{
- return string_list_copy(cache_entry->next);
+ return string_list_copy(cache_values);
}
/*
{
return g_list_append(nullptr, metadata_file_info(fd, key, format));
}
-#ifdef HAVE_LUA
+#if HAVE_LUA
else if (strncmp(key, "lua.", 4) == 0)
{
return g_list_append(nullptr, metadata_lua_info(fd, key, format));
{
gdouble coord;
gchar *endptr;
- gdouble deg, min, sec;
+ gdouble deg;
+ gdouble min;
+ gdouble sec;
gboolean ok = FALSE;
gchar *string = metadata_read_string(fd, key, METADATA_PLAIN);
if (!string) return fallback;
{
return metadata_write_string(fd, key, value);
}
- else
- {
- gchar *new_string = g_strconcat(str, value, NULL);
- gboolean ret = metadata_write_string(fd, key, new_string);
- g_free(str);
- g_free(new_string);
- return ret;
- }
+
+ gchar *new_string = g_strconcat(str, value, NULL);
+ gboolean ret = metadata_write_string(fd, key, new_string);
+ g_free(str);
+ g_free(new_string);
+ return ret;
}
gboolean metadata_write_GPS_coord(FileData *fd, const gchar *key, gdouble value)
char *coordinate;
const char *ref;
gboolean ok = TRUE;
- char *old_locale, *saved_locale;
+ char *old_locale;
+ char *saved_locale;
param = value;
if (param < 0)
{
return metadata_write_list(fd, key, values);
}
- else
- {
- gboolean ret;
- list = g_list_concat(list, string_list_copy(values));
- list = remove_duplicate_strings_from_list(list);
- ret = metadata_write_list(fd, key, list);
- g_list_free_full(list, g_free);
- return ret;
- }
+ gboolean ret;
+ list = g_list_concat(list, string_list_copy(values));
+ list = remove_duplicate_strings_from_list(list);
+
+ ret = metadata_write_list(fd, key, list);
+ g_list_free_full(list, g_free);
+ return ret;
}
/**
{
if (options->metadata.keywords_case_sensitive)
return find_string_in_list_utf8case(list, string);
- else
- return find_string_in_list_utf8nocase(list, string);
-}
-#define KEYWORDS_SEPARATOR(c) ((c) == ',' || (c) == ';' || (c) == '\n' || (c) == '\r' || (c) == '\b')
+ return find_string_in_list_utf8nocase(list, string);
+}
GList *string_to_keywords_list(const gchar *text)
{
const gchar *begin;
gint l = 0;
- while (KEYWORDS_SEPARATOR(*ptr)) ptr++;
+ while (is_keywords_separator(*ptr)) ptr++;
begin = ptr;
- while (*ptr != '\0' && !KEYWORDS_SEPARATOR(*ptr))
+ while (*ptr != '\0' && !is_keywords_separator(*ptr))
{
ptr++;
l++;
*/
-gboolean meta_data_get_keyword_mark(FileData *fd, gint UNUSED(n), gpointer data)
+gboolean meta_data_get_keyword_mark(FileData *fd, gint, gpointer data)
{
/** @FIXME do not use global keyword_tree */
auto path = static_cast<GList *>(data);
return found;
}
-gboolean meta_data_set_keyword_mark(FileData *fd, gint UNUSED(n), gboolean value, gpointer data)
+gboolean meta_data_set_keyword_mark(FileData *fd, gint, gboolean value, gpointer data)
{
auto path = static_cast<GList *>(data);
GList *keywords = nullptr;
{
return keyword_compare(keyword_tree, &parent_a, &parent_b) == 0;
}
- else
- {
- return (!valid_pa && !valid_pb); /* both are toplevel */
- }
+
+ return (!valid_pa && !valid_pb); /* both are toplevel */
}
gboolean keyword_exists(GtkTreeModel *keyword_tree, GtkTreeIter *parent_ptr, GtkTreeIter *sibling, const gchar *name, gboolean exclude_sibling, GtkTreeIter *result)
void keyword_copy(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter *from)
{
- gchar *mark, *name, *casefold;
+ gchar *mark;
+ gchar *name;
+ gchar *casefold;
gboolean is_keyword;
/* do not copy KEYWORD_COLUMN_HIDE_IN, it fully shows the new subtree */
if (keyword_get_is_keyword(keyword_tree, &iter))
{
- GList *work = casefold_list;
- gboolean found = FALSE;
gchar *iter_casefold = keyword_get_casefold(keyword_tree, &iter);
- while (work)
- {
- auto casefold = static_cast<const gchar *>(work->data);
- work = work->next;
-
- if (strcmp(iter_casefold, casefold) == 0)
- {
- found = TRUE;
- break;
- }
- }
+ GList *found = g_list_find_custom(casefold_list, iter_casefold, reinterpret_cast<GCompareFunc>(strcmp));
g_free(iter_casefold);
if (!found) return FALSE;
}
if (keyword_get_is_keyword(keyword_tree, &iter))
{
- GList *work = kw_list;
- gboolean found = FALSE;
gchar *iter_name = keyword_get_name(keyword_tree, &iter);
- while (work)
- {
- auto name = static_cast<const gchar *>(work->data);
- work = work->next;
-
- if (strcmp(iter_name, name) == 0)
- {
- found = TRUE;
- break;
- }
- }
+ GList *found = g_list_find_custom(kw_list, iter_name, reinterpret_cast<GCompareFunc>(strcmp));
g_free(iter_name);
if (!found) return FALSE;
}
return !!g_list_find(list, id);
}
-static gboolean keyword_show_all_in_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer data)
+static gboolean keyword_show_all_in_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
{
keyword_show_in(GTK_TREE_STORE(model), iter, data);
return FALSE;
gtk_tree_model_foreach(GTK_TREE_MODEL(keyword_tree), keyword_show_all_in_cb, id);
}
-static gboolean keyword_revert_hidden_in_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer data)
+static gboolean keyword_revert_hidden_in_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
{
if (keyword_is_hidden_in(GTK_TREE_MODEL(keyword_tree), iter, data))
{
keyword_hide_unset_in_recursive(keyword_tree, &iter, id, keywords);
}
-static gboolean keyword_show_set_in_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter_ptr, gpointer data)
+static gboolean keyword_show_set_in_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter_ptr, gpointer data)
{
GtkTreeIter iter = *iter_ptr;
auto keywords = static_cast<GList *>(data);
void keyword_tree_new_default()
{
- GtkTreeIter i1, i2;
+ GtkTreeIter i1;
+ GtkTreeIter i2;
if (!keyword_tree) keyword_tree_new();