/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * metadata-editor.c
 * Copyright (C) 2016, 2017 Ben Touchette
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <stdlib.h>
#include <ctype.h>

#include <gegl.h>
#include <gtk/gtk.h>
#include <gexiv2/gexiv2.h>

#include <glib.h>
#include <glib/gstdio.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"

#include "metadata-tags.h"
#include "metadata-xml.h"
#include "metadata-impexp.h"
#include "metadata-misc.h"

#define PLUG_IN_PROC            "plug-in-metadata-editor"
#define PLUG_IN_BINARY          "metadata-editor"
#define PLUG_IN_ROLE            "gimp-metadata"

#define DEFAULT_TEMPLATE_FILE   "gimp_metadata_template.xml"

/*  local function prototypes  */

static void       query                          (void);
static void       run                            (const gchar          *name,
                                                  gint                 nparams,
                                                  const GimpParam     *param,
                                                  gint                *nreturn_vals,
                                                  GimpParam          **return_vals);

static gboolean metadata_editor_dialog           (gint32               image_id,
                                                  GimpMetadata        *metadata,
                                                  GError             **error);

static void metadata_dialog_editor_set_metadata  (GExiv2Metadata      *metadata,
                                                  GtkBuilder          *builder);

void metadata_editor_write_callback              (GtkWidget           *dialog,
                                                  GtkBuilder          *builder,
                                                  gint32               image_id);

static void impex_combo_callback                (GtkComboBoxText      *combo,
                                                 gpointer              data);

static void gpsaltsys_combo_callback            (GtkComboBoxText      *combo,
                                                 gpointer              data);

static void remove_substring                    (const gchar          *string,
                                                 const gchar          *substring);

static gchar * clean_xmp_string                 (const gchar          *value);
static gchar ** split_metadata_string           (gchar                *value);
static void     add_to_store                    (gchar                *value,
                                                 GtkListStore         *liststore,
                                                 gint                  store_column);

static void     set_tag_string                  (GimpMetadata         *metadata,
                                                 const gchar          *name,
                                                 const gchar          *value);

static gchar *  get_phonetype                   (gchar                *cur_value);

static void     write_metadata_tag              (GtkBuilder           *builder,
                                                 GimpMetadata         *metadata,
                                                 gchar                *tag,
                                                 gint                  data_column);

static void     write_metadata_tag_multiple     (GtkBuilder           *builder,
                                                 GimpMetadata         *metadata,
                                                 GExiv2StructureType   type,
                                                 const gchar          *header_tag,
                                                 gint                  n_columns,
                                                 const gchar         **column_tags,
                                                 const gint            special_handling[]);

gboolean hasCreatorTagData                      (GtkBuilder           *builder);
gboolean hasLocationCreationTagData             (GtkBuilder           *builder);
gboolean hasImageSupplierTagData                (GtkBuilder           *builder);

void on_date_button_clicked                     (GtkButton            *widget,
                                                 GtkWidget            *entry_widget,
                                                 gchar                *tag);

void on_create_date_button_clicked              (GtkButton            *widget,
                                                 gpointer              data);

void on_patient_dob_date_button_clicked         (GtkButton            *widget,
                                                 gpointer              data);

void on_study_date_button_clicked               (GtkButton            *widget,
                                                 gpointer              data);

void on_series_date_button_clicked              (GtkButton            *widget,
                                                 gpointer              data);


static void
property_release_id_remove_callback             (GtkWidget            *widget,
                                                 gpointer              data);
static void
property_release_id_add_callback                (GtkWidget            *widget,
                                                 gpointer              data);
static void
model_release_id_remove_callback                (GtkWidget            *widget,
                                                 gpointer              data);
static void
model_release_id_add_callback                   (GtkWidget            *widget,
                                                 gpointer              data);
static void
shown_location_remove_callback                  (GtkWidget            *widget,
                                                 gpointer              data);
static void
shown_location_add_callback                     (GtkWidget            *widget,
                                                 gpointer              data);
static void
feat_org_name_add_callback                      (GtkWidget            *widget,
                                                 gpointer              data);
static void
feat_org_name_remove_callback                   (GtkWidget            *widget,
                                                 gpointer              data);
static void
feat_org_code_add_callback                      (GtkWidget            *widget,
                                                 gpointer              data);
static void
feat_org_code_remove_callback                   (GtkWidget            *widget,
                                                 gpointer              data);
static void
artwork_object_add_callback                     (GtkWidget            *widget,
                                                 gpointer              data);
static void
artwork_object_remove_callback                  (GtkWidget            *widget,
                                                 gpointer              data);
static void
reg_entry_add_callback                          (GtkWidget            *widget,
                                                 gpointer              data);
static void
reg_entry_remove_callback                       (GtkWidget            *widget,
                                                 gpointer              data);
static void
image_creator_add_callback                      (GtkWidget            *widget,
                                                 gpointer              data);
static void
image_creator_remove_callback                   (GtkWidget            *widget,
                                                 gpointer              data);

static void
copyright_own_add_callback                      (GtkWidget            *widget,
                                                 gpointer              data);
static void
copyright_own_remove_callback                   (GtkWidget            *widget,
                                                 gpointer              data);
static void
licensor_add_callback                           (GtkWidget            *widget,
                                                 gpointer              data);
static void
licensor_remove_callback                        (GtkWidget            *widget,
                                                 gpointer              data);

static void
list_row_remove_callback                        (GtkWidget            *widget,
                                                 gpointer              data,
                                                 gchar                *tag);

static void
list_row_add_callback                           (GtkWidget            *widget,
                                                 gpointer              data,
                                                 gchar                *tag);

static gint
count_tags                                      (GExiv2Metadata       *metadata,
                                                 const gchar          *header,
                                                 const gchar         **tags,
                                                 int                   items);

static gchar **
get_tags                                        (GExiv2Metadata       *metadata,
                                                 const gchar          *header,
                                                 const gchar         **tags,
                                                 const int             items,
                                                 const int             count);

static void
free_tagdata                                    (gchar               **tagdata,
                                                 gint                  rows,
                                                 gint                  cols);

gboolean
hasModelReleaseTagData                          (GtkBuilder           *builder);

gboolean
hasPropertyReleaseTagData                       (GtkBuilder           *builder);

