Include a Other Software section in Help file
[geeqie.git] / src / metadata.c
index 5842e74..a4c7d02 100644 (file)
@@ -1,15 +1,25 @@
 /*
- * Geeqie
- * (C) 2004 John Ellis
- * Copyright (C) 2008 - 2012 The Geeqie Team
+ * Copyright (C) 2004 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
  *
- * Author: John Ellis, Laurent Monin
+ * Authors: John Ellis, Laurent Monin
  *
- * This software is released under the GNU General Public License (GNU GPL).
- * Please read the included file COPYING for more information.
- * This software comes with no warranty of any kind, use at your own risk!
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include <locale.h>
 
 #include "main.h"
 #include "metadata.h"
@@ -290,11 +300,13 @@ gboolean metadata_write_perform(FileData *fd)
 {
        gboolean success;
        ExifData *exif;
+       guint lf;
 
        g_assert(fd->change);
 
+       lf = strlen(GQ_CACHE_EXT_METADATA);
        if (fd->change->dest &&
-           strcmp(extension_from_path(fd->change->dest), GQ_CACHE_EXT_METADATA) == 0)
+           g_ascii_strncasecmp(fd->change->dest + strlen(fd->change->dest) - lf, GQ_CACHE_EXT_METADATA, lf) == 0)
                {
                success = metadata_legacy_write(fd);
                if (success) metadata_legacy_delete(fd, fd->change->dest);
@@ -316,7 +328,9 @@ gboolean metadata_write_perform(FileData *fd)
                   (we can't wait until the sidecar is discovered by directory scanning because
                    exif_read_fd is called before that and it would read the main file only and
                    store the metadata in the cache)
-                   FIXME: this does not catch new sidecars created by independent external programs
+               */
+               /**
+               @FIXME this does not catch new sidecars created by independent external programs
                */
                file_data_unref(file_data_new_group(fd->change->dest));
 
@@ -387,7 +401,7 @@ gboolean metadata_write_list(FileData *fd, const gchar *key, const GList *values
                        FileData *sfd = work->data;
                        work = work->next;
 
-                       if (filter_file_class(sfd->extension, FORMAT_CLASS_META)) continue;
+                       if (sfd->format_class == FORMAT_CLASS_META) continue;
 
                        metadata_write_list(sfd, key, values);
                        }
@@ -658,7 +672,7 @@ GList *metadata_read_list(FileData *fd, const gchar *key, MetadataFormat format)
        const GList *cache_entry;
        if (!fd) return NULL;
 
-       /* unwritten data overide everything */
+       /* unwritten data override everything */
        if (fd->modified_xmp && format == METADATA_PLAIN)
                {
                list = g_hash_table_lookup(fd->modified_xmp, key);
@@ -699,6 +713,12 @@ GList *metadata_read_list(FileData *fd, const gchar *key, MetadataFormat format)
                {
                return g_list_append(NULL, metadata_file_info(fd, key, format));
                }
+#ifdef HAVE_LUA
+       else if (strncmp(key, "lua.", 4) == 0)
+               {
+               return g_list_append(NULL, metadata_lua_info(fd, key, format));
+               }
+#endif
 
        exif = exif_read_fd(fd); /* this is cached, thus inexpensive */
        if (!exif) return NULL;
@@ -739,6 +759,16 @@ guint64 metadata_read_int(FileData *fd, const gchar *key, guint64 fallback)
        return ret;
 }
 