static void
organisation_image_code_cell_edited_callback    (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
organisation_image_name_cell_edited_callback    (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
prop_rel_id_cell_edited_callback                (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
loc_sho_sub_loc_cell_edited_callback            (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
loc_sho_city_cell_edited_callback               (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
loc_sho_state_prov_cell_edited_callback         (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
loc_sho_cntry_cell_edited_callback              (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
loc_sho_cntry_iso_cell_edited_callback          (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
reg_org_id_cell_edited_callback                 (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
reg_item_id_cell_edited_callback                (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
aoo_title_cell_edited_callback                  (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
aoo_copyright_notice_cell_edited_callback       (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
aoo_source_inv_cell_edited_callback             (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
aoo_source_cell_edited_callback                 (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
aoo_creator_cell_edited_callback                (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
aoo_date_creat_cell_edited_callback             (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
cr_owner_name_cell_edited_callback              (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
cr_owner_id_cell_edited_callback                (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_name_cell_edited_callback              (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_id_cell_edited_callback                (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_phone1_cell_edited_callback            (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_phone_type1_cell_edited_callback       (GtkCellRendererCombo *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_phone2_cell_edited_callback            (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_phone_type2_cell_edited_callback       (GtkCellRendererCombo *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_email_cell_edited_callback             (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

static void
licensor_web_cell_edited_callback               (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data);

void
cell_edited_callback                            (GtkCellRendererText  *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data,
                                                 int                   index);

void
cell_edited_callback_combo                      (GtkCellRendererCombo *cell,
                                                 const gchar          *path_string,
                                                 const gchar          *new_text,
                                                 gpointer              data,
                                                 int                   index);


/* local variables */

static int last_gpsaltsys_sel;

gboolean gimpmetadata;
gboolean force_write;

static const gchar *lang_default = "lang=\"x-default\"";
static const gchar *seq_default = "type=\"Seq\"";
static const gchar *bag_default = "type=\"Bag\"";

metadata_editor meta_args;

#define ME_LOG_DOMAIN "metadata-editor"

const GimpPlugInInfo PLUG_IN_INFO =
{
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
};

/* ============================================================================
 * ==[           ]=============================================================
 * ==[ FUNCTIONS ]=============================================================
 * ==[           ]=============================================================
 * ============================================================================
 */


MAIN ()

/* ============================================================================
 * ==[ QUERY ]=================================================================
 * ============================================================================
 */

static void
query (void)
{
  static const GimpParamDef metadata_args[] =
  {
    { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
    { GIMP_PDB_IMAGE, "image",    "Input image"                      }
  };

  gimp_install_procedure (PLUG_IN_PROC,
                          N_("Edit metadata (IPTC, EXIF, XMP)"),
                          "Edit metadata information attached to the "
                          "current image. Some or all of this metadata "
                          "will be saved in the file, depending on the output "
                          "file format.",
                          "Ben Touchette",
                          "Ben Touchette",
                          "2017",
                          N_("_Edit Metadata"),
                          "*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (metadata_args), 0,
                          metadata_args, NULL);

  gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Metadata");
}

/* ============================================================================
 * ==[ RUN ]===================================================================
 * ============================================================================
 */

static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam   values[2];
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  GError            *error  = NULL;

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

  force_write = FALSE;

  INIT_I18N ();
  gimp_ui_init (PLUG_IN_BINARY, TRUE);

  if (! strcmp (name, PLUG_IN_PROC))
    {
      GimpMetadata *metadata;
      gint32        image_ID = param[1].data.d_image;

      metadata = gimp_image_get_metadata (image_ID);

      /* Always show metadata dialog so we can add
         appropriate iptc data as needed. Sometimes
         license data needs to be added after the
         fact and the image may not contain metadata
         but should have it added as needed. */

      if (!metadata)
        {
          metadata = gimp_metadata_new ();
          gimp_image_set_metadata (image_ID, metadata);
        }

      if (metadata_editor_dialog (image_ID, metadata, &error))
        status = GIMP_PDB_SUCCESS;
      else
        {
          status = GIMP_PDB_EXECUTION_ERROR;
          if (error)
            {
             *nreturn_vals = 2;
              values[1].type          = GIMP_PDB_STRING;
              values[1].data.d_string = error->message;
            }
        }
    }
  else
    {
      status = GIMP_PDB_CALLING_ERROR;
    }

  values[0].data.d_status = status;
}

/* ============================================================================
 * ==[ EDITOR DIALOG UI ]======================================================
 * ============================================================================
 */

static GtkWidget *
builder_get_widget (GtkBuilder  *builder,
                    const gchar *name)
{
  GObject *object = gtk_builder_get_object (builder, name);

  return GTK_WIDGET (object);
}

static gboolean
metadata_editor_dialog (gint32         image_id,
                        GimpMetadata  *g_metadata,
                        GError       **error)
{
  GtkBuilder     *builder;
  GtkWidget      *dialog;
  GtkWidget      *metadata_vbox;
  GtkWidget      *impex_combo;
  GtkWidget      *content_area;
  GExiv2Metadata *metadata;
  gchar          *ui_file;
  gchar          *title;
  gchar          *name;
  GError         *local_error = NULL;
  gboolean        run;

  metadata = GEXIV2_METADATA (g_metadata);

  builder = gtk_builder_new ();

  meta_args.image_id = image_id;
  meta_args.builder = builder;
  meta_args.metadata = metadata;
  meta_args.filename = g_strconcat (g_get_home_dir (), "/", DEFAULT_TEMPLATE_FILE,
                                    NULL);

  ui_file = g_build_filename (gimp_data_directory (),
                              "ui", "plug-ins", "plug-in-metadata-editor.ui", NULL);

  if (! gtk_builder_add_from_file (builder, ui_file, &local_error))
    {
      if (! local_error)
        local_error = g_error_new_literal (G_FILE_ERROR, 0,
                                           _("Error loading metadata-editor dialog."));
      g_propagate_error (error, local_error);

      g_free (ui_file);
      g_object_unref (builder);
      return FALSE;
    }

  g_free (ui_file);

  name = gimp_image_get_name (image_id);
  title = g_strdup_printf (_("Metadata Editor: %s"), name);
  if (name)
    g_free (name);

  dialog = gimp_dialog_new (title,
                            "gimp-metadata-editor-dialog",
                            NULL, 0,
                            gimp_standard_help_func, PLUG_IN_PROC,
                            _("_Cancel"),         GTK_RESPONSE_CANCEL,
                            _("_Write Metadata"), GTK_RESPONSE_OK,
                            NULL);

  meta_args.dialog = dialog;

  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                            GTK_RESPONSE_OK,
                                            GTK_RESPONSE_CANCEL,
                                            -1);

  gimp_window_set_transient (GTK_WINDOW (dialog));

  if (title)
    g_free (title);

  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));

  metadata_vbox = builder_get_widget (builder, "metadata-vbox");
  impex_combo   = builder_get_widget (builder, "impex_combo");

  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impex_combo),
                                  _("Select:"));
  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impex_combo),
                                  _("Import metadata"));
  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impex_combo),
                                  _("Export metadata"));
  gtk_combo_box_set_active (GTK_COMBO_BOX (impex_combo), 0);

  g_signal_connect (G_OBJECT (impex_combo),
                    "changed", G_CALLBACK (impex_combo_callback), &meta_args);

  gtk_container_set_border_width (GTK_CONTAINER (metadata_vbox), 12);
  gtk_box_pack_start (GTK_BOX (content_area), metadata_vbox, TRUE, TRUE, 0);

  metadata_dialog_editor_set_metadata (metadata, builder);

  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
  if (run)
    {
      metadata_editor_write_callback (dialog, builder, image_id);
    }

  if (meta_args.filename)
    {
      g_free (meta_args.filename);
    }

  return TRUE;
}

/* ============================================================================
 * ==[                   ]=====================================================
 * ==[ PRIVATE FUNCTIONS ]=====================================================
 * ==[                   ]=====================================================
 * ============================================================================
 */
static void
remove_substring (const gchar *string,
                  const gchar *substring)
{
  if (string != NULL && substring != NULL && substring[0] != '\0')
    {
      gchar *p = strstr (string, substring);
      if (p)
        {
          memmove (p, p + strlen (substring), strlen (p + strlen (substring)) + 1);
        }
    }
}

static gchar *
clean_xmp_string (const gchar *value)
{
  gchar *value_clean;
  gchar *value_utf8;

  value_clean = g_strdup (value);

  if (strstr (value_clean, lang_default) != NULL)
    {
      remove_substring (value_clean, lang_default);
      if (strstr (value_clean, " ") != NULL)
        {
          remove_substring (value_clean, " ");
        }
    }

  if (strstr (value_clean, bag_default) != NULL)
    {
      remove_substring (value_clean, bag_default);
      if (strstr (value_clean, " ") != NULL)
        {
          remove_substring (value_clean, " ");
        }
    }

  if (strstr (value_clean, seq_default) != NULL)
    {
      remove_substring (value_clean, seq_default);
      if (strstr (value_clean, " ") != NULL)
        {
          remove_substring (value_clean, " ");
        }
    }

  if (! g_utf8_validate (value_clean, -1, NULL))
    {
      value_utf8 = g_locale_to_utf8 (value_clean, -1, NULL, NULL, NULL);
    }
  else
    {
      value_utf8 = g_strdup (value_clean);
    }

  g_free (value_clean);

  return value_utf8;
}

/* We split a string and accept "," and ";" as delimiters.
 * The result needs to be freed with g_strfreev.
 */
static gchar **
split_metadata_string (gchar *value)
{
  gchar  **split;
  gint     item;

  /* Can't use g_strsplit_set since we work with utf-8 here. */
  split = g_strsplit (g_strdelimit (value, ";", ','), ",", 0);

  for (item = 0; split[item]; item++)
    {
      split[item] = g_strstrip(split[item]);
    }

  return split;
}

static void
add_to_store (gchar *value, GtkListStore *liststore, gint store_column)
{
  gchar       **strings;
  gint          cnt = 0;
  gint          item;
  GtkTreeIter   iter;

  if (value)
    {
      strings = split_metadata_string (value);
      if (strings)
        {
          for (item = 0; strings[item]; item++)
            {
              if (strings[item][0] != '\0')
                {
                  cnt++;

                  gtk_list_store_append (liststore, &iter);
                  gtk_list_store_set (liststore, &iter,
                                      store_column, strings[item],
                                      -1);
                }
            }
          g_strfreev(strings);
        }
    }

  /* If there are less than two rows, add empty ones. */
  for (item = cnt; item < 2; item++)
    {
      gtk_list_store_append (liststore, &iter);
      gtk_list_store_set (liststore, &iter,
                          store_column, NULL,
                          -1);
    }
}

static gint
count_tags (GExiv2Metadata  *metadata,
            const gchar     *header,
            const gchar    **tags,
            gint             items)
{
  int   tagcount;
  gchar tag[256];
  int   row, col;

  tagcount = 0;
  for (row = 1; row < 256; row++)
    {
      for (col = 0; col < items; col++)
        {
          g_snprintf ((gchar *) &tag, 256, "%s[%d]", header, row);
          g_snprintf ((gchar *) &tag, 256, "%s%s",
                      (gchar *) &tag, (gchar *) tags[col]);
          if (gexiv2_metadata_has_tag (metadata, (gchar *) &tag))
            {
              tagcount++;
              break;
            }
        }
    }
  return tagcount;
}

static gchar **
get_tags (GExiv2Metadata  *metadata,
          const gchar     *header,
          const gchar    **tags,
          const gint       items,
          const gint       count)
{
  gchar **tagdata;
  gchar **_datarow;
  gchar   tag[256];
  int     row, col;

  g_return_val_if_fail (header != NULL && tags != NULL, NULL);
  g_return_val_if_fail (items > 0, NULL);

  if (count <= 0)
    return NULL;
  tagdata = g_new0 (gchar *, count);
  if (! tagdata)
    return NULL;

  for (row = 1; row < count + 1; row++)
    {
      tagdata[row-1] = g_malloc0 (sizeof (gchar *) * items);
      for (col = 0; col < items; col++)
        {
          gchar *value;

          g_snprintf ((gchar *) &tag, 256, "%s[%d]", header, row);
          g_snprintf ((gchar *) &tag, 256, "%s%s",
                      (gchar *) &tag, (gchar *) tags[col]);

          value = gexiv2_metadata_get_tag_string (metadata, (gchar *) &tag);
          g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "get_tags tag: %s, value: %s", (gchar *) &tag, value);

          _datarow = (gchar **) tagdata[row-1];
          if (_datarow)
            _datarow[col] = strdup (value);
        }
    }
  return tagdata;
}

static void
free_tagdata(gchar **tagdata, gint rows, gint cols)
{
  gint    row, col;
  gchar **tagdatarow;

  for (row = 0; row < rows; row++)
    {
      tagdatarow = (gpointer) tagdata[row];

      for (col = 0; col < cols; col++)
        {
          g_free (tagdatarow[col]);
        }
      g_free (tagdatarow);
    }
  g_free (tagdata);
}

/* ============================================================================
 * ==[ DATE CALLBACKS ]========================================================
 * ============================================================================
 */
void
on_create_date_button_clicked (GtkButton *widget,
                               gpointer   data)
{
  on_date_button_clicked (widget, (GtkWidget*)data,
                          "Xmp.photoshop.DateCreated");
}

void
on_patient_dob_date_button_clicked (GtkButton *widget,
                                    gpointer   data)
{
  on_date_button_clicked (widget, (GtkWidget*)data,
                          "Xmp.DICOM.PatientDOB");
}

void
on_study_date_button_clicked (GtkButton *widget,
                              gpointer   data)
{
  on_date_button_clicked (widget, (GtkWidget*)data,
                          "Xmp.DICOM.StudyDateTime");
}

void
on_series_date_button_clicked (GtkButton *widget,
                               gpointer   data)
{
  on_date_button_clicked (widget, (GtkWidget*)data,
                          "Xmp.DICOM.SeriesDateTime");
}

void
on_date_button_clicked (GtkButton *widget,
                        GtkWidget *entry_widget,
                        gchar     *tag)
{
  GtkBuilder     *builder;
  GtkWidget      *calendar_dialog;
  GtkWidget      *calendar_content_area;
  GtkWidget      *calendar_vbox;
  GtkWidget      *calendar;
  const gchar    *date_text;
  gchar          *ui_file;
  GError         *error = NULL;
  GDateTime      *current_datetime;
  guint           year, month, day;

  builder = gtk_builder_new ();

  ui_file = g_build_filename (gimp_data_directory (),
                              "ui", "plug-ins",
                              "plug-in-metadata-editor-calendar.ui", NULL);

  if (! gtk_builder_add_from_file (builder, ui_file, &error))
    {
      g_log ("", G_LOG_LEVEL_MESSAGE,
             _("Error loading calendar. %s"),
             error ? error->message : "");
      g_clear_error (&error);

      if (ui_file)
        g_free (ui_file);
      g_object_unref (builder);
      return;
    }

  if (ui_file)
    g_free (ui_file);

  date_text = gtk_entry_get_text (GTK_ENTRY (entry_widget));
  if (date_text && date_text[0] != '\0')
    {
      sscanf (date_text, "%d-%d-%d;", &year, &month, &day);
      month--;
    }
  else
    {
      current_datetime = g_date_time_new_now_local ();
      year = g_date_time_get_year (current_datetime);
      month = g_date_time_get_month (current_datetime) - 1;
      day = g_date_time_get_day_of_month (current_datetime);
    }

  calendar_dialog =
    gtk_dialog_new_with_buttons (_("Calendar Date:"),
                                 NULL,
                                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                 _("_Cancel"),  GTK_RESPONSE_CANCEL,
                                 _("Set Date"), GTK_RESPONSE_OK,
                                 NULL);

  gtk_dialog_set_default_response (GTK_DIALOG (calendar_dialog),
                                   GTK_RESPONSE_OK);
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (calendar_dialog),
                                            GTK_RESPONSE_OK,
                                            GTK_RESPONSE_CANCEL,
                                            -1);

  gimp_window_set_transient (GTK_WINDOW (calendar_dialog));

  calendar_content_area = gtk_dialog_get_content_area (GTK_DIALOG (
                            calendar_dialog));

  calendar_vbox = builder_get_widget (builder, "calendar-vbox");

  gtk_container_set_border_width (GTK_CONTAINER (calendar_vbox), 12);
  gtk_box_pack_start (GTK_BOX (calendar_content_area), calendar_vbox, TRUE, TRUE,
                      0);

  calendar = builder_get_widget (builder, "calendar");

  gtk_calendar_select_month (GTK_CALENDAR (calendar), month, year);
  gtk_calendar_select_day (GTK_CALENDAR (calendar), day);
  gtk_calendar_mark_day (GTK_CALENDAR (calendar), day);

  if (gtk_dialog_run (GTK_DIALOG (calendar_dialog)) == GTK_RESPONSE_OK)
    {
      gchar date[25];
      gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
      g_sprintf ((gchar*) &date, "%d-%02d-%02d", year, month+1, day);
      gtk_entry_set_text (GTK_ENTRY (entry_widget), date);
    }

  gtk_widget_destroy (calendar_dialog);
}

/* ============================================================================
 * ==[ SPECIAL TAGS HANDLERS ]=================================================
 * ============================================================================
 */

gboolean
hasImageSupplierTagData (GtkBuilder *builder)
{
  gint loop;

  for (loop = 0; loop < imageSupplierInfoHeader.size; loop++)
    {
      GObject     *object;
      const gchar *text;

      object = gtk_builder_get_object (builder, imageSupplierInfoTags[loop].id);

      if (! strcmp (imageSupplierInfoTags[loop].mode, "single"))
        {
          text = gtk_entry_get_text (GTK_ENTRY (object));

          if (text && *text)
            return TRUE;
        }
      else if (! strcmp (imageSupplierInfoTags[loop].mode, "multi"))
        {
          text = gtk_entry_get_text (GTK_ENTRY (object));

          if (text && *text)
            return TRUE;
        }
    }

  return FALSE;
}

gboolean
hasLocationCreationTagData (GtkBuilder *builder)
{
  gint loop;

  for (loop = 0; loop < locationCreationInfoHeader.size; loop++)
    {
      GObject     *object;
      const gchar *text;

      object = gtk_builder_get_object (builder, locationCreationInfoTags[loop].id);

      if (! strcmp (locationCreationInfoTags[loop].mode, "single"))
        {
          text = gtk_entry_get_text (GTK_ENTRY (object));

          if (text && *text)
            return TRUE;
        }
    }

  return FALSE;
}

gboolean
hasModelReleaseTagData (GtkBuilder *builder)
{
  return FALSE;
}

gboolean
hasPropertyReleaseTagData (GtkBuilder *builder)
{
  return FALSE;
}


gboolean
hasCreatorTagData (GtkBuilder *builder)
{
  gboolean has_data = FALSE;
  gint     loop;

  for (loop = 0; loop < creatorContactInfoHeader.size; loop++)
    {
      GObject *object;

      object = gtk_builder_get_object (builder, creatorContactInfoTags[loop].id);

      if (GTK_IS_ENTRY (object))
        {
          const gchar *text = gtk_entry_get_text (GTK_ENTRY (object));

          if (text && *text)
            has_data = TRUE;
        }
      else if (GTK_IS_TEXT_VIEW (object))
        {
          GtkTextView   *text_view = GTK_TEXT_VIEW (object);
          GtkTextBuffer *buffer    = gtk_text_view_get_buffer (text_view);
          GtkTextIter    start;
          GtkTextIter    end;
          gchar         *text;

          gtk_text_buffer_get_start_iter (buffer, &start);
          gtk_text_buffer_get_end_iter (buffer, &end);

          text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);

          if (text && *text)
            has_data = TRUE;

          if (text)
            g_free (text);
        }
    }

  return has_data;
}

/* ============================================================================
 * ==[ SET DIALOG METADATA ]===================================================
 * ============================================================================
 */

/* CELL EDITED */

void
cell_edited_callback (GtkCellRendererText *cell,
                      const gchar         *path_string,
                      const gchar         *new_text,
                      gpointer             data,
                      int                  index)
{
  GtkTreeModel *model;
  GtkTreePath  *path;
  GtkTreeIter   iter;

  model = (GtkTreeModel *)data;
  path = gtk_tree_path_new_from_string (path_string);

  gtk_tree_model_get_iter (model, &iter, path);

  gtk_list_store_set (GTK_LIST_STORE (model), &iter, index,
                      new_text, -1);
}

void
cell_edited_callback_combo (GtkCellRendererCombo *cell,
                            const gchar          *path_string,
                            const gchar          *new_text,
                            gpointer              data,
                            int                   column)
{
  GtkWidget        *widget;
  GtkTreeModel     *treemodel;
  GtkListStore     *liststore;
  GtkTreeIter       iter;
  GtkTreePath      *path;
  GtkTreeSelection *selection;

  widget = GTK_WIDGET (data);

  treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
  liststore = GTK_LIST_STORE (treemodel);

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));

  if (gtk_tree_selection_get_selected (GTK_TREE_SELECTION (selection),
                                       NULL, &iter))
    {
      path = gtk_tree_model_get_path (treemodel, &iter);
      gtk_tree_path_free (path);
      gtk_list_store_set (liststore, &iter, column, new_text, -1);
    }
}

static void
licensor_name_cell_edited_callback (GtkCellRendererText *cell,
                                    const gchar         *path_string,
                                    const gchar         *new_text,
                                    gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 0);
}

static void
licensor_id_cell_edited_callback (GtkCellRendererText *cell,
                                  const gchar         *path_string,
                                  const gchar         *new_text,
                                  gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 1);
}

static void
licensor_phone1_cell_edited_callback (GtkCellRendererText *cell,
                                      const gchar         *path_string,
                                      const gchar         *new_text,
                                      gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 2);
}

static void
licensor_phone_type1_cell_edited_callback (GtkCellRendererCombo *cell,
                                           const gchar          *path_string,
                                           const gchar          *new_text,
                                           gpointer              data)
{
  cell_edited_callback_combo (cell, path_string, new_text, data, 3);
}

static void
licensor_phone2_cell_edited_callback (GtkCellRendererText *cell,
                                      const gchar         *path_string,
                                      const gchar         *new_text,
                                      gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 4);
}

static void
licensor_phone_type2_cell_edited_callback (GtkCellRendererCombo *cell,
                                           const gchar          *path_string,
                                           const gchar          *new_text,
                                           gpointer              data)
{
  cell_edited_callback_combo (cell, path_string, new_text, data, 5);
}

static void
licensor_email_cell_edited_callback (GtkCellRendererText *cell,
                                     const gchar         *path_string,
                                     const gchar         *new_text,
                                     gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 6);
}

static void
licensor_web_cell_edited_callback (GtkCellRendererText *cell,
                                   const gchar         *path_string,
                                   const gchar         *new_text,
                                   gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 7);
}

static void
cr_owner_name_cell_edited_callback (GtkCellRendererText *cell,
                                    const gchar         *path_string,
                                    const gchar         *new_text,
                                    gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 0);
}

static void
cr_owner_id_cell_edited_callback (GtkCellRendererText *cell,
                                  const gchar         *path_string,
                                  const gchar         *new_text,
                                  gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 1);
}

static void
img_cr8_name_cell_edited_callback (GtkCellRendererText *cell,
                                   const gchar         *path_string,
                                   const gchar         *new_text,
                                   gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 0);
}

static void
img_cr8_id_cell_edited_callback (GtkCellRendererText *cell,
                                 const gchar         *path_string,
                                 const gchar         *new_text,
                                 gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 1);
}

static void
aoo_copyright_notice_cell_edited_callback (GtkCellRendererText *cell,
                                           const gchar         *path_string,
                                           const gchar         *new_text,
                                           gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 5);
}

static void
aoo_source_inv_cell_edited_callback (GtkCellRendererText *cell,
                                     const gchar         *path_string,
                                     const gchar         *new_text,
                                     gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 4);
}

static void
aoo_source_cell_edited_callback (GtkCellRendererText *cell,
                                 const gchar         *path_string,
                                 const gchar         *new_text,
                                 gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 3);
}

static void
aoo_creator_cell_edited_callback (GtkCellRendererText *cell,
                                  const gchar         *path_string,
                                  const gchar         *new_text,
                                  gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 2);
}

static void
aoo_date_creat_cell_edited_callback (GtkCellRendererText *cell,
                                     const gchar         *path_string,
                                     const gchar         *new_text,
                                     gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 1);
}

static void
aoo_title_cell_edited_callback (GtkCellRendererText *cell,
                                const gchar         *path_string,
                                const gchar         *new_text,
                                gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 0);
}

static void
reg_org_id_cell_edited_callback (GtkCellRendererText *cell,
                                 const gchar         *path_string,
                                 const gchar         *new_text,
                                 gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 0);
}

static void
reg_item_id_cell_edited_callback (GtkCellRendererText *cell,
                                  const gchar         *path_string,
                                  const gchar         *new_text,
                                  gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 1);
}

static void
loc_sho_sub_loc_cell_edited_callback (GtkCellRendererText *cell,
                                      const gchar         *path_string,
                                      const gchar         *new_text,
                                      gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 0);
}

static void
loc_sho_city_cell_edited_callback (GtkCellRendererText *cell,
                                   const gchar         *path_string,
                                   const gchar         *new_text,
                                   gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 1);
}

static void
loc_sho_state_prov_cell_edited_callback (GtkCellRendererText *cell,
    const gchar         *path_string,
    const gchar         *new_text,
    gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 2);
}

static void
loc_sho_cntry_cell_edited_callback (GtkCellRendererText *cell,
                                    const gchar         *path_string,
                                    const gchar         *new_text,
                                    gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 3);
}

static void
loc_sho_cntry_iso_cell_edited_callback (GtkCellRendererText *cell,
                                        const gchar         *path_string,
                                        const gchar         *new_text,
                                        gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 4);
}

static void
loc_sho_wrld_reg_cell_edited_callback (GtkCellRendererText *cell,
                                       const gchar         *path_string,
                                       const gchar         *new_text,
                                       gpointer             data)
{
  cell_edited_callback (cell, path_string, new_text, data, 5);
}

static void
prop_rel_id_cell_edited_callback (GtkCellRendererText *cell,
                                  const gchar         *path_string,
                                  const gchar         *new_text,
                                  gpointer             data)
{
  GtkTreeModel *model;
  GtkTreePath  *path;
  GtkTreeIter   iter;
  gint          column;
  model = (GtkTreeModel *)data;
  path = gtk_tree_path_new_from_string (path_string);

  column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));

  gtk_tree_model_get_iter (model, &iter, path);

  gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
                      new_text, -1);
}

static void
mod_rel_id_cell_edited_callback (GtkCellRendererText *cell,
                                 const gchar         *path_string,
                                 const gchar         *new_text,
                                 gpointer             data)
{
  GtkTreeModel *model;
  GtkTreePath  *path;
  GtkTreeIter   iter;
  gint          column;

  model = (GtkTreeModel *)data;
  path = gtk_tree_path_new_from_string (path_string);

  column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));

  gtk_tree_model_get_iter (model, &iter, path);

  gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
                      new_text, -1);
}

static void
organisation_image_name_cell_edited_callback (GtkCellRendererText *cell,
                                              const gchar         *path_string,
                                              const gchar         *new_text,
                                              gpointer             data)
{
  GtkTreeModel *model;
  GtkTreePath  *path;
  GtkTreeIter   iter;
  gint          column;

  model = (GtkTreeModel *)data;
  path = gtk_tree_path_new_from_string (path_string);

  column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));

  gtk_tree_model_get_iter (model, &iter, path);

  gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
                      new_text, -1);
}

static void
organisation_image_code_cell_edited_callback (GtkCellRendererText *cell,
                                              const gchar         *path_string,
                                              const gchar         *new_text,
                                              gpointer             data)
{
  GtkTreeModel *model;
  GtkTreePath  *path;
  GtkTreeIter   iter;
  gint          column;

  model = (GtkTreeModel *)data;
  path = gtk_tree_path_new_from_string (path_string);

  column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));

  gtk_tree_model_get_iter (model, &iter, path);

  gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
                      new_text, -1);
}


/* CELL / ROW REMOVE */

static void
list_row_remove_callback (GtkWidget *widget,
                          gpointer   data,
                          gchar     *tag)
{
  GtkBuilder       *builder = data;
  GtkWidget        *list_widget;
  GtkListStore     *liststore;
  GtkTreeIter       iter;
  GtkTreeModel     *treemodel;
  GtkTreeSelection *selection;
  GtkTreePath      *path;

  list_widget = builder_get_widget (builder, tag);

  treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget));
  liststore = GTK_LIST_STORE (treemodel);

  selection = gtk_tree_view_get_selection ((GtkTreeView *)list_widget);

  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
    {
      gint number_of_rows;

      path = gtk_tree_model_get_path (treemodel, &iter);
      gtk_list_store_remove (liststore, &iter);
      gtk_tree_path_free (path);

      number_of_rows = gtk_tree_model_iter_n_children (treemodel, NULL);
      /* Make sur that two rows are always showing, else it looks ugly. */
      if (number_of_rows < 2)
        {
          gtk_list_store_append (liststore, &iter);
        }
    }
}

static void
property_release_id_remove_callback (GtkWidget *widget,
                                     gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.plus.PropertyReleaseID");
}

static void
model_release_id_remove_callback (GtkWidget *widget,
                                  gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.plus.ModelReleaseID");
}

static void
shown_location_remove_callback (GtkWidget *widget,
                                gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.iptcExt.LocationShown");
}

static void
feat_org_name_remove_callback (GtkWidget *widget,
                               gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.iptcExt.OrganisationInImageName");
}

static void
feat_org_code_remove_callback (GtkWidget *widget,
                               gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.iptcExt.OrganisationInImageCode");
}

static void
artwork_object_remove_callback (GtkWidget *widget,
                                gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.iptcExt.ArtworkOrObject");
}

static void
reg_entry_remove_callback (GtkWidget *widget,
                           gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.iptcExt.RegistryId");
}

static void
image_creator_remove_callback (GtkWidget *widget,
                               gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.plus.ImageCreator");
}

static void
copyright_own_remove_callback (GtkWidget *widget,
                               gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.plus.CopyrightOwner");
}

static void
licensor_remove_callback (GtkWidget *widget,
                          gpointer   data)
{
  list_row_remove_callback (widget, data, "Xmp.plus.Licensor");
}


/* CELL / ROW ADD */

static void
list_row_add_callback (GtkWidget *widget,
                       gpointer   data,
                       gchar     *tag)
{
  GtkBuilder    *builder = data;
  GtkWidget     *list_widget;
  GtkListStore  *liststore;
  GtkTreeIter    iter;

  list_widget = builder_get_widget (builder, tag);

  liststore = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget)));

  gtk_list_store_append (liststore, &iter);
}

static void
property_release_id_add_callback (GtkWidget *widget,
                                  gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.plus.PropertyReleaseID");
}

static void
model_release_id_add_callback (GtkWidget *widget,
                               gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.plus.ModelReleaseID");
}

static void
shown_location_add_callback (GtkWidget *widget,
                             gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.iptcExt.LocationShown");
}

static void
feat_org_name_add_callback (GtkWidget *widget,
                            gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.iptcExt.OrganisationInImageName");
}

static void
feat_org_code_add_callback (GtkWidget *widget,
                            gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.iptcExt.OrganisationInImageCode");
}

static void
artwork_object_add_callback (GtkWidget *widget,
                             gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.iptcExt.ArtworkOrObject");
}

static void
reg_entry_add_callback (GtkWidget *widget,
                        gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.iptcExt.RegistryId");
}

static void
image_creator_add_callback (GtkWidget *widget,
                            gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.plus.ImageCreator");
}

static void
copyright_own_add_callback (GtkWidget *widget,
                            gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.plus.CopyrightOwner");
}

static void
licensor_add_callback (GtkWidget *widget,
                       gpointer   data)
{
  list_row_add_callback (widget, data, "Xmp.plus.Licensor");
}

const gchar *gpstooltips[] =
{
    N_ ("Enter or edit GPS value here.\n"
        "Valid values consist of 1, 2 or 3 numbers "
        "(degrees, minutes, seconds), see the following examples:\n"
        "10deg 15' 20\", or 10\u00b0 15' 20\", or 10:15:20.45, or "
        "10 15 20, or 10 15.30, or 10.45\n"
        "Delete all text to remove the current value."),
    N_ ("Enter or edit GPS altitude value here.\n"
        "A valid value consists of one number:\n"
        "e.g. 100, or 12.24\n"
        "Depending on the selected measurement type "
        "the value should be entered in meter (m) "
        "or feet (ft)\n"
        "Delete all text to remove the current value.")
};

enum
{
  GPS_LONG_LAT_TOOLTIP,
  GPS_ALTITUDE_TOOLTIP,
};

/* Set dialog display settings and data */

static void
metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata,
                                     GtkBuilder     *builder)
{
  GtkWidget *combo_widget;
  GtkWidget *entry_widget;
  GtkWidget *text_widget;
  GtkWidget *button_widget;
  gint       width, height;
  gchar     *value;
  gint       i;

  gint32 numele = n_default_metadata_tags;

  /* Setup Buttons */
  button_widget = builder_get_widget (builder, "add_licensor_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (licensor_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_licensor_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (licensor_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_copyright_own_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (copyright_own_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_copyright_own_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (copyright_own_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_image_creator_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (image_creator_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_image_creator_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (image_creator_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_reg_entry_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (reg_entry_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_reg_entry_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (reg_entry_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_artwork_object_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (artwork_object_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_artwork_object_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (artwork_object_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_feat_org_code_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (feat_org_code_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_feat_org_code_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (feat_org_code_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_feat_org_name_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (feat_org_name_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_feat_org_name_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (feat_org_name_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_shown_location_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (shown_location_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_shown_location_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (shown_location_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_model_rel_id_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (model_release_id_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_model_rel_id_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (model_release_id_remove_callback),
                    builder);

  button_widget = builder_get_widget (builder, "add_prop_rel_id_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (property_release_id_add_callback),
                    builder);

  button_widget = builder_get_widget (builder, "rem_prop_rel_id_button");
  g_signal_connect (G_OBJECT (button_widget), "clicked",
                    G_CALLBACK (property_release_id_remove_callback),
                    builder);


  /* Setup Comboboxes */
  combo_widget = builder_get_widget (builder, "Xmp.xmp.Rating");
  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                  _("Unrated"));
  for (i = 1; i < 6; i++)
    {
      gchar *display = g_strdup_printf ("%d", i);

      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      display);
      g_free (display);
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Xmp.xmpRights.Marked");
  for (i = 0; i < 3; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (marked[i].display));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Xmp.photoshop.Urgency");
  for (i = 0; i < 9; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (urgency[i]));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Xmp.plus.MinorModelAgeDisclosure");
  for (i = 0; i < 13; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (minormodelagedisclosure[i].display));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Xmp.plus.ModelReleaseStatus");
  for (i = 0; i < n_modelreleasestatus; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (modelreleasestatus[i].display));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
  gtk_widget_get_size_request (combo_widget, &width, &height);
  gtk_widget_set_size_request (combo_widget, 180, height);

  combo_widget = builder_get_widget (builder, "Xmp.iptcExt.DigitalSourceType");
  for (i = 0; i < n_digitalsourcetype; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (digitalsourcetype[i].display));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Xmp.plus.PropertyReleaseStatus");
  for (i = 0; i < 4; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (propertyreleasestatus[i].display));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
  gtk_widget_get_size_request (combo_widget, &width, &height);
  gtk_widget_set_size_request (combo_widget, 180, height);

  combo_widget = builder_get_widget (builder, "Xmp.DICOM.PatientSex");
  for (i = 0; i < 4; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (dicom[i].display));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Exif.GPSInfo.GPSLatitudeRef");
  for (i = 0; i < 3; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (gpslatref[i]));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Exif.GPSInfo.GPSLongitudeRef");
  for (i = 0; i < 3; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (gpslngref[i]));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "Exif.GPSInfo.GPSAltitudeRef");
  for (i = 0; i < 3; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (gpsaltref[i]));
    }
  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  combo_widget = builder_get_widget (builder, "GPSAltitudeSystem");
  for (i = 0; i < 2; i++)
    {
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
                                      gettext (gpsaltsys[i]));
    }

  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);

  g_signal_connect (G_OBJECT (combo_widget), "changed",
                    G_CALLBACK (gpsaltsys_combo_callback),
                    builder);

  /* Set up text view heights */
  for (i = 0; i < numele; i++)
    {
      if (! strcmp ("multi", default_metadata_tags[i].mode))
        {
          text_widget = builder_get_widget (builder,
                                            default_metadata_tags[i].tag);
          gtk_widget_get_size_request (text_widget, &width, &height);
          gtk_widget_set_size_request (text_widget, width, height + 60);
        }
    }

  for (i = 0; i < creatorContactInfoHeader.size; i++)
    {
      if (! strcmp ("multi", creatorContactInfoTags[i].mode))
        {
          text_widget = builder_get_widget (builder,
                                            creatorContactInfoTags[i].id);
          gtk_widget_get_size_request (text_widget, &width, &height);
          gtk_widget_set_size_request (text_widget, width, height + 60);
        }
    }

  /* Set up lists */
  for (i = 0; i < imageSupplierInfoHeader.size; i++)
    {
      GtkWidget *widget;

      widget = builder_get_widget (builder,
                                   imageSupplierInfoTags[i].id);

      value = gexiv2_metadata_get_tag_interpreted_string (metadata,
                                                          imageSupplierInfoTags[i].tag);

      if (value)
        {
          gchar *value_utf;

          value_utf = clean_xmp_string (value);
          g_free (value);

          if (! strcmp ("single", imageSupplierInfoTags[i].mode))
            {
              gtk_entry_set_text (GTK_ENTRY (widget), value_utf);
            }
          else if (! strcmp ("multi", imageSupplierInfoTags[i].mode))
            {
              GtkTextBuffer *buffer;

              buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
              gtk_text_buffer_set_text (buffer, value_utf, -1);
            }
          g_free (value_utf);
        }
    }

  for (i = 0; i < locationCreationInfoHeader.size; i++)
    {
      GtkWidget *widget;

      widget = builder_get_widget (builder,
                                   locationCreationInfoTags[i].id);

      value = gexiv2_metadata_get_tag_interpreted_string (metadata,
                                                          locationCreationInfoTags[i].tag);

      if (value)
        {
          gchar *value_utf;

          value_utf = clean_xmp_string (value);
          g_free (value);

          if (! strcmp ("single", locationCreationInfoTags[i].mode))
            {
              gtk_entry_set_text (GTK_ENTRY (widget), value_utf);
            }
          g_free (value_utf);
        }
    }

  /* Set up tag data */

  for (i = 0; i < numele; i++)
    {
      GtkWidget *widget;
      gint       index;

      widget = builder_get_widget (builder, default_metadata_tags[i].tag);

      if (! strcmp ("Exif.GPSInfo.GPSLongitude",
                    default_metadata_tags[i].tag))
        {
          gdouble  gps_value;
          gchar   *str;

          if (gexiv2_metadata_get_gps_longitude (metadata, &gps_value))
            {
              str = metadata_format_gps_longitude_latitude (gps_value);
              gtk_entry_set_text (GTK_ENTRY (widget), str);
              g_free (str);
            }
          gtk_widget_set_tooltip_text (widget,
                                       gettext (gpstooltips[GPS_LONG_LAT_TOOLTIP]));
          continue;
        }
      else if (! strcmp ("Exif.GPSInfo.GPSLatitude",
                         default_metadata_tags[i].tag))
        {
          gdouble  gps_value;
          gchar   *str;

          if (gexiv2_metadata_get_gps_latitude (metadata, &gps_value))
            {
              str = metadata_format_gps_longitude_latitude (gps_value);
              gtk_entry_set_text (GTK_ENTRY (widget), str);
              g_free (str);
            }
          gtk_widget_set_tooltip_text (widget,
                                       gettext (gpstooltips[GPS_LONG_LAT_TOOLTIP]));
          continue;
        }
      else if (! strcmp ("Exif.GPSInfo.GPSAltitude",
                        default_metadata_tags[i].tag))
        {
          gdouble  gps_value;
          gchar   *str;

          if (gexiv2_metadata_get_gps_altitude (metadata, &gps_value))
            {
              str = metadata_format_gps_altitude (gps_value, TRUE, "");
              gtk_entry_set_text (GTK_ENTRY (widget), str);
              g_free (str);
            }
          gtk_widget_set_tooltip_text (widget,
                                       gettext (gpstooltips[GPS_ALTITUDE_TOOLTIP]));
          continue;
        }

      index = default_metadata_tags[i].other_tag_index;

      if (default_metadata_tags[i].xmp_type == GIMP_XMP_BAG ||
          default_metadata_tags[i].xmp_type == GIMP_XMP_SEQ)
        {
          gchar **values;

          value = NULL;
          values = gexiv2_metadata_get_tag_multiple (metadata,
                                                     default_metadata_tags[i].tag);

          if (values)
            {
              gint vi;

              for (vi = 0; values[vi] != NULL; vi++)
                {
                  gchar *value_clean;

                  value_clean = clean_xmp_string (values[vi]);

                  if (value_clean != NULL && value_clean[0] != '\0')
                    {
                      if (! value)
                        {
                          value = g_strdup (value_clean);
                        }
                      else
                        {
                          gchar *tmpvalue;

                          tmpvalue = value;
                          value = g_strconcat (value, "\n", value_clean, NULL);
                          g_free (tmpvalue);
                        }
                    }
                  g_free (value_clean);
                }
            }

          if (index > -1)
            {
              gchar **equiv_values;

              /* These are all IPTC tags some of which can appear multiple times so
                * we will use get_tag_multiple. Also IPTC most commonly uses UTF-8
                * not current locale so get_tag_interpreted was wrong anyway.
                * FIXME For now lets interpret as UTF-8 and in the future read
                * and interpret based on the CharacterSet tag.
                */
              equiv_values = gexiv2_metadata_get_tag_multiple (metadata,
                                                        equivalent_metadata_tags[index].tag);

              if (equiv_values)
                {
                  gint evi;

                  for (evi = 0; equiv_values[evi] != NULL; evi++)
                    {
                      if (equiv_values[evi][0] != '\0')
                        {
                          if (! value)
                            {
                              value = g_strdup (equiv_values[evi]);
                            }
                          else
                            {
                              if (! g_strv_contains (values, equiv_values[evi]))
                                {
                                  gchar *tmpvalue;

                                  tmpvalue = value;
                                  value = g_strconcat (value, "\n", equiv_values[evi], NULL);
                                  g_free (tmpvalue);
                                }
                            }
                        }
                    }
                }
            }
          g_strfreev (values);
        }
      else
        {
          value = gexiv2_metadata_get_tag_interpreted_string (metadata,
                                                              default_metadata_tags[i].tag);

          if (value)
            {
              gchar *value_utf8 = clean_xmp_string (value);

              g_free (value);

              if (value_utf8 && value_utf8[0] != '\0')
                {
                  value = g_strdup (value_utf8);
                }
              else
                {
                  value = NULL;
                }

              g_free (value_utf8);
            }

          if (index > -1)
            {
              gchar **values;

              /* It's not very likely we will have an XMP tag that can only
               * have a single value instead of an array, which corresponds to
               * an IPTC tag that can have multiple values, but since we
               * already have this code it can't hurt to keep testing for it.
               * FIXME For now lets interpret as UTF-8 and in the future read
               * and interpret based on the CharacterSet tag.
               */
              values = gexiv2_metadata_get_tag_multiple (metadata,
                                                        equivalent_metadata_tags[index].tag);

              if (values)
                {
                  gint     i;
                  GString *str = NULL;

                  for (i = 0; values[i] != NULL; i++)
                    {
                      if (values[i][0] != '\0')
                        {
                          if (! str)
                            {
                              str = g_string_new (values[i]);
                            }
                          else
                            {
                              if (! strcmp ("multi", equivalent_metadata_tags[index].mode))
                                {
                                  g_string_append (str, "\n");
                                }
                              else
                                {
                                  g_string_append (str, ", ");
                                }
                              g_string_append (str, values[i]);
                            }
                        }
                    }

                  if (str)
                    {
                      /* If we got values from both Xmp and Iptc then compare those
                       * values and if they are different concatenate them. Usually they
                       * should be the same in which case we won't duplicate the string.
                       */
                      if (value && strcmp (value, str->str))
                        {
                          if (! strcmp ("multi", equivalent_metadata_tags[index].mode))
                            {
                              g_string_prepend (str, "\n");
                            }
                          else
                            {
                              g_string_prepend (str, ", ");
                            }
                          g_string_prepend (str, value);
                          g_free (value);
                        }
                      value = g_string_free (str, FALSE);
                    }
                }
            }
        }

      if (!strcmp ("list", default_metadata_tags[i].mode))
        {
          /* Tab: IPTC Extension, Label: Location Shown */
          if (! strcmp ("Xmp.iptcExt.LocationShown",
                        default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;
              GtkTreeIter        iter;
              gint               counter;
              gchar            **tagdata;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              /* LOCATION SHOWN - SUB LOCATION */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LOC_SHO_SUB_LOC);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (loc_sho_sub_loc_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LOC_SHO_SUB_LOC));
                }

              /* LOCATION SHOWN - CITY */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LOC_SHO_CITY);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (loc_sho_city_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LOC_SHO_CITY));
                }

              /* LOCATION SHOWN - STATE PROVINCE */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LOC_SHO_STATE_PROV);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (loc_sho_state_prov_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LOC_SHO_STATE_PROV));
                }

              /* LOCATION SHOWN - COUNTRY */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LOC_SHO_CNTRY);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (loc_sho_cntry_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LOC_SHO_CNTRY));
                }

              /* LOCATION SHOWN - COUNTRY ISO */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LOC_SHO_CNTRY_ISO);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (loc_sho_cntry_iso_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LOC_SHO_CNTRY_ISO));
                }

              /* LOCATION SHOWN - WORLD REGION */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LOC_SHO_CNTRY_WRLD_REG);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (loc_sho_wrld_reg_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LOC_SHO_CNTRY_WRLD_REG));
                }

              /* Favor the most common form: /Iptc4xmpExt:* */
              counter = count_tags (metadata, LOCATIONSHOWN_HEADER,
                                    locationshown,
                                    n_locationshown);

              tagdata = get_tags (metadata, LOCATIONSHOWN_HEADER,
                                  locationshown,
                                  n_locationshown, counter);

              if (counter == 0 || ! tagdata)
                {
                  /* Alternatively try: /iptcExt:* */
                  counter = count_tags (metadata, LOCATIONSHOWN_HEADER,
                                        locationshown_alternative,
                                        n_locationshown);

                  tagdata = get_tags (metadata, LOCATIONSHOWN_HEADER,
                                      locationshown_alternative,
                                      n_locationshown, counter);
                }

              if (counter > 0 && tagdata)
                {
                  gint item;

                  for (item = 0; item < counter; item++)
                    {
                      gchar **tagdatarow = (gchar **) tagdata[item];

                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_LOC_SHO_SUB_LOC,        tagdatarow[0],
                                          COL_LOC_SHO_CITY,           tagdatarow[1],
                                          COL_LOC_SHO_STATE_PROV,     tagdatarow[2],
                                          COL_LOC_SHO_CNTRY,          tagdatarow[3],
                                          COL_LOC_SHO_CNTRY_ISO,      tagdatarow[4],
                                          COL_LOC_SHO_CNTRY_WRLD_REG, tagdatarow[5],
                                          -1);
                    }
                  free_tagdata(tagdata, counter, n_locationshown);

                  if (counter == 1)
                    {
                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_LOC_SHO_SUB_LOC,        NULL,
                                          COL_LOC_SHO_CITY,           NULL,
                                          COL_LOC_SHO_STATE_PROV,     NULL,
                                          COL_LOC_SHO_CNTRY,          NULL,
                                          COL_LOC_SHO_CNTRY_ISO,      NULL,
                                          COL_LOC_SHO_CNTRY_WRLD_REG, NULL,
                                          -1);
                    }
                }
              else
                {
                  gint item;

                  for (item = 0; item < 2; item++)
                    {
                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_LOC_SHO_SUB_LOC,        NULL,
                                          COL_LOC_SHO_CITY,           NULL,
                                          COL_LOC_SHO_STATE_PROV,     NULL,
                                          COL_LOC_SHO_CNTRY,          NULL,
                                          COL_LOC_SHO_CNTRY_ISO,      NULL,
                                          COL_LOC_SHO_CNTRY_WRLD_REG, NULL,
                                          -1);
                    }
                }
            }
          /* Tab: IPTC Extension, Label: Featured Organization - Name */
          else if (! strcmp ("Xmp.iptcExt.OrganisationInImageName",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
                                           GTK_SELECTION_SINGLE);

              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (organisation_image_name_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_ORG_IMG_NAME));
                }

              add_to_store (value, liststore, COL_ORG_IMG_NAME);
            }
          /* Tab: IPTC Extension, Label: Featured Organization - Code */
          else if (! strcmp ("Xmp.iptcExt.OrganisationInImageCode",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
                                           GTK_SELECTION_SINGLE);

              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (organisation_image_code_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_ORG_IMG_CODE));
                }

              add_to_store (value, liststore, COL_ORG_IMG_CODE);
            }
          /* Tab: IPTC Extension, Label: Artwork or Object */
          else if (! strcmp ("Xmp.iptcExt.ArtworkOrObject",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;
              GtkTreeIter        iter;
              gint               counter;
              gchar            **tagdata;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              /* ARTWORK OR OBJECT - TITLE */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_AOO_TITLE);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (aoo_title_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_AOO_TITLE));
                }

              /* ARTWORK OR OBJECT - DATE CREATED */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_AOO_DATE_CREAT);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r != NULL; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (aoo_date_creat_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_AOO_DATE_CREAT));
                }

              /* ARTWORK OR OBJECT - CREATOR */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_AOO_CREATOR);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r != NULL; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (aoo_creator_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_AOO_CREATOR));
                }

              /* ARTWORK OR OBJECT - SOURCE */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_AOO_SOURCE);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (aoo_source_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_AOO_SOURCE));
                }

              /* ARTWORK OR OBJECT - SOURCE INVENTORY ID */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_AOO_SRC_INV_ID);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (aoo_source_inv_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_AOO_SRC_INV_ID));
                }

              /* ARTWORK OR OBJECT - COPYRIGHT NOTICE */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_AOO_CR_NOT);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (aoo_copyright_notice_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_AOO_CR_NOT));
                }

              counter = count_tags (metadata, ARTWORKOROBJECT_HEADER,
                                    artworkorobject,
                                    n_artworkorobject);

              tagdata = get_tags (metadata, ARTWORKOROBJECT_HEADER,
                                  artworkorobject,
                                  n_artworkorobject, counter);

              if (counter == 0 || ! tagdata)
                {
                  /* Alternatively try: /iptcExt:* */
                  counter = count_tags (metadata, ARTWORKOROBJECT_HEADER,
                                        artworkorobject_alternative,
                                        n_artworkorobject);

                  tagdata = get_tags (metadata, ARTWORKOROBJECT_HEADER,
                                      artworkorobject_alternative,
                                      n_artworkorobject, counter);
                }


              if (counter > 0 && tagdata)
                {
                  gint item;

                  for (item = 0; item < counter; item++)
                    {
                      gchar **tagdatarow = (gchar **) tagdata[item];

                      /* remove substring for language id in title field */
                      remove_substring (tagdatarow[COL_AOO_TITLE], lang_default);
                      if (strstr (tagdatarow[COL_AOO_TITLE], " "))
                        {
                          remove_substring (tagdatarow[COL_AOO_TITLE], " ");
                        }

                      remove_substring (tagdatarow[COL_AOO_TITLE], bag_default);
                      if (strstr (tagdatarow[COL_AOO_TITLE], " "))
                        {
                          remove_substring (tagdatarow[COL_AOO_TITLE], " ");
                        }

                      remove_substring (tagdatarow[COL_AOO_TITLE], seq_default);
                      if (strstr (tagdatarow[COL_AOO_TITLE], " "))
                        {
                          remove_substring (tagdatarow[COL_AOO_TITLE], " ");
                        }

                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_AOO_TITLE,      tagdatarow[0],
                                          COL_AOO_DATE_CREAT, tagdatarow[1],
                                          COL_AOO_CREATOR,    tagdatarow[2],
                                          COL_AOO_SOURCE,     tagdatarow[3],
                                          COL_AOO_SRC_INV_ID, tagdatarow[4],
                                          COL_AOO_CR_NOT,     tagdatarow[5],
                                          -1);
                    }
                  free_tagdata(tagdata, counter, n_artworkorobject);
                }
              else
                {
                  gint item;

                  for (item = 0; item < 2; item++)
                    {
                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_AOO_TITLE,      NULL,
                                          COL_AOO_DATE_CREAT, NULL,
                                          COL_AOO_CREATOR,    NULL,
                                          COL_AOO_SOURCE,     NULL,
                                          COL_AOO_SRC_INV_ID, NULL,
                                          COL_AOO_CR_NOT,     NULL,
                                          -1);
                    }
                }
            }
          /* Tab: IPTC Extension, Label: Model Release Identifier */
          else if (! strcmp ("Xmp.plus.ModelReleaseID",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
                                           GTK_SELECTION_SINGLE);

              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (mod_rel_id_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_PROP_REL_ID));
                }

              add_to_store (value, liststore, COL_MOD_REL_ID);
            }
          /* Tab: IPTC Extension, Label: Registry Entry */
          else if (! strcmp ("Xmp.iptcExt.RegistryId",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;
              GtkTreeIter        iter;
              gint               counter;
              gchar            **tagdata;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              /* REGISTRY - ORGANIZATION ID */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_REGISTRY_ORG_ID);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r != NULL; r = r->next)
                {
                  renderer = (GtkCellRenderer*) r->data;
                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);
                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (reg_org_id_cell_edited_callback),
                                    treemodel);
                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_REGISTRY_ORG_ID));
                }

              /* REGISTRY - ITEM ID */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_REGISTRY_ITEM_ID);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (reg_item_id_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_REGISTRY_ITEM_ID));
                }

              counter = count_tags (metadata, REGISTRYID_HEADER,
                                    registryid,
                                    n_registryid);

              tagdata = get_tags (metadata, REGISTRYID_HEADER,
                                  registryid,
                                  n_registryid, counter);

              if (counter == 0 || ! tagdata)
                {
                  /* Alternatively try: /iptcExt:* */
                  counter = count_tags (metadata, REGISTRYID_HEADER,
                                        registryid_alternative,
                                        n_registryid);

                  tagdata = get_tags (metadata, REGISTRYID_HEADER,
                                      registryid_alternative,
                                      n_registryid, counter);
                }

              if (counter > 0 && tagdata)
                {
                  gint item;

                  for (item = 0; item < counter; item++)
                    {
                      gchar **tagdatarow = (gchar **) tagdata[item];

                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_REGISTRY_ORG_ID,  tagdatarow[0],
                                          COL_REGISTRY_ITEM_ID, tagdatarow[1],
                                          -1);
                    }
                  free_tagdata(tagdata, counter, n_registryid);
                }
              else
                {
                  gint item;

                  for (item = 0; item < 2; item++)
                    {
                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_REGISTRY_ORG_ID,  NULL,
                                          COL_REGISTRY_ITEM_ID, NULL,
                                          -1);
                    }
                }
            }
          /* Tab: IPTC Extension, Label: Image Creator */
          else if (! strcmp ("Xmp.plus.ImageCreator",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;
              GtkTreeIter        iter;
              gint               counter;
              gchar            **tagdata;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              /* IMAGE CREATOR - NAME */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_IMG_CR8_NAME);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (img_cr8_name_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_IMG_CR8_NAME));
                }

              /* IMAGE CREATOR - ID */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_IMG_CR8_ID);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (img_cr8_id_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_IMG_CR8_ID));
                }

                counter = count_tags (metadata, IMAGECREATOR_HEADER,
                                      imagecreator,
                                      n_imagecreator);

                tagdata = get_tags (metadata, IMAGECREATOR_HEADER,
                                    imagecreator,
                                    n_imagecreator, counter);

                if (counter > 0 && tagdata)
                  {
                    gint item;

                    for (item = 0; item < counter; item++)
                      {
                        gchar **tagdatarow = (gchar **) tagdata[item];

                        gtk_list_store_append (liststore, &iter);
                        gtk_list_store_set (liststore, &iter,
                                            COL_IMG_CR8_NAME, tagdatarow[0],
                                            COL_IMG_CR8_ID,   tagdatarow[1],
                                            -1);
                      }
                    free_tagdata(tagdata, counter, n_imagecreator);
                  }
                else
                  {
                    gint item;

                    for (item = 0; item < 2; item++)
                      {
                        gtk_list_store_append (liststore, &iter);
                        gtk_list_store_set (liststore, &iter,
                                            COL_IMG_CR8_NAME, NULL,
                                            COL_IMG_CR8_ID,   NULL,
                                            -1);
                      }
                  }
            }
          /* Tab: IPTC Extension, Label: Copyright Owner */
          else if (! strcmp ("Xmp.plus.CopyrightOwner",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;
              GtkTreeIter        iter;
              gint               counter;
              gchar            **tagdata;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              /* COPYRIGHT OWNER - NAME */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_CR_OWNER_NAME);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (cr_owner_name_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_CR_OWNER_NAME));
                }

              /* COPYRIGHT OWNER - ID */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_CR_OWNER_ID);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (cr_owner_id_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_CR_OWNER_ID));
                }

              counter = count_tags (metadata, COPYRIGHTOWNER_HEADER,
                                    copyrightowner,
                                    n_copyrightowner);

              tagdata = get_tags (metadata, COPYRIGHTOWNER_HEADER,
                                  copyrightowner,
                                  n_copyrightowner, counter);

              if (counter > 0 && tagdata)
                {
                  gint item;

                  for (item = 0; item < counter; item++)
                    {
                      gchar **tagdatarow = (gchar **) tagdata[item];

                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_CR_OWNER_NAME, tagdatarow[0],
                                          COL_CR_OWNER_ID,   tagdatarow[1],
                                          -1);
                    }
                  free_tagdata(tagdata, counter, n_copyrightowner);
                }
              else
                {
                  gint item;

                  for (item = 0; item < 2; item++)
                    {
                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_CR_OWNER_NAME, NULL,
                                          COL_CR_OWNER_ID,   NULL,
                                          -1);
                    }
                }
            }
          /* Tab: IPTC Extension, Label: Licensor */
          else if (! strcmp ("Xmp.plus.Licensor",
                             default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkTreeModel      *phonemodel;
              GtkListStore      *liststore;
              GtkListStore      *phonestore;
              GtkTreeIter        iter;
              GtkTreeIter        phoneiter;
              gint               counter;
              gint               j;
              gchar            **tagdata;

              phonestore = gtk_list_store_new (1, G_TYPE_STRING);
              gtk_list_store_append (phonestore, &phoneiter);
              gtk_list_store_set (phonestore, &phoneiter, 0, "Unknown", -1);
              for (j=1; j < 6; j++)
                {
                  gtk_list_store_append (phonestore, &phoneiter);
                  gtk_list_store_set (phonestore, &phoneiter,
                                      0, gettext (phone_types[j].display),
                                      -1);
                }
              phonemodel = GTK_TREE_MODEL (phonestore);

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              /* LICENSOR - NAME */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_NAME);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_name_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_NAME));
                }

              /* LICENSOR - ID */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_ID);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_id_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_ID));
                }

              /* LICENSOR - PHONE NUMBER 1 */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_PHONE1);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_phone1_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_PHONE1));
                }

              /* LICENSOR - PHONE TYPE 1 */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_PHONE_TYPE1);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable",    TRUE,
                                "text-column", 0,
                                "has-entry",   FALSE,
                                "model",       phonemodel,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_phone_type1_cell_edited_callback),
                                    widget);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_PHONE_TYPE1));
                }

              /* LICENSOR - PHONE NUMBER 2 */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_PHONE2);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_phone2_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_PHONE2));
                }

              /* LICENSOR - PHONE TYPE 2 */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_PHONE_TYPE2);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable",    TRUE,
                                "text-column", 0,
                                "has-entry",   FALSE,
                                "model",       phonemodel,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_phone_type2_cell_edited_callback),
                                    widget);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_PHONE_TYPE2));
                }

              /* LICENSOR - EMAIL */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_EMAIL);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_email_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_EMAIL));
                }

              /* LICENSOR - WEB ADDRESS */
              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
                                                 COL_LICENSOR_WEB);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (licensor_web_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_LICENSOR_WEB));
                }

              counter = count_tags (metadata, LICENSOR_HEADER,
                                    licensor,
                                    n_licensor);

              tagdata = get_tags (metadata, LICENSOR_HEADER,
                                  licensor,
                                  n_licensor, counter);

              if (counter > 0 && tagdata)
                {
                  gint item;

                  for (item = 0; item < counter; item++)
                    {
                      gchar **tagdatarow = (gchar **) tagdata[item];
                      gchar   *type1;
                      gchar   *type2;
                      gint    types;

                      type1 = g_strdup (gettext (phone_types[0].display));
                      type2 = g_strdup (gettext (phone_types[0].display));

                      for (types = 0; types < 6; types++)
                        {
                          /* phone type 1 */
                          if (tagdatarow[3] &&
                              ! strcmp (tagdatarow[3],
                                        phone_types[types].data))
                            {
                              g_free (type1);
                              type1 = g_strdup (gettext (phone_types[types].display));
                            }

                          /* phone type 2 */
                          if (tagdatarow[5] &&
                              ! strcmp (tagdatarow[5],
                                        phone_types[types].data))
                            {
                              g_free (type2);
                              type2 = g_strdup (gettext (phone_types[types].display));
                            }
                        }

                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_LICENSOR_NAME,        tagdatarow[0],
                                          COL_LICENSOR_ID,          tagdatarow[1],
                                          COL_LICENSOR_PHONE1,      tagdatarow[2],
                                          COL_LICENSOR_PHONE_TYPE1, type1,
                                          COL_LICENSOR_PHONE2,      tagdatarow[4],
                                          COL_LICENSOR_PHONE_TYPE2, type2,
                                          COL_LICENSOR_EMAIL,       tagdatarow[6],
                                          COL_LICENSOR_WEB,         tagdatarow[7],
                                          -1);
                      g_free (type1);
                      g_free (type2);
                    }
                  free_tagdata(tagdata, counter, n_licensor);
                }
              else
                {
                  gint item;

                  for (item = 0; item < 2; item++)
                    {
                      gtk_list_store_append (liststore, &iter);
                      gtk_list_store_set (liststore, &iter,
                                          COL_LICENSOR_NAME,        NULL,
                                          COL_LICENSOR_ID,          NULL,
                                          COL_LICENSOR_PHONE1,      NULL,
                                          COL_LICENSOR_PHONE_TYPE1, gettext (phone_types[0].display),
                                          COL_LICENSOR_PHONE2,      NULL,
                                          COL_LICENSOR_PHONE_TYPE1, gettext (phone_types[0].display),
                                          COL_LICENSOR_EMAIL,       NULL,
                                          COL_LICENSOR_WEB,         NULL,
                                          -1);
                    }
                }
            }
          /* Tab: IPTC Extension, Label: Property Release Identifier */
          else if (! strcmp ("Xmp.plus.PropertyReleaseID",
                              default_metadata_tags[i].tag))
            {
              GList             *rlist;
              GList             *r;
              GtkTreeViewColumn *column;
              GtkCellRenderer   *renderer;
              GtkTreeModel      *treemodel;
              GtkListStore      *liststore;

              treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
              liststore = GTK_LIST_STORE (treemodel);

              gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
                                           GTK_SELECTION_SINGLE);

              column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
              rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
              for (r = rlist; r; r = r->next)
                {
                  renderer = r->data;

                  g_object_set (renderer,
                                "editable", TRUE,
                                NULL);

                  g_signal_connect (renderer, "edited",
                                    G_CALLBACK (prop_rel_id_cell_edited_callback),
                                    treemodel);

                  g_object_set_data (G_OBJECT (renderer),
                                     "column",
                                     GINT_TO_POINTER (COL_PROP_REL_ID));
                }

              add_to_store (value, liststore, COL_PROP_REL_ID);
            }
        }

      if (value)
        {
          if (! strcmp ("single", default_metadata_tags[i].mode))
            {
              gtk_entry_set_text (GTK_ENTRY (widget), value);
            }
          else if (! strcmp ("multi", default_metadata_tags[i].mode))
            {
              GtkTextBuffer *buffer;

              buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
              gtk_text_buffer_set_text (buffer, value, -1);
            }
          else if (! strcmp ("combo", default_metadata_tags[i].mode))
            {
              gint32 data = 0;

              if (! strcmp ("Exif.GPSInfo.GPSLatitudeRef",
                            default_metadata_tags[i].tag))
                {
                  if (! strncmp ("N", value, 1))
                    {
                      data = 1;
                    }
                  else if (! strncmp ("S", value, 1))
                    {
                      data = 2;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Exif.GPSInfo.GPSLongitudeRef",
                                 default_metadata_tags[i].tag))
                {
                  if (! strncmp ("E", value, 1))
                    {
                      data = 1;
                    }
                  else if (! strncmp ("W", value, 1))
                    {
                      data = 2;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Exif.GPSInfo.GPSAltitudeRef",
                                 default_metadata_tags[i].tag))
                {
                  if (! strncmp ("A", value, 1))
                    {
                      data = 1;
                    }
                  else if (! strncmp ("B", value, 1))
                    {
                      data = 2;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Xmp.xmp.Rating", default_metadata_tags[i].tag))
                {
                  if (! strcmp ("1", value))
                    {
                      data = 1;
                    }
                  else if (! strcmp ("2", value))
                    {
                      data = 2;
                    }
                  else if (! strcmp ("3", value))
                    {
                      data = 3;
                    }
                  else if (! strcmp ("4", value))
                    {
                      data = 4;
                    }
                  else if (! strcmp ("5", value))
                    {
                      data = 5;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Xmp.xmpRights.Marked",
                                 default_metadata_tags[i].tag))
                {
                  if (! strcmp ("True", value))
                    {
                      data = 1;
                    }
                  else if (! strcmp ("False", value))
                    {
                      data = 2;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Xmp.photoshop.Urgency",
                                 default_metadata_tags[i].tag))
                {
                  if (! strcmp ("1", value))
                    {
                      data = 1;
                    }
                  else if (! strcmp ("2", value))
                    {
                      data = 2;
                    }
                  else if (! strcmp ("3", value))
                    {
                      data = 3;
                    }
                  else if (! strcmp ("4", value))
                    {
                      data = 4;
                    }
                  else if (! strcmp ("5", value))
                    {
                      data = 5;
                    }
                  else if (! strcmp ("6", value))
                    {
                      data = 6;
                    }
                  else if (! strcmp ("7", value))
                    {
                      data = 7;
                    }
                  else if (! strcmp ("8", value))
                    {
                      data = 8;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Xmp.plus.MinorModelAgeDisclosure",
                                 default_metadata_tags[i].tag))
                {
                  if (! strcmp ("Age Unknown", value))
                    {
                      data = 0;
                    }
                  else if (! strcmp ("Age 25 or Over", value))
                    {
                      data = 1;
                    }
                  else if (! strcmp ("Age 24", value))
                    {
                      data = 2;
                    }
                  else if (! strcmp ("Age 23", value))
                    {
                      data = 3;
                    }
                  else if (! strcmp ("Age 22", value))
                    {
                      data = 4;
                    }
                  else if (! strcmp ("Age 21", value))
                    {
                      data = 5;
                    }
                  else if (! strcmp ("Age 20", value))
                    {
                      data = 6;
                    }
                  else if (! strcmp ("Age 19", value))
                    {
                      data = 7;
                    }
                  else if (! strcmp ("Age 18", value))
                    {
                      data = 8;
                    }
                  else if (! strcmp ("Age 17", value))
                    {
                      data = 9;
                    }
                  else if (! strcmp ("Age 16", value))
                    {
                      data = 10;
                    }
                  else if (! strcmp ("Age 15", value))
                    {
                      data = 11;
                    }
                  else if (! strcmp ("Age 14 or Under", value))
                    {
                      data = 12;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
              else if (! strcmp ("Xmp.plus.ModelReleaseStatus",
                                 default_metadata_tags[i].tag))
                {
                  gint loop;

                  for (loop = 0; loop < n_modelreleasestatus; loop++)
                    {
                      if (! strcmp (modelreleasestatus[loop].data, value))
                        {
                          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
                          break;
                        }

                      if (! strcmp (gettext (modelreleasestatus[loop].display),
                                    value))
                        {
                          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
                          break;
                        }
                    }
                }
              else if (! strcmp ("Xmp.iptcExt.DigitalSourceType",
                                 default_metadata_tags[i].tag))
                {
                  gint loop;

                  for (loop = 0; loop < n_digitalsourcetype; loop++)
                    {
                      if (! strcmp (digitalsourcetype[loop].data, value))
                        {
                          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
                          break;
                        }

                      if (! strcmp (gettext (digitalsourcetype[loop].display),
                                    value))
                        {
                          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
                          break;
                        }
                    }
                }
              else if (! strcmp ("Xmp.plus.PropertyReleaseStatus",
                                 default_metadata_tags[i].tag))
                {
                  gint loop;

                  for (loop = 0; loop < 4; loop++)
                    {
                      if (! strcmp (propertyreleasestatus[loop].data, value))
                        {
                          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
                          break;
                        }

                      if (! strcmp (gettext (propertyreleasestatus[loop].display),
                                    value))
                        {
                          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
                          break;
                        }
                    }
                }
              else if (! strcmp ("Xmp.DICOM.PatientSex",
                                 default_metadata_tags[i].tag))
                {
                  if (! strcmp ("male", value))
                    {
                      data = 1;
                    }
                  else if (! strcmp ("female", value))
                    {
                      data = 2;
                    }
                  else if (! strcmp ("other", value))
                    {
                      data = 3;
                    }

                  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
                }
            }
          g_free (value);
        }
    }

  /* Set Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:* last since the short form
   * Xmp.iptc.Ci* could have been used to set this information too. Because
   * the first (longer) form is the most common let that override the shorter
   * form in the (unlikely) case that both are present and also have
   * different values. Due to a bug in the metadata-editor previously only
   * the short form was saved.
   */
  for (i = 0; i < creatorContactInfoHeader.size; i++)
    {
      GtkWidget *widget;

      widget = builder_get_widget (builder, creatorContactInfoTags[i].id);

      value = gexiv2_metadata_get_tag_interpreted_string (metadata,
                                                          creatorContactInfoTags[i].tag);

      if (value)
        {
          gchar *value_utf;

          value_utf = clean_xmp_string (value);
          g_free (value);

          if (! strcmp ("single", creatorContactInfoTags[i].mode))
            {
              gtk_entry_set_text (GTK_ENTRY (widget), value_utf);
            }
          else if (! strcmp ("multi", creatorContactInfoTags[i].mode))
            {
              GtkTextBuffer *buffer;
              buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
              gtk_text_buffer_set_text (buffer, value_utf, -1);
            }
          g_free (value_utf);
        }
    }

  /* Set creation date */
  entry_widget = builder_get_widget (builder, "create_date_button");
  g_signal_connect (entry_widget, "clicked",
                    G_CALLBACK (on_create_date_button_clicked),
                    builder_get_widget (builder,
                                        "Xmp.photoshop.DateCreated"));

  /* Set patient dob date */
  entry_widget = builder_get_widget (builder, "dob_date_button");
  g_signal_connect (entry_widget, "clicked",
                    G_CALLBACK (on_patient_dob_date_button_clicked),
                    builder_get_widget (builder,
                                        "Xmp.DICOM.PatientDOB"));

  /* Set study date */
  entry_widget = builder_get_widget (builder, "study_date_button");
  g_signal_connect (entry_widget, "clicked",
                    G_CALLBACK (on_study_date_button_clicked),
                    builder_get_widget (builder,
                                        "Xmp.DICOM.StudyDateTime"));

  /* Set series date */
  entry_widget = builder_get_widget (builder, "series_date_button");
  g_signal_connect (entry_widget, "clicked",
                    G_CALLBACK (on_series_date_button_clicked),
                    builder_get_widget (builder,
                                        "Xmp.DICOM.SeriesDateTime"));
}


/* ============================================================================
 * ==[ WRITE METADATA ]========================================================
 * ============================================================================
 */

static void
set_tag_failed (const gchar *tag)
{
  g_log ("", G_LOG_LEVEL_MESSAGE,
         _("Failed to set metadata tag %s"), tag);
}

static void
set_tag_string (GimpMetadata *metadata,
                const gchar  *name,
                const gchar  *value)
{
  gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), name);

  if (metadata == NULL) return;
  if (name == NULL) return;
  if (value == NULL) return;

  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
                                        name, value))
    {
      set_tag_failed (name);
    }
}

static gchar *
get_phonetype (gchar *cur_value)
{
  gchar *phone_type_value = NULL;
  gint   types;

  if (cur_value != NULL)
    {
      for (types = 0; types < 6; types++)
        {
          if (! strcmp (cur_value, gettext (phone_types[types].display)))
            {
              phone_type_value = strdup (phone_types[types].data);
              break;
            }
        }
      g_free (cur_value);
    }
  if (! phone_type_value)
    phone_type_value = strdup (phone_types[0].data);
  cur_value = phone_type_value;

  return phone_type_value;
}

static void
write_metadata_tag (GtkBuilder *builder, GimpMetadata *metadata, gchar * tag, gint data_column)
{
  GtkWidget     *list_widget;
  GtkTreeModel  *treemodel;
  gint           row;
  gint           number_of_rows;
  gchar         *rc_data;
  GString       *data;

  list_widget = builder_get_widget (builder, tag);
  treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget));

  number_of_rows = gtk_tree_model_iter_n_children (treemodel, NULL);

  if (number_of_rows <= 0)
    return;

  data = g_string_sized_new (256);

  for (row = 0; row < number_of_rows; row++)
    {
      GtkTreeIter iter;

      if (gtk_tree_model_iter_nth_child (treemodel, &iter, NULL, row))
        {
          gtk_tree_model_get (treemodel, &iter,
                              data_column, &rc_data,
                              -1);
          if (rc_data && rc_data[0] != '\0')
            {
              if (row > 0)
                g_string_append (data, ", ");

              g_string_append (data, rc_data);
            }
          g_free (rc_data);
        }
    }

  g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
         "write_metadata_tag tag: %s, value: %s",
         tag, data->str);

  set_tag_string (metadata, tag, data->str);
  g_string_free (data, TRUE);
}

static void
write_metadata_tag_multiple (GtkBuilder *builder, GimpMetadata *metadata,
                             GExiv2StructureType type, const gchar * header_tag,
                             gint n_columns, const gchar **column_tags,
                             const gint special_handling[])
{
  GtkWidget     *list_widget;
  GtkTreeModel  *treemodel;
  gint           row;
  gint           number_of_rows;
  gint           counter;
  gchar          temp_tag[1024];

  /* Clear old tag data first */
  gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), header_tag);

  for (row = 0; row < 256; row++)
    {
      gint item;

      for (item = 0; item < n_columns; item++)
        {
          g_snprintf (temp_tag, sizeof (temp_tag), "%s[%d]%s",
                      header_tag, row, locationshown[item]);
          gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), temp_tag);
        }
    }

  list_widget = builder_get_widget (builder, header_tag);
  treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget));

  number_of_rows = gtk_tree_model_iter_n_children (treemodel, NULL);

  if (number_of_rows <= 0)
    return;

  gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (metadata),
                                      header_tag,
                                      GEXIV2_STRUCTURE_XA_BAG);

  /* We need a separate counter because an empty row will not be written */
  counter = 1;
  for (row = 0; row < number_of_rows; row++)
    {
      GtkTreeIter iter;

      if (gtk_tree_model_iter_nth_child (treemodel, &iter, NULL, row))
        {
          gint col;

          for (col = 0; col < n_columns; col++)
            {
              gchar *tag_data;

              gtk_tree_model_get (treemodel, &iter,
                                  col, &tag_data,
                                  -1);

              g_snprintf (temp_tag, sizeof (temp_tag), "%s[%d]%s",
                          header_tag, counter, column_tags[col]);

              if (special_handling)
                switch(special_handling[col])
                  {
                  case METADATA_PHONETYPE:
                    tag_data = get_phonetype (tag_data);
                    break;
                  }

              g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
                     "write_metadata_tag_multiple tag: %s, value: %s, col: %d",
                     temp_tag, tag_data, col);

              set_tag_string (metadata, temp_tag, tag_data);
              g_free (tag_data);
            }
          counter++;
        }
    }
}

static void
set_gps_longitude_latitude (GimpMetadata *metadata,
                            const gchar  *tag,
                            const gchar  *value)
{
  /* \u00b0 - degree symbol */
  const gchar  delimiters_dms[] = " deg'\":\u00b0";
  gchar        lng_lat[256];
  gchar       *s    = g_strdup (value);
  gchar       *str1 = NULL;
  gchar       *str2 = NULL;
  gchar       *str3 = NULL;
  gdouble      val  = 0.f;
  gint         degrees, minutes;
  gdouble      seconds;
  gboolean     remove_val = FALSE;

  g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "set_gps_longitude_latitude - Tag %s, Input value: %s", tag, value);

  if (s && s[0] != '\0')
    {
      str1 = strtok (s, delimiters_dms);
      str2 = strtok (NULL, delimiters_dms);
      str3 = strtok (NULL, delimiters_dms);

      g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "String split into: %s - %s - %s", str1, str2, str3);
    }

  g_free (s);

  if (str1 && str2 && str3)
    {
      /* Assuming degrees, minutes, seconds */
      degrees = g_ascii_strtoll (str1, NULL, 10);
      minutes = g_ascii_strtoll (str2, NULL, 10);
      seconds = g_ascii_strtod (str3, NULL);
    }
  else if (str1 && str2)
    {
      /* Assuming degrees, minutes */
      gdouble min;

      degrees = g_ascii_strtoll (str1, NULL, 10);
      min = g_ascii_strtod (str2, NULL);
      minutes = (gint) min;
      seconds = (min - (gdouble) minutes) * 60.f;
    }
  else if (str1)
    {
      /* Assuming degrees only */
      val = g_ascii_strtod (str1, NULL);
      degrees = (gint) val;
      minutes = (gint) ((val - (gdouble) degrees) * 60.f);
      seconds = ((val - (gdouble) degrees - (gdouble) (minutes / 60.f)) * 60.f * 60.f);
    }
  else
    remove_val = TRUE;

  if (!remove_val)
    {
      g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Converted values: %d - %d - %f", degrees, minutes, seconds);
      g_snprintf (lng_lat, sizeof (lng_lat),
                  "%d/1 %d/1 %d/1000",
                  abs (degrees), abs (minutes), abs ((gint) (seconds * 1000.f)));
      g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Tag: %s, output string: %s", tag, lng_lat);

      if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
                                            tag, lng_lat))
        {
          set_tag_failed (tag);
        }
    }
  else
    {
      gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), tag);
      g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Removed tag %s (no value).", tag);
    }
}

void
metadata_editor_write_callback (GtkWidget  *dialog,
                                GtkBuilder *builder,
                                gint32      image_id)
{
  GimpMetadata  *g_metadata;
  gint           max_elements;
  gint           i = 0;

  g_metadata = gimp_image_get_metadata (image_id);

  gimp_metadata_add_xmp_history (g_metadata, "metadata");

  write_metadata_tag (builder, g_metadata,
                      "Xmp.iptcExt.OrganisationInImageName",
                      COL_ORG_IMG_NAME);

  write_metadata_tag (builder, g_metadata,
                      "Xmp.iptcExt.OrganisationInImageCode",
                      COL_ORG_IMG_CODE);

  write_metadata_tag (builder, g_metadata,
                      "Xmp.plus.ModelReleaseID",
                      COL_MOD_REL_ID);

  write_metadata_tag (builder, g_metadata,
                      "Xmp.plus.PropertyReleaseID",
                      COL_PROP_REL_ID);

  write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_BAG,
                               "Xmp.iptcExt.LocationShown",
                               n_locationshown, locationshown_alternative, NULL);

  write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_BAG,
                               "Xmp.iptcExt.ArtworkOrObject",
                               n_artworkorobject, artworkorobject_alternative, NULL);

  write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_BAG,
                               "Xmp.iptcExt.RegistryId",
                               n_registryid, registryid_alternative, NULL);

  write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_SEQ,
                               "Xmp.plus.ImageCreator",
                               n_imagecreator, imagecreator, NULL);

  write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_SEQ,
                               "Xmp.plus.CopyrightOwner",
                               n_copyrightowner, copyrightowner, NULL);

  write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_SEQ,
                               "Xmp.plus.Licensor",
                               n_licensor, licensor,
                               licensor_special_handling);

  /* DO CREATOR TAGS */

  if (hasCreatorTagData (builder))
    {
      if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                            creatorContactInfoHeader.header,
                                            "type=\"Struct\""))
        {
          set_tag_failed (creatorContactInfoTags[i].tag);
        }

      for (i = 0; i < creatorContactInfoHeader.size; i++)
        {
          GObject *object = gtk_builder_get_object (builder,
                                                    creatorContactInfoTags[i].id);

          if (! strcmp ("single", creatorContactInfoTags[i].mode))
            {
              GtkEntry *entry = GTK_ENTRY (object);

              if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                    creatorContactInfoTags[i].tag,
                                                    gtk_entry_get_text (entry)))
                {
                  set_tag_failed (creatorContactInfoTags[i].tag);
                }
            }
          else if (! strcmp ("multi", creatorContactInfoTags[i].mode))
            {
              GtkTextView   *text_view = GTK_TEXT_VIEW (object);
              GtkTextBuffer *buffer;
              GtkTextIter    start;
              GtkTextIter    end;
              gchar         *text;

              buffer = gtk_text_view_get_buffer (text_view);
              gtk_text_buffer_get_start_iter (buffer, &start);
              gtk_text_buffer_get_end_iter (buffer, &end);

              text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);

              if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                    creatorContactInfoTags[i].tag,
                                                    text))
                {
                  set_tag_failed (creatorContactInfoTags[i].tag);
                }

              g_free (text);
            }
        }
    }

  /* DO SINGLE, MULTI AND COMBO TAGS */

  else
    {
      gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                 creatorContactInfoHeader.header);

      for (i = 0; i < creatorContactInfoHeader.size; i++)
        {
          gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                     creatorContactInfoTags[i].tag);
        }
    }

  max_elements = n_default_metadata_tags;

  for (i = 0; i < max_elements; i++)
    {
      GObject *object = gtk_builder_get_object (builder,
                                                default_metadata_tags[i].tag);

      /* SINGLE TAGS */

      if (! strcmp ("single", default_metadata_tags[i].mode))
        {
          GtkEntry *entry       = GTK_ENTRY (object);
          gchar    *value_entry = g_strdup (gtk_entry_get_text (entry));

          if (! strcmp ("Exif.GPSInfo.GPSLongitude",
                        default_metadata_tags[i].tag) ||
              ! strcmp ("Exif.GPSInfo.GPSLatitude",
                        default_metadata_tags[i].tag))
            {
              set_gps_longitude_latitude (g_metadata,
                                          default_metadata_tags[i].tag,
                                          value_entry);
            }
          else if (! strcmp ("Exif.GPSInfo.GPSAltitude",
                             default_metadata_tags[i].tag))
            {
              GtkWidget *combo_widget;
              gchar      alt_str[256];
              gdouble    alt_d;
              gint       msr;

              combo_widget = builder_get_widget (builder,
                                                 "GPSAltitudeSystem");
              msr = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_widget));

              alt_d = atof (gtk_entry_get_text (entry));
              if (msr == 1)
                alt_d = (alt_d * 12 * 2.54);
              else
                alt_d *= 100.f;

              g_snprintf (alt_str, sizeof (alt_str), "%d/100", (gint) alt_d);

              if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                    default_metadata_tags[i].tag,
                                                    alt_str))
                {
                  set_tag_failed (default_metadata_tags[i].tag);
                }
            }
          else
            {
              gint         index;
              const gchar *text_value = gtk_entry_get_text (entry);

              if (default_metadata_tags[i].xmp_type == GIMP_XMP_TEXT ||
                  default_metadata_tags[i].xmp_type == GIMP_XMP_NONE)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (g_metadata),
                                                      default_metadata_tags[i].tag,
                                                      GEXIV2_STRUCTURE_XA_NONE);
                  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        default_metadata_tags[i].tag,
                                                        text_value))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                }
              else
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        default_metadata_tags[i].tag,
                                                        text_value))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                }

              index = default_metadata_tags[i].other_tag_index;
              if (index > -1)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             equivalent_metadata_tags[index].tag);
                  if (*text_value &&
                      ! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        equivalent_metadata_tags[index].tag,
                                                        text_value))
                    {
                      set_tag_failed (equivalent_metadata_tags[index].tag);
                    }
                }
            }
        }

      /* MULTI TAGS */

      else if (! strcmp ("multi", default_metadata_tags[i].mode))
        {
          GtkTextView   *text_view = GTK_TEXT_VIEW (object);
          GtkTextBuffer *buffer;
          GtkTextIter    start;
          GtkTextIter    end;
          gchar         *text;
          gint           index;

          buffer = gtk_text_view_get_buffer (text_view);
          gtk_text_buffer_get_start_iter (buffer, &start);
          gtk_text_buffer_get_end_iter (buffer, &end);

          text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);

          gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                     default_metadata_tags[i].tag);

          if (text && *text)
            {
              if (default_metadata_tags[i].xmp_type == GIMP_XMP_TEXT ||
                  default_metadata_tags[i].xmp_type == GIMP_XMP_NONE)
                {
                  gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (g_metadata),
                                                      default_metadata_tags[i].tag,
                                                      GEXIV2_STRUCTURE_XA_NONE);
                  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        default_metadata_tags[i].tag,
                                                        text))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                }
              else
                {
                  gchar **multi;

                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);

                  /* We have one value per line. */
                  multi = g_strsplit (text, "\n", 0);

                  if (! gexiv2_metadata_set_tag_multiple (GEXIV2_METADATA (g_metadata),
                                                          default_metadata_tags[i].tag,
                                                          (const gchar **) multi))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                  g_strfreev (multi);
                }
            }

            index = default_metadata_tags[i].other_tag_index;
            if (index > -1)
              {
                gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                           equivalent_metadata_tags[index].tag);

                if (text && *text)
                  {
                    if (! strcmp ("multi", equivalent_metadata_tags[index].mode))
                      {
                        gchar **multi;

                        multi = g_strsplit (text, "\n", 0);

                        if (! gexiv2_metadata_set_tag_multiple (GEXIV2_METADATA (g_metadata),
                                                                equivalent_metadata_tags[index].tag,
                                                                (const gchar **) multi))
                          {
                            set_tag_failed (equivalent_metadata_tags[index].tag);
                          }

                        g_strfreev (multi);
                      }
                    else if (! strcmp ("single", equivalent_metadata_tags[index].mode))
                      {
                        /* Convert from multiline to single line: keep the \n and just add the whole text. */
                        if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                              equivalent_metadata_tags[index].tag,
                                                              text))
                          {
                            set_tag_failed (equivalent_metadata_tags[index].tag);
                          }
                      }
                    else
                      {
                        g_warning ("Copying from multiline tag %s to %s tag %s not implemented!",
                                   default_metadata_tags[i].tag,
                                   equivalent_metadata_tags[index].mode,
                                   equivalent_metadata_tags[index].tag);
                      }
                  }
              }

          if (text)
            g_free (text);
        }
      else if (! strcmp ("list", default_metadata_tags[i].mode))
        {
          /* MIGHT DO SOMETHING HERE */
        }

      /* COMBO TAGS */

      else if (! strcmp ("combo", default_metadata_tags[i].mode))
        {
          GtkComboBoxText *combo;
          gint32           value;

          combo = GTK_COMBO_BOX_TEXT (object);
          value = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));

          if (! strcmp ("Xmp.photoshop.Urgency", default_metadata_tags[i].tag))
            {
              /* IPTC tab - Urgency */
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             "Iptc.Application2.Urgency");
                }
              else
                {
                  gchar *save;

                  save = g_strdup_printf ("%d", value);

                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  save);
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  "Iptc.Application2.Urgency",
                                                  save);
                  g_free (save);
                }
            }
          else if (! strcmp ("Xmp.xmpRights.Marked",
                             default_metadata_tags[i].tag))
            {
              /* Description tab - Copyright Status */
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                }
              else
                {
                  gchar *save_value;

                  if (value == 1)
                    save_value = g_strdup_printf ("%s", "True");
                  else /* (value == 2) */
                    save_value = g_strdup_printf ("%s", "False");

                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  save_value);
                  g_free (save_value);
                }
            }
          else if (! strcmp ("Xmp.xmp.Rating", default_metadata_tags[i].tag))
            {
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                }
              else
                {
                  gchar *save;

                  save = g_strdup_printf ("%d", value);

                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  save);
                  g_free (save);
                }
            }
          else if (! strcmp ("Xmp.DICOM.PatientSex",
                             default_metadata_tags[i].tag))
            {
              switch (value)
                {
                case 0:
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  break;

                case 1:
                  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        default_metadata_tags[i].tag,
                                                        "male"))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                  break;

                case 2:
                  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        default_metadata_tags[i].tag,
                                                        "female"))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                  break;

                case 3:
                  if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                        default_metadata_tags[i].tag,
                                                        "other"))
                    {
                      set_tag_failed (default_metadata_tags[i].tag);
                    }
                  break;
                }
            }
          else if (! strcmp ("Exif.GPSInfo.GPSLongitudeRef",
                             default_metadata_tags[i].tag))
            {
              switch (value)
                {
                case 0:
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  break;

                case 1:
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  "E");
                  break;

                case 2:
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  "W");
                  break;
                }
            }
          else if (! strcmp ("Exif.GPSInfo.GPSLatitudeRef",
                             default_metadata_tags[i].tag))
            {
              switch (value)
                {
                case 0:
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  break;

                case 1:
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  "N");
                  break;

                case 2:
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  "S");
                  break;
                }
            }
          else if (! strcmp ("Exif.GPSInfo.GPSAltitudeRef",
                             default_metadata_tags[i].tag))
            {
              switch (value)
                {
                case 0:
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                  break;

                case 1:
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  "0");
                  break;

                case 2:
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  "1");
                  break;
                }
            }
          else if (! strcmp ("Xmp.plus.ModelReleaseStatus",
                             default_metadata_tags[i].tag))
            {
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                }
              else
                {
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  modelreleasestatus[value].data);
                }
            }
          else if (! strcmp ("Xmp.plus.PropertyReleaseStatus",
                             default_metadata_tags[i].tag))
            {
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                }
              else
                {
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  propertyreleasestatus[value].data);
                }
            }
          else if (! strcmp ("Xmp.plus.MinorModelAgeDisclosure",
                             default_metadata_tags[i].tag))
            {
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                }
              else
                {
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  minormodelagedisclosure[value].data);
                }
            }
          else if (! strcmp ("Xmp.iptcExt.DigitalSourceType",
                             default_metadata_tags[i].tag))
            {
              if (value == 0)
                {
                  gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
                                             default_metadata_tags[i].tag);
                }
              else
                {
                  gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
                                                  default_metadata_tags[i].tag,
                                                  digitalsourcetype[value].data);
                }
            }
        }
    }

  gimp_image_set_metadata (image_id, g_metadata);
}

/* ============================================================================
 * ==[ METADATA IMPORT / EXPORT FILE DIALOG UI ]===============================
 * ============================================================================
 */

static void
import_dialog_metadata (metadata_editor *args)
{
  GtkWidget *file_dialog;
  gchar     *filename;

  file_dialog = gtk_file_chooser_dialog_new (_("Import Metadata File"),
                                             NULL,
                                             GTK_FILE_CHOOSER_ACTION_OPEN,
                                             _("_Cancel"), GTK_RESPONSE_CANCEL,
                                             _("_Import"), GTK_RESPONSE_ACCEPT,
                                             NULL);

  gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_dialog),
                                 args->filename);

  if (gtk_dialog_run (GTK_DIALOG (file_dialog)) == GTK_RESPONSE_ACCEPT)
    {
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_dialog));

      if (filename)
        {
          if (args->filename)
            {
              g_free (args->filename);
            }

          args->filename = g_strdup (filename);
          import_file_metadata (args);
        }
    }

  gtk_widget_destroy (file_dialog);
}

static void
export_dialog_metadata (metadata_editor *args)
{
  GtkWidget *file_dialog;
  gchar     *filename;

  file_dialog = gtk_file_chooser_dialog_new (_("Export Metadata File"),
                                             NULL,
                                             GTK_FILE_CHOOSER_ACTION_SAVE,
                                             _("_Cancel"), GTK_RESPONSE_CANCEL,
                                             _("_Export"), GTK_RESPONSE_ACCEPT,
                                             NULL);

  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_dialog),
                                                  TRUE);
  gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_dialog),
                                 args->filename);

  if (gtk_dialog_run (GTK_DIALOG (file_dialog)) == GTK_RESPONSE_ACCEPT)
    {
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_dialog));

      if (filename)
        {
          if (args->filename)
            {
              g_free (args->filename);
            }

          args->filename = g_strdup (filename);
          export_file_metadata (args);
        }
    }

  gtk_widget_destroy (file_dialog);
}

static void
impex_combo_callback (GtkComboBoxText *combo,
                      gpointer         data)
{
  metadata_editor *args;
  gint32           selection;

  args = data;
  selection = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));

  switch (selection)
    {
    case 1: /* Import */
      import_dialog_metadata (args);
      gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
      break;

    case 2: /* Export */
      export_dialog_metadata (args);
      gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
      break;
    }
}


static void
gpsaltsys_combo_callback (GtkComboBoxText *combo,
                          gpointer         data)
{
  GtkWidget  *entry;
  GtkBuilder *builder;
  gint32      selection;
  gchar       alt_str[256];
  double      alt_d;

  builder = data;
  selection = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));

  entry = builder_get_widget (builder, "Exif.GPSInfo.GPSAltitude");

  switch (selection)
    {
    case 0: /* Meters */
      if (last_gpsaltsys_sel != 0)
        {
          alt_d = atof (gtk_entry_get_text (GTK_ENTRY (entry)));
          alt_d = (alt_d * (12 * 2.54)) / 100;

          g_snprintf (alt_str, sizeof (alt_str), "%.2f", (float)alt_d);

          gtk_entry_set_text (GTK_ENTRY (entry), alt_str);
        }
      break;

    case 1: /* Feet */
      if (last_gpsaltsys_sel != 1)
        {
          alt_d = atof (gtk_entry_get_text (GTK_ENTRY (entry)));
          alt_d = alt_d * 3.28;

          g_snprintf (alt_str, sizeof (alt_str), "%.2f", (float)alt_d);

          gtk_entry_set_text (GTK_ENTRY (entry), alt_str);
        }
      break;
    }

  last_gpsaltsys_sel = selection;
}