+gchar *metadata_read_rating_stars(FileData *fd)
+{
+       gchar *ret;
+       gint n = metadata_read_int(fd, RATING_KEY, METADATA_PLAIN);
+
+       ret = convert_rating_to_stars(n);
+
+       return ret;
+}
+
 gdouble metadata_read_GPS_coord(FileData *fd, const gchar *key, gdouble fallback)
 {
        gdouble coord;
@@ -776,6 +806,37 @@ gdouble metadata_read_GPS_coord(FileData *fd, const gchar *key, gdouble fallback
        return coord;
 }
 
+gdouble metadata_read_GPS_direction(FileData *fd, const gchar *key, gdouble fallback)
+{
+       gchar *endptr;
+       gdouble deg;
+       gboolean ok = FALSE;
+       gchar *string = metadata_read_string(fd, key, METADATA_PLAIN);
+       if (!string) return fallback;
+
+       DEBUG_3("GPS_direction: %s\n", string);
+       deg = g_ascii_strtod(string, &endptr);
+
+       /* Expected text string is of the format e.g.:
+        * 18000/100
+        */
+       if (*endptr == '/')
+               {
+               deg = deg/100;
+               ok = TRUE;
+               }
+
+       if (!ok)
+               {
+               deg = fallback;
+               log_printf("unable to parse GPS direction '%s: %f'\n", string, deg);
+               }
+
+       g_free(string);
+
+       return deg;
+}
+
 gboolean metadata_append_string(FileData *fd, const gchar *key, const char *value)
 {
        gchar *str = metadata_read_string(fd, key, METADATA_PLAIN);
@@ -794,6 +855,59 @@ gboolean metadata_append_string(FileData *fd, const gchar *key, const char *valu
                }
 }
 
+gboolean metadata_write_GPS_coord(FileData *fd, const gchar *key, gdouble value)
+{
+       gint deg;
+       gdouble min;
+       gdouble param;
+       char *coordinate;
+       char *ref;
+       gboolean ok = TRUE;
+       char *old_locale, *saved_locale;
+
+       param = value;
+       if (param < 0)
+               param = -param;
+       deg = param;
+       min = (param * 60) - (deg * 60);
+       if (g_strcmp0(key, "Xmp.exif.GPSLongitude") == 0)
+               if (value < 0)
+                       ref = "W";
+               else
+                       ref = "E";
+       else if (g_strcmp0(key, "Xmp.exif.GPSLatitude") == 0)
+               if (value < 0)
+                       ref = "S";
+               else
+                       ref = "N";
+       else
+               {
+               log_printf("unknown GPS parameter key '%s'\n", key);
+               ok = FALSE;
+               }
+
+       if (ok)
+               {
+               /* Avoid locale problems with commas and decimal points in numbers */
+               old_locale = setlocale(LC_ALL, NULL);
+               saved_locale = strdup(old_locale);
+               if (saved_locale == NULL)
+                       {
+                       return FALSE;
+                       }
+               setlocale(LC_ALL, "C");
+
+               coordinate = g_strdup_printf("%i,%lf,%s", deg, min, ref);
+               metadata_write_string(fd, key, coordinate );
+
+               setlocale(LC_ALL, saved_locale);
+               free(saved_locale);
+               g_free(coordinate);
+               }
+
+       return ok;
+}
+
 gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *values)
 {
        GList *list = metadata_read_list(fd, key, METADATA_PLAIN);
@@ -815,7 +929,7 @@ gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *value
 }
 
 /**
- * \see find_string_in_list
+ * @see find_string_in_list
  */
 gchar *find_string_in_list_utf8nocase(GList *list, const gchar *string)
 {
@@ -848,7 +962,7 @@ gchar *find_string_in_list_utf8nocase(GList *list, const gchar *string)
 }
 
 /**
- * \see find_string_in_list
+ * @see find_string_in_list
  */
 gchar *find_string_in_list_utf8case(GList *list, const gchar *string)
 {
@@ -866,18 +980,18 @@ gchar *find_string_in_list_utf8case(GList *list, const gchar *string)
 } // gchar *find_string_in_list_utf...
 
 /**
- * \brief Find a existent string in a list.
+ * @brief Find a existent string in a list.
  *
  * This is a switch between find_string_in_list_utf8case and
  * find_string_in_list_utf8nocase to search with or without case for the
  * existence of a string.
  *
- * \param list The list to search in
- * \param string The string to search for
- * \return The string or NULL
+ * @param list The list to search in
+ * @param string The string to search for
+ * @return The string or NULL
  *
- * \see find_string_in_list_utf8case
- * \see find_string_in_list_utf8nocase
+ * @see find_string_in_list_utf8case
+ * @see find_string_in_list_utf8nocase
  */
 gchar *find_string_in_list(GList *list, const gchar *string)
 {
@@ -933,7 +1047,7 @@ GList *string_to_keywords_list(const gchar *text)
 
 gboolean meta_data_get_keyword_mark(FileData *fd, gint n, gpointer data)
 {
-       /* FIXME: do not use global keyword_tree */
+       /** @FIXME do not use global keyword_tree */
        GList *path = data;
        GList *keywords;
        gboolean found = FALSE;
@@ -1013,7 +1127,7 @@ void meta_data_connect_mark_with_keyword(GtkTreeModel *keyword_tree, GtkTreeIter
                path = keyword_tree_get_path(keyword_tree, kw_iter);
                file_data_register_mark_func(mark, meta_data_get_keyword_mark, meta_data_set_keyword_mark, path, (GDestroyNotify)string_list_free);
 
-               mark_str = g_strdup_printf("%d", mark + 1);
+               mark_str = g_strdup_printf("%d", (mark < 9 ? mark : -1) + 1);
                gtk_tree_store_set(GTK_TREE_STORE(keyword_tree), kw_iter, KEYWORD_COLUMN_MARK, mark_str, -1);
                g_free(mark_str);
                }
@@ -1037,6 +1151,14 @@ gchar *keyword_get_name(GtkTreeModel *keyword_tree, GtkTreeIter *iter)
        return name;
 }
 
+gchar *keyword_get_mark(GtkTreeModel *keyword_tree, GtkTreeIter *iter)
+{
+       gchar *mark_str;
+
+       gtk_tree_model_get(keyword_tree, iter, KEYWORD_COLUMN_MARK, &mark_str, -1);
+       return mark_str;
+}
+
 gchar *keyword_get_casefold(GtkTreeModel *keyword_tree, GtkTreeIter *iter)
 {
        gchar *casefold;
@@ -1518,6 +1640,20 @@ void keyword_show_all_in(GtkTreeStore *keyword_tree, gpointer id)
        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 *path, GtkTreeIter *iter, gpointer data)
+{
+       if (keyword_is_hidden_in(GTK_TREE_MODEL(keyword_tree), iter, data))
+               {
+               keyword_show_in(GTK_TREE_STORE(model), iter, data);
+               }
+       return FALSE;
+}
+
+void keyword_revert_hidden_in(GtkTreeStore *keyword_tree, gpointer id)
+{
+       gtk_tree_model_foreach(GTK_TREE_MODEL(keyword_tree), keyword_revert_hidden_in_cb, id);
+}
+
 static void keyword_hide_unset_in_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *iter_ptr, gpointer id, GList *keywords)
 {
        GtkTreeIter iter = *iter_ptr;
@@ -1666,12 +1802,19 @@ static void keyword_tree_node_write_config(GtkTreeModel *keyword_tree, GtkTreeIt
                {
                GtkTreeIter children;
                gchar *name;
+               gchar *mark_str;
 
                WRITE_NL(); WRITE_STRING("<keyword ");
                name = keyword_get_name(keyword_tree, &iter);
                write_char_option(outstr, indent, "name", name);
                g_free(name);
                write_bool_option(outstr, indent, "kw", keyword_get_is_keyword(keyword_tree, &iter));
+               mark_str = keyword_get_mark(keyword_tree, &iter);
+               if (mark_str && mark_str[0])
+                       {
+                       write_char_option(outstr, indent, "mark", mark_str);
+                       }
+
                if (gtk_tree_model_iter_children(keyword_tree, &children, &iter))
                        {
                        WRITE_STRING(">");
@@ -1702,10 +1845,40 @@ void keyword_tree_write_config(GString *outstr, gint indent)
        WRITE_NL(); WRITE_STRING("</keyword_tree>");
 }
 
+ void keyword_tree_node_disconnect_marks(GtkTreeModel *keyword_tree, GtkTreeIter *iter_ptr)
+{
+       GtkTreeIter iter = *iter_ptr;
+
+       while (TRUE)
+               {
+               GtkTreeIter children;
+
+               meta_data_connect_mark_with_keyword((keyword_tree), &iter, -1);
+
+               if (gtk_tree_model_iter_children((keyword_tree), &children, &iter))
+                       {
+                       keyword_tree_node_disconnect_marks((keyword_tree), &children);
+                       }
+
+               if (!gtk_tree_model_iter_next((keyword_tree), &iter)) return;
+               }
+}
+
+void keyword_tree_disconnect_marks()
+{
+       GtkTreeIter iter;
+
+       if (keyword_tree && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_tree), &iter))
+               {
+               keyword_tree_node_disconnect_marks(GTK_TREE_MODEL(keyword_tree), &iter);
+               }
+}
+
 GtkTreeIter *keyword_add_from_config(GtkTreeStore *keyword_tree, GtkTreeIter *parent, const gchar **attribute_names, const gchar **attribute_values)
 {
        gchar *name = NULL;
        gboolean is_kw = TRUE;
+       gchar *mark_str = NULL;
 
        while (*attribute_names)
                {
@@ -1714,6 +1887,7 @@ GtkTreeIter *keyword_add_from_config(GtkTreeStore *keyword_tree, GtkTreeIter *pa
 
                if (READ_CHAR_FULL("name", name)) continue;
                if (READ_BOOL_FULL("kw", is_kw)) continue;
+               if (READ_CHAR_FULL("mark", mark_str)) continue;
 
                log_printf("unknown attribute %s = %s\n", option, value);
                }
@@ -1726,6 +1900,16 @@ GtkTreeIter *keyword_add_from_config(GtkTreeStore *keyword_tree, GtkTreeIter *pa
                        gtk_tree_store_append(keyword_tree, &iter, parent);
                        }
                keyword_set(keyword_tree, &iter, name, is_kw);
+
+               if (mark_str)
+                       {
+                       gint i = (gint)atoi(mark_str);
+                       if (i == 0) i = 10;
+
+                       meta_data_connect_mark_with_keyword(GTK_TREE_MODEL(keyword_tree),
+                                                                                       &iter, i - 1);
+                       }
+
                g_free(name);
                return gtk_tree_iter_copy(&iter);
                }