summaryrefslogtreecommitdiffstats
path: root/plug-ins/metadata
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
commit3c57dd931145d43f2b0aef96c4d178135956bf91 (patch)
tree3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/metadata
parentInitial commit. (diff)
downloadgimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz
gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/metadata')
-rw-r--r--plug-ins/metadata/Makefile.am82
-rw-r--r--plug-ins/metadata/Makefile.in1114
-rw-r--r--plug-ins/metadata/metadata-editor.c4757
-rw-r--r--plug-ins/metadata/metadata-editor.h27
-rw-r--r--plug-ins/metadata/metadata-impexp.c244
-rw-r--r--plug-ins/metadata/metadata-impexp.h30
-rw-r--r--plug-ins/metadata/metadata-misc.h70
-rw-r--r--plug-ins/metadata/metadata-tags.c515
-rw-r--r--plug-ins/metadata/metadata-tags.h253
-rw-r--r--plug-ins/metadata/metadata-viewer.c683
-rw-r--r--plug-ins/metadata/metadata-xml.c1147
-rw-r--r--plug-ins/metadata/metadata-xml.h98
12 files changed, 9020 insertions, 0 deletions
diff --git a/plug-ins/metadata/Makefile.am b/plug-ins/metadata/Makefile.am
new file mode 100644
index 0000000..23e00f2
--- /dev/null
+++ b/plug-ins/metadata/Makefile.am
@@ -0,0 +1,82 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+metadata_editor_RC = metadata-editor.rc.o
+metadata_viewer_RC = metadata-viewer.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+me_libexecdir = $(gimpplugindir)/plug-ins/metadata-editor
+mv_libexecdir = $(gimpplugindir)/plug-ins/metadata-viewer
+
+me_libexec_PROGRAMS = metadata-editor
+mv_libexec_PROGRAMS = metadata-viewer
+
+metadata_editor_SOURCES = \
+ metadata-editor.c \
+ metadata-editor.h \
+ metadata-impexp.c \
+ metadata-impexp.h \
+ metadata-misc.h \
+ metadata-tags.c \
+ metadata-tags.h \
+ metadata-xml.c \
+ metadata-xml.h
+
+metadata_viewer_SOURCES = \
+ metadata-viewer.c \
+ metadata-tags.c \
+ metadata-tags.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+metadata_viewer_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_viewer_RC)
+
+metadata_editor_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_editor_RC)
+
diff --git a/plug-ins/metadata/Makefile.in b/plug-ins/metadata/Makefile.in
new file mode 100644
index 0000000..8d8e2e6
--- /dev/null
+++ b/plug-ins/metadata/Makefile.in
@@ -0,0 +1,1114 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+me_libexec_PROGRAMS = metadata-editor$(EXEEXT)
+mv_libexec_PROGRAMS = metadata-viewer$(EXEEXT)
+subdir = plug-ins/metadata
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(me_libexecdir)" \
+ "$(DESTDIR)$(mv_libexecdir)"
+PROGRAMS = $(me_libexec_PROGRAMS) $(mv_libexec_PROGRAMS)
+am_metadata_editor_OBJECTS = metadata-editor.$(OBJEXT) \
+ metadata-impexp.$(OBJEXT) metadata-tags.$(OBJEXT) \
+ metadata-xml.$(OBJEXT)
+metadata_editor_OBJECTS = $(am_metadata_editor_OBJECTS)
+am__DEPENDENCIES_1 =
+metadata_editor_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(metadata_editor_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_metadata_viewer_OBJECTS = metadata-viewer.$(OBJEXT) \
+ metadata-tags.$(OBJEXT)
+metadata_viewer_OBJECTS = $(am_metadata_viewer_OBJECTS)
+metadata_viewer_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(metadata_viewer_RC)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/metadata-editor.Po \
+ ./$(DEPDIR)/metadata-impexp.Po ./$(DEPDIR)/metadata-tags.Po \
+ ./$(DEPDIR)/metadata-viewer.Po ./$(DEPDIR)/metadata-xml.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(metadata_editor_SOURCES) $(metadata_viewer_SOURCES)
+DIST_SOURCES = $(metadata_editor_SOURCES) $(metadata_viewer_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@metadata_editor_RC = metadata-editor.rc.o
+@HAVE_WINDRES_TRUE@metadata_viewer_RC = metadata-viewer.rc.o
+AM_LDFLAGS = $(mwindows)
+me_libexecdir = $(gimpplugindir)/plug-ins/metadata-editor
+mv_libexecdir = $(gimpplugindir)/plug-ins/metadata-viewer
+metadata_editor_SOURCES = \
+ metadata-editor.c \
+ metadata-editor.h \
+ metadata-impexp.c \
+ metadata-impexp.h \
+ metadata-misc.h \
+ metadata-tags.c \
+ metadata-tags.h \
+ metadata-xml.c \
+ metadata-xml.h
+
+metadata_viewer_SOURCES = \
+ metadata-viewer.c \
+ metadata-tags.c \
+ metadata-tags.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+metadata_viewer_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_viewer_RC)
+
+metadata_editor_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_editor_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/metadata/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/metadata/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-me_libexecPROGRAMS: $(me_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(me_libexec_PROGRAMS)'; test -n "$(me_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(me_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(me_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(me_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(me_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-me_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(me_libexec_PROGRAMS)'; test -n "$(me_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(me_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(me_libexecdir)" && rm -f $$files
+
+clean-me_libexecPROGRAMS:
+ @list='$(me_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-mv_libexecPROGRAMS: $(mv_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(mv_libexec_PROGRAMS)'; test -n "$(mv_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(mv_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(mv_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(mv_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(mv_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-mv_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(mv_libexec_PROGRAMS)'; test -n "$(mv_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(mv_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(mv_libexecdir)" && rm -f $$files
+
+clean-mv_libexecPROGRAMS:
+ @list='$(mv_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+metadata-editor$(EXEEXT): $(metadata_editor_OBJECTS) $(metadata_editor_DEPENDENCIES) $(EXTRA_metadata_editor_DEPENDENCIES)
+ @rm -f metadata-editor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(metadata_editor_OBJECTS) $(metadata_editor_LDADD) $(LIBS)
+
+metadata-viewer$(EXEEXT): $(metadata_viewer_OBJECTS) $(metadata_viewer_DEPENDENCIES) $(EXTRA_metadata_viewer_DEPENDENCIES)
+ @rm -f metadata-viewer$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(metadata_viewer_OBJECTS) $(metadata_viewer_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-editor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-impexp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-tags.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-viewer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-xml.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(me_libexecdir)" "$(DESTDIR)$(mv_libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-me_libexecPROGRAMS \
+ clean-mv_libexecPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/metadata-editor.Po
+ -rm -f ./$(DEPDIR)/metadata-impexp.Po
+ -rm -f ./$(DEPDIR)/metadata-tags.Po
+ -rm -f ./$(DEPDIR)/metadata-viewer.Po
+ -rm -f ./$(DEPDIR)/metadata-xml.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-me_libexecPROGRAMS install-mv_libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/metadata-editor.Po
+ -rm -f ./$(DEPDIR)/metadata-impexp.Po
+ -rm -f ./$(DEPDIR)/metadata-tags.Po
+ -rm -f ./$(DEPDIR)/metadata-viewer.Po
+ -rm -f ./$(DEPDIR)/metadata-xml.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-me_libexecPROGRAMS \
+ uninstall-mv_libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-me_libexecPROGRAMS \
+ clean-mv_libexecPROGRAMS cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-me_libexecPROGRAMS \
+ install-mv_libexecPROGRAMS install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-me_libexecPROGRAMS uninstall-mv_libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/metadata/metadata-editor.c b/plug-ins/metadata/metadata-editor.c
new file mode 100644
index 0000000..87a2e87
--- /dev/null
+++ b/plug-ins/metadata/metadata-editor.c
@@ -0,0 +1,4757 @@
+/* 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;
+}
diff --git a/plug-ins/metadata/metadata-editor.h b/plug-ins/metadata/metadata-editor.h
new file mode 100644
index 0000000..c3fe93c
--- /dev/null
+++ b/plug-ins/metadata/metadata-editor.h
@@ -0,0 +1,27 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __METADATA_EDITOR_H__
+#define __METADATA_EDITOR_H__
+
+extern void metadata_editor_write_callback (GtkWidget *dialog,
+ GtkBuilder *builder,
+ gint32 image_id);
+
+#endif /* __METADATA_EDITOR_H__ */
diff --git a/plug-ins/metadata/metadata-impexp.c b/plug-ins/metadata/metadata-impexp.c
new file mode 100644
index 0000000..c7bc176
--- /dev/null
+++ b/plug-ins/metadata/metadata-impexp.c
@@ -0,0 +1,244 @@
+/* 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 <gexiv2/gexiv2.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-xml.h"
+#include "metadata-misc.h"
+#include "metadata-tags.h"
+#include "metadata-impexp.h"
+#include "metadata-editor.h"
+
+extern gboolean gimpmetadata;
+extern gboolean xmptag;
+extern gboolean iptctag;
+extern gboolean tagvalue;
+extern gboolean tagname;
+extern gboolean force_write;
+extern gchar *str_tag_value;
+extern gchar *str_tag_name;
+
+const GMarkupParser xml_markup_parser =
+{
+ xml_parser_start_element,
+ xml_parser_end_element,
+ xml_parser_data,
+ NULL, /* passthrough */
+ NULL /* error */
+};
+
+
+/* ============================================================================
+ * ==[ METADATA IMPORT TEMPLATE ]==============================================
+ * ============================================================================
+ */
+void
+import_file_metadata(metadata_editor *args)
+{
+ GimpXmlParser *xml_parser;
+ GError *error = NULL;
+ FILE *file;
+
+ gimpmetadata = FALSE;
+ xmptag = FALSE;
+ iptctag = FALSE;
+ tagvalue = FALSE;
+ tagname = FALSE;
+
+ file = g_fopen (args->filename, "r");
+ if (file != NULL)
+ {
+ /* parse xml data fetched from file */
+ xml_parser = xml_parser_new (&xml_markup_parser, args);
+ if (! xml_parser_parse_file (xml_parser, args->filename, &error))
+ {
+ g_warning ("Error parsing xml: %s.", error? error->message: "");
+ g_clear_error (&error);
+ }
+ xml_parser_free (xml_parser);
+
+ fclose (file);
+ }
+}
+
+/* ============================================================================
+ * ==[ METADATA EXPORT TEMPLATE ]==============================================
+ * ============================================================================
+ */
+void
+export_file_metadata (metadata_editor *args)
+{
+ FILE *file;
+ GString *xmldata;
+ gint i, size;
+
+ if (force_write == TRUE)
+ {
+ /* Save fields in case of updates */
+ metadata_editor_write_callback (args->dialog, args->builder, args->image_id);
+ /* Fetch a fresh copy of the metadata */
+ args->metadata = GEXIV2_METADATA (gimp_image_get_metadata (args->image_id));
+ }
+
+ xmldata = g_string_new ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<gimp-metadata>\n");
+
+ /* HANDLE IPTC */
+ for (i = 0; i < n_equivalent_metadata_tags; i++)
+ {
+ int index = equivalent_metadata_tags[i].other_tag_index;
+ g_string_append (xmldata, "\t<iptc-tag>\n");
+ g_string_append (xmldata, "\t\t<tag-name>");
+ g_string_append (xmldata, equivalent_metadata_tags[i].tag);
+ g_string_append (xmldata, "</tag-name>\n");
+ g_string_append (xmldata, "\t\t<tag-mode>");
+ g_string_append (xmldata, equivalent_metadata_tags[i].mode);
+ g_string_append (xmldata, "</tag-mode>\n");
+ g_string_append (xmldata, "\t\t<tag-value>");
+
+ if (!strcmp("single", default_metadata_tags[index].mode) ||
+ !strcmp("multi", default_metadata_tags[index].mode))
+ {
+ const gchar *value;
+
+ value = get_tag_ui_text (args, default_metadata_tags[index].tag,
+ default_metadata_tags[index].mode);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+ g_string_append (xmldata, value_utf);
+ g_free (value_utf);
+ }
+ }
+ else if (!strcmp("combo", default_metadata_tags[index].mode))
+ {
+ gint data = get_tag_ui_combo (args, default_metadata_tags[index].tag,
+ default_metadata_tags[index].mode);
+ g_string_append_printf (xmldata, "%d", data);
+ }
+ else if (!strcmp("list", default_metadata_tags[i].mode))
+ {
+ /* No IPTC lists elements at this point */
+ }
+
+ g_string_append (xmldata, "</tag-value>\n");
+ g_string_append (xmldata, "\t</iptc-tag>\n");
+ }
+
+ /* HANDLE XMP */
+ for (i = 0; i < n_default_metadata_tags; i++)
+ {
+ g_string_append (xmldata, "\t<xmp-tag>\n");
+ g_string_append (xmldata, "\t\t<tag-name>");
+ g_string_append (xmldata, default_metadata_tags[i].tag);
+ g_string_append (xmldata, "</tag-name>\n");
+ g_string_append (xmldata, "\t\t<tag-mode>");
+ g_string_append (xmldata, default_metadata_tags[i].mode);
+ g_string_append (xmldata, "</tag-mode>\n");
+
+ if (!strcmp("single", default_metadata_tags[i].mode) ||
+ !strcmp("multi", default_metadata_tags[i].mode))
+ {
+ const gchar *value;
+
+ g_string_append (xmldata, "\t\t<tag-value>");
+ value = get_tag_ui_text (args, default_metadata_tags[i].tag,
+ default_metadata_tags[i].mode);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+ g_string_append (xmldata, value_utf);
+ g_free (value_utf);
+ }
+
+ g_string_append (xmldata, "</tag-value>\n");
+ }
+ else if (!strcmp("combo", default_metadata_tags[i].mode))
+ {
+ gint data;
+
+ g_string_append (xmldata, "\t\t<tag-value>");
+
+ data = get_tag_ui_combo (args, default_metadata_tags[i].tag,
+ default_metadata_tags[i].mode);
+ g_string_append_printf (xmldata, "%d", data);
+
+ g_string_append (xmldata, "</tag-value>\n");
+ }
+ else if (!strcmp("list", default_metadata_tags[i].mode))
+ {
+ gchar *data;
+
+ g_string_append (xmldata, "\t\t<tag-list-value>\n");
+
+ data = get_tag_ui_list (args, default_metadata_tags[i].tag,
+ default_metadata_tags[i].mode);
+
+ if (data)
+ {
+ g_string_append (xmldata, data);
+ g_free(data);
+ }
+
+ g_string_append (xmldata, "\t\t</tag-list-value>\n");
+ }
+
+ g_string_append (xmldata, "\t</xmp-tag>\n");
+
+ }
+
+ g_string_append (xmldata, "</gimp-metadata>\n");
+
+
+ size = strlen (xmldata->str);
+ file = g_fopen (args->filename, "w");
+ if (file != NULL)
+ {
+ GError *error = NULL;
+
+ if (! g_file_set_contents (args->filename, xmldata->str, size, &error))
+ {
+ g_warning ("Error saving file: %s.", error? error->message: "");
+ g_clear_error (&error);
+ }
+ fclose (file);
+ }
+
+ if (xmldata)
+ {
+ g_string_free(xmldata, TRUE);
+ }
+}
+
diff --git a/plug-ins/metadata/metadata-impexp.h b/plug-ins/metadata/metadata-impexp.h
new file mode 100644
index 0000000..0229751
--- /dev/null
+++ b/plug-ins/metadata/metadata-impexp.h
@@ -0,0 +1,30 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __METADATA_IMPEXP_H__
+#define __METADATA_IMPEXP_H__
+
+void
+import_file_metadata (metadata_editor *args);
+
+void
+export_file_metadata (metadata_editor *args);
+
+#endif /* __METADATA_IMPEXP_H__ */
+
diff --git a/plug-ins/metadata/metadata-misc.h b/plug-ins/metadata/metadata-misc.h
new file mode 100644
index 0000000..4ba27d5
--- /dev/null
+++ b/plug-ins/metadata/metadata-misc.h
@@ -0,0 +1,70 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __METADATA_MISC_H__
+#define __METADATA_MISC_H__
+
+typedef struct
+{
+ GtkWidget *dialog;
+ GtkBuilder *builder;
+ GExiv2Metadata *metadata;
+ gint32 image_id;
+ gchar *filename;
+} metadata_editor;
+
+typedef struct
+{
+ gchar *tag;
+ gchar *mode;
+ gint32 other_tag_index;
+ gint32 tag_type;
+ gint32 xmp_type;
+} metadata_tag;
+
+typedef struct
+{
+ gchar *data;
+ gchar *display;
+} combobox_str_tag;
+
+typedef struct
+{
+ gint32 data;
+ gchar *display;
+} combobox_int_tag;
+
+typedef struct
+{
+ gchar *header;
+ gchar *type;
+ gint32 size;
+} TranslateHeaderTag;
+
+typedef struct
+{
+ gchar *id;
+ gchar *tag;
+ gchar *mode;
+ gint32 other_tag_index;
+ gint32 tag_type;
+} TranslateTag;
+
+#endif /* __METADATA_MISC_H__ */
+
diff --git a/plug-ins/metadata/metadata-tags.c b/plug-ins/metadata/metadata-tags.c
new file mode 100644
index 0000000..5371367
--- /dev/null
+++ b/plug-ins/metadata/metadata-tags.c
@@ -0,0 +1,515 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 <gexiv2/gexiv2.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-tags.h"
+
+
+/* The meaning of "single" and "multi" here denotes whether it is used in a
+ * single line or a multi line edit field.
+ * Depending on it's xmp type multi line can be saved as either:
+ * - one tag of type text, possibly including newlines
+ * - an array of tags of the same type for seq and bag, where each line in
+ * the multi line edit will be one item in the array
+ */
+const metadata_tag default_metadata_tags[] =
+{
+ /* Description */
+ { "Xmp.dc.title", "single", 16, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 0
+ { "Xmp.dc.creator", "single", 13, TAG_TYPE_XMP, GIMP_XMP_SEQ }, // 1
+ { "Xmp.dc.description", "multi", 14, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 2
+ { "Xmp.dc.subject", "multi", 15, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 3
+ { "Xmp.dc.rights", "single", 17, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 4
+ { "Xmp.photoshop.AuthorsPosition", "single", 19, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 5
+ { "Xmp.photoshop.CaptionWriter", "single", 21, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 6
+ { "Xmp.xmp.Rating", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 7
+ { "Xmp.xmpRights.Marked", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 8
+ { "Xmp.xmpRights.WebStatement", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 9
+
+ /* IPTC */
+ { "Xmp.photoshop.DateCreated", "single", 0, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 10
+ { "Xmp.photoshop.Headline", "multi", 3, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 11
+ { "Xmp.photoshop.TransmissionReference", "single", 1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 12
+ { "Xmp.photoshop.Instructions", "multi", 2, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 13
+ { "Xmp.iptc.IntellectualGenre", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 14
+ { "Xmp.iptc.Scene", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 15
+ { "Xmp.iptc.Location", "single", 18, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 16
+ { "Xmp.iptc.CountryCode", "single", 20, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 17
+ { "Xmp.iptc.SubjectCode", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 18
+ { "Xmp.xmpRights.UsageTerms", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 19
+ { "Xmp.photoshop.City", "single", 5, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 20
+ { "Xmp.photoshop.State", "single", 6, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 21
+ { "Xmp.photoshop.Country", "single", 7, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 22
+ /* Xmp.photoshop.CaptionWriter here is a duplicate of #6 above. We keep it here to not have
+ * to renumber the tag references. It seems it is not used on the IPTC tab. */
+ { "Xmp.photoshop.CaptionWriter", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 23
+ { "Xmp.photoshop.Credit", "single", 8, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 24
+ { "Xmp.photoshop.Source", "single", 9, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 25
+ { "Xmp.photoshop.Urgency", "combo", 11, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 26
+
+ /* IPTC Extension */
+ { "Xmp.iptcExt.PersonInImage", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 27
+ { "Xmp.iptcExt.Sublocation", "single", 12, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 28
+ { "Xmp.iptcExt.City", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 29
+ { "Xmp.iptcExt.ProvinceState", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 30
+ { "Xmp.iptcExt.CountryName", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 31
+ { "Xmp.iptcExt.CountryCode", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 32
+ { "Xmp.iptcExt.WorldRegion", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 33
+ { "Xmp.iptcExt.LocationShown", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 34
+ { "Xmp.iptcExt.OrganisationInImageName", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 35
+ { "Xmp.iptcExt.OrganisationInImageCode", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 36
+ { "Xmp.iptcExt.Event", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 37
+ { "Xmp.iptcExt.RegistryId", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 38
+ { "Xmp.iptcExt.ArtworkOrObject", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 39
+ { "Xmp.iptcExt.AddlModelInfo", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 40
+ { "Xmp.iptcExt.ModelAge", "single", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 41
+ { "Xmp.iptcExt.MaxAvailWidth", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 42
+ { "Xmp.iptcExt.MaxAvailHeight", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 43
+ { "Xmp.iptcExt.DigitalSourceType", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 44
+ { "Xmp.plus.MinorModelAgeDisclosure", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 45
+ { "Xmp.plus.ModelReleaseStatus", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 46
+ { "Xmp.plus.ModelReleaseID", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 47
+ { "Xmp.plus.ImageSupplierName", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 48
+ { "Xmp.plus.ImageSupplierID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 49
+ { "Xmp.plus.ImageSupplierImageID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 50
+ { "Xmp.plus.ImageCreator", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 51
+ { "Xmp.plus.CopyrightOwner", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 52
+ { "Xmp.plus.Licensor", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 53
+ { "Xmp.plus.PropertyReleaseStatus", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 54
+ { "Xmp.plus.PropertyReleaseID", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 55
+
+ /* Categories */
+ { "Xmp.photoshop.Category", "single", 4, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 56
+ { "Xmp.photoshop.SupplementalCategories", "multi", 10, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 57
+
+ /* GPS */
+ { "Exif.GPSInfo.GPSLongitude", "single", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 58
+ { "Exif.GPSInfo.GPSLongitudeRef", "combo", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 59
+ { "Exif.GPSInfo.GPSLatitude", "single", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 60
+ { "Exif.GPSInfo.GPSLatitudeRef", "combo", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 61
+ { "Exif.GPSInfo.GPSAltitude", "single", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 62
+ { "Exif.GPSInfo.GPSAltitudeRef", "combo", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 63
+
+ /* DICOM */
+ { "Xmp.DICOM.PatientName", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 64
+ { "Xmp.DICOM.PatientID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 65
+ { "Xmp.DICOM.PatientDOB", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 66
+ { "Xmp.DICOM.PatientSex", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 67
+ { "Xmp.DICOM.StudyID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 68
+ { "Xmp.DICOM.StudyPhysician", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 69
+ { "Xmp.DICOM.StudyDateTime", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 70
+ { "Xmp.DICOM.StudyDescription", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 71
+ { "Xmp.DICOM.SeriesNumber", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 72
+ { "Xmp.DICOM.SeriesModality", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 73
+ { "Xmp.DICOM.SeriesDateTime", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 74
+ { "Xmp.DICOM.SeriesDescription", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 75
+ { "Xmp.DICOM.EquipmentInstitution", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 76
+ { "Xmp.DICOM.EquipmentManufacturer", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 77
+
+ /* IPTC */
+ { "Xmp.iptc.CiAdrExtadr", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 78
+ { "Xmp.iptc.CiAdrCity", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 79
+ { "Xmp.iptc.CiAdrRegion", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 80
+ { "Xmp.iptc.CiAdrPcode", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 81
+ { "Xmp.iptc.CiAdrCtry", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 82
+ { "Xmp.iptc.CiTelWork", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 83
+ { "Xmp.iptc.CiEmailWork", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 84
+ { "Xmp.iptc.CiUrlWork", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT } // 85
+
+};
+const gint n_default_metadata_tags = G_N_ELEMENTS (default_metadata_tags);
+
+/* Then meaning of "single" and "multi" below is a little different than above.
+ * "single" - for iptc tags that can appear only once,
+ * "multi" - for iptc tags that are repeatable, i.e. can appear multiple times.
+ */
+const metadata_tag equivalent_metadata_tags[] =
+{
+ { "Iptc.Application2.DateCreated", "single", 10, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 0
+ { "Iptc.Application2.TransmissionReference", "single", 12, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 1
+ { "Iptc.Application2.SpecialInstructions", "single", 13, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 2
+ { "Iptc.Application2.Headline", "single", 11, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 3
+ { "Iptc.Application2.Category", "single", 56, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 4
+ { "Iptc.Application2.City", "single", 20, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 5
+ { "Iptc.Application2.ProvinceState", "single", 21, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 6
+ { "Iptc.Application2.CountryName", "single", 22, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 7
+ { "Iptc.Application2.Credit", "single", 24, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 8
+ { "Iptc.Application2.Source", "single", 25, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 9
+ { "Iptc.Application2.SuppCategory", "multi", 57, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 10
+ { "Iptc.Application2.Urgency", "combo", 26, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 11
+ { "Iptc.Application2.SubLocation", "single", 28, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 12
+ { "Iptc.Application2.Byline", "single", 1, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 13
+ { "Iptc.Application2.Caption", "single", 2, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 14
+ { "Iptc.Application2.Keywords", "multi", 3, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 15
+ { "Iptc.Application2.ObjectName", "single", 0, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 16
+ { "Iptc.Application2.Copyright", "single", 4, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 17
+ { "Iptc.Application2.LocationName", "multi", 16, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 18
+ { "Iptc.Application2.BylineTitle", "multi", 5, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 19
+ { "Iptc.Application2.CountryCode", "single", 17, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 20
+ { "Iptc.Application2.Writer", "multi", 6, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 21
+};
+const gint n_equivalent_metadata_tags = G_N_ELEMENTS (equivalent_metadata_tags);
+
+/* Digital Source Type Combobox Items
+ * http://cv.iptc.org/newscodes/digitalsourcetype/
+ */
+const combobox_str_tag digitalsourcetype[] =
+{
+ { "", N_("Select a value") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture", N_("Original digital capture of a real life scene") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/negativeFilm", N_("Digitized from a negative on film") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/positiveFilm", N_("Digitized from a positive on film") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/print", N_("Digitized from a print on non-transparent medium") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/minorHumanEdits", N_("Original media with minor human edits") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/compositeCapture", N_("Composite of captured elements") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced", N_("Algorithmically-enhanced media") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/dataDrivenMedia", N_("Data-driven media") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/digitalArt", N_("Digital art") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/virtualRecording", N_("Virtual recording") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/compositeSynthetic", N_("Composite including synthetic elements") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia", N_("Trained algorithmic media") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia", N_("Pure algorithmic media") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/softwareImage", N_("Created by software") }
+};
+const gint n_digitalsourcetype = G_N_ELEMENTS (digitalsourcetype);
+
+/* Model Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#ModelReleaseStatus
+ */
+const combobox_str_tag modelreleasestatus[] =
+{
+ { "", N_("Select a value") },
+ { "http://ns.useplus.org/ldf/vocab/MR-NON", N_("None") },
+ { "http://ns.useplus.org/ldf/vocab/MR-NAP", N_("Not Applicable") },
+ { "http://ns.useplus.org/ldf/vocab/MR-UMR", N_("Unlimited Model Releases") },
+ { "http://ns.useplus.org/ldf/vocab/MR-LMR", N_("Limited or Incomplete Model Releases") }
+};
+const gint n_modelreleasestatus = G_N_ELEMENTS (modelreleasestatus);
+
+/* Property Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#PropertyReleaseStatus
+ */
+const combobox_str_tag propertyreleasestatus[] =
+{
+ { "http://ns.useplus.org/ldf/vocab/PR-NON", N_("None") },
+ { "http://ns.useplus.org/ldf/vocab/PR-NAP", N_("Not Applicable") },
+ { "http://ns.useplus.org/ldf/vocab/PR-UPR", N_("Unlimited Property Releases") },
+ { "http://ns.useplus.org/ldf/vocab/PR-LPR", N_("Limited or Incomplete Property Releases") }
+};
+const gint n_propertyreleasestatus = G_N_ELEMENTS (propertyreleasestatus);
+
+/* Minor Model Age Disclosure Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#MinorModelAgeDisclosure
+ */
+const combobox_str_tag minormodelagedisclosure[] =
+{
+ { "http://ns.useplus.org/ldf/vocab/AG-UNK", N_("Age Unknown") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A25", N_("Age 25 or Over") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A24", N_("Age 24") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A23", N_("Age 23") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A22", N_("Age 22") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A21", N_("Age 21") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A20", N_("Age 20") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A19", N_("Age 19") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A18", N_("Age 18") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A17", N_("Age 17") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A16", N_("Age 16") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A15", N_("Age 15") },
+ { "http://ns.useplus.org/ldf/vocab/AG-U14", N_("Age 14 or Under") }
+};
+const gint n_minormodelagedisclosure = G_N_ELEMENTS (minormodelagedisclosure);
+
+/* Urgency */
+const gchar *urgency[] =
+{
+ N_("None"), N_("High"), N_("2"), N_("3"), N_("4"), N_("Normal"), N_("6"), N_("7"), N_("Low")
+};
+const gint n_urgency = G_N_ELEMENTS (urgency);
+
+/* Marked */
+const combobox_int_tag marked[] =
+{
+ { -1, N_("Unknown") }, // DO NOT SAVE
+ { TRUE, N_("Copyrighted") }, // TRUE
+ { FALSE, N_("Public Domain") }, // FALSE
+};
+const gint n_marked = G_N_ELEMENTS (marked);
+
+/* Phone Types */
+const combobox_str_tag phone_types[] =
+{
+ { "", N_("Select a value") },
+ { "http://ns.useplus.org/ldf/vocab/work", N_("Work") },
+ { "http://ns.useplus.org/ldf/vocab/cell", N_("Cell") },
+ { "http://ns.useplus.org/ldf/vocab/fax", N_("Fax") },
+ { "http://ns.useplus.org/ldf/vocab/home", N_("Home") },
+ { "http://ns.useplus.org/ldf/vocab/pager", N_("Pager") }
+};
+const gint n_phone_types = G_N_ELEMENTS (phone_types);
+
+/* DICOM Patient Sex
+ * http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.7-1
+ * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/XMP.html#DICOM
+ * https://dicomiseasy.blogspot.ca/2011/11/introduction-to-dicom-chapter-iii-dicom.html
+ * http://dicom.nema.org/standard.html
+ */
+const combobox_str_tag dicom[] =
+{
+ { "", N_("Select a value") },
+ { "male", N_("Male") },
+ { "female", N_("Female") },
+ { "other", N_("Other") },
+};
+const gint n_dicom = G_N_ELEMENTS (dicom);
+
+/* GPS Altitude Ref */
+const gchar *gpsaltref[] =
+{
+ N_("Unknown"), N_("Above sea level"), N_("Below sea level")
+};
+const gint n_gpsaltref = G_N_ELEMENTS (gpsaltref);
+
+/* GPS Latitude Ref */
+const gchar *gpslatref[] =
+{
+ N_("Unknown"), N_("North"), N_("South")
+};
+const gint n_gpslatref = G_N_ELEMENTS (gpslatref);
+
+/* GPS Longitude Ref */
+const gchar *gpslngref[] =
+{
+ N_("Unknown"), N_("East"), N_("West")
+};
+const gint n_gpslngref = G_N_ELEMENTS (gpslngref);
+
+/* GPS Measurement System */
+const gchar *gpsaltsys[] =
+{
+ "m", "ft"
+};
+const gint n_gpsaltsys = G_N_ELEMENTS (gpsaltsys);
+
+const TranslateHeaderTag creatorContactInfoHeader =
+{
+ "Xmp.iptc.CreatorContactInfo", "type=\"Struct\"", 8
+};
+
+const TranslateTag creatorContactInfoTags[] =
+{
+ { "Xmp.iptc.CiAdrExtadr", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrExtadr", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrCity", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCity", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrRegion", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrRegion", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrPcode", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrPcode", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrCtry", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCtry", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiTelWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiTelWork", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiEmailWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiEmailWork", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiUrlWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiUrlWork", "multi", -1, TAG_TYPE_XMP }
+};
+
+const TranslateHeaderTag locationCreationInfoHeader =
+{
+ "Xmp.iptcExt.LocationCreated", "type=\"Bag\"", 6
+};
+
+const TranslateTag locationCreationInfoTags[] =
+{
+ { "Xmp.iptcExt.Sublocation", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:Sublocation", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.City", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:City", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.ProvinceState", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:ProvinceState", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.CountryName", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:CountryName", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.CountryCode", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:CountryCode", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.WorldRegion", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:WorldRegion", "single", -1, TAG_TYPE_XMP }
+};
+
+const TranslateHeaderTag imageSupplierInfoHeader =
+{
+ "Xmp.plus.ImageSupplier", "type=\"Seq\"", 2
+};
+
+const TranslateTag imageSupplierInfoTags[] =
+{
+ { "Xmp.plus.ImageSupplierName", "Xmp.plus.ImageSupplier[1]/plus:ImageSupplierName", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.plus.ImageSupplierID", "Xmp.plus.ImageSupplier[1]/plus:ImageSupplierID", "single", -1, TAG_TYPE_XMP }
+};
+
+/* Plus and IPTC extension tags */
+
+const gchar *licensor[] =
+{
+ "/plus:LicensorName",
+ "/plus:LicensorID",
+ "/plus:LicensorTelephone1",
+ "/plus:LicensorTelephoneType1",
+ "/plus:LicensorTelephone2",
+ "/plus:LicensorTelephoneType2",
+ "/plus:LicensorEmail",
+ "/plus:LicensorURL"
+};
+const gint n_licensor = G_N_ELEMENTS (licensor);
+
+const gint licensor_special_handling[] =
+{
+ METADATA_NONE,
+ METADATA_NONE,
+ METADATA_NONE,
+ METADATA_PHONETYPE,
+ METADATA_NONE,
+ METADATA_PHONETYPE,
+ METADATA_NONE,
+ METADATA_NONE
+};
+
+#ifdef USE_TAGS
+const gchar *imagesupplier[] =
+{
+ "/plus:ImageSupplierName",
+ "/plus:ImageSupplierID"
+};
+const gint n_imagesupplier = G_N_ELEMENTS (imagesupplier);
+#endif
+
+const gchar *imagecreator[] =
+{
+ "/plus:ImageCreatorName",
+ "/plus:ImageCreatorID"
+};
+const gint n_imagecreator = G_N_ELEMENTS (imagecreator);
+
+const gchar *copyrightowner[] =
+{
+ "/plus:CopyrightOwnerName",
+ "/plus:CopyrightOwnerID"
+};
+const gint n_copyrightowner = G_N_ELEMENTS (copyrightowner);
+
+const gchar *registryid[] =
+{
+ "/Iptc4xmpExt:RegOrgId",
+ "/Iptc4xmpExt:RegItemId"
+};
+const gint n_registryid = G_N_ELEMENTS (registryid);
+
+const gchar *registryid_alternative[] =
+{
+ "/iptcExt:RegOrgId",
+ "/iptcExt:RegItemId"
+};
+
+const gchar *artworkorobject[] =
+{
+ "/Iptc4xmpExt:AOTitle",
+ "/Iptc4xmpExt:AODateCreated",
+ "/Iptc4xmpExt:AOCreator",
+ "/Iptc4xmpExt:AOSource",
+ "/Iptc4xmpExt:AOSourceInvNo",
+ "/Iptc4xmpExt:AOCopyrightNotice",
+};
+const gint n_artworkorobject = G_N_ELEMENTS (artworkorobject);
+
+const gchar *artworkorobject_alternative[] =
+{
+ "/iptcExt:AOTitle",
+ "/iptcExt:AODateCreated",
+ "/iptcExt:AOCreator",
+ "/iptcExt:AOSource",
+ "/iptcExt:AOSourceInvNo",
+ "/iptcExt:AOCopyrightNotice",
+};
+
+const gchar *locationshown[] =
+{
+ "/Iptc4xmpExt:Sublocation",
+ "/Iptc4xmpExt:City",
+ "/Iptc4xmpExt:ProvinceState",
+ "/Iptc4xmpExt:CountryName",
+ "/Iptc4xmpExt:CountryCode",
+ "/Iptc4xmpExt:WorldRegion"
+};
+const gint n_locationshown = G_N_ELEMENTS (locationshown);
+
+const gchar *locationshown_alternative[] =
+{
+ "/iptcExt:Sublocation",
+ "/iptcExt:City",
+ "/iptcExt:ProvinceState",
+ "/iptcExt:CountryName",
+ "/iptcExt:CountryCode",
+ "/iptcExt:WorldRegion"
+};
+
+
+#ifdef USE_TAGS
+const gchar *locationcreated[] =
+{
+ "/Iptc4xmpExt:Sublocation",
+ "/Iptc4xmpExt:City",
+ "/Iptc4xmpExt:ProvinceState",
+ "/Iptc4xmpExt:CountryName",
+ "/Iptc4xmpExt:CountryCode",
+ "/Iptc4xmpExt:WorldRegion"
+};
+const gint n_locationcreated = G_N_ELEMENTS (locationcreated);
+#endif
+
+
+gchar *
+metadata_format_gps_longitude_latitude (const gdouble value)
+{
+ gint deg, min;
+ gdouble sec;
+ gdouble gps_value = value;
+
+ if (gps_value < 0.f)
+ gps_value *= -1.f;
+
+ deg = (gint) gps_value;
+ min = (gint) ((gps_value - (gdouble) deg) * 60.f);
+ sec = ((gps_value - (gdouble) deg - (gdouble) (min / 60.f)) * 60.f * 60.f);
+
+ return g_strdup_printf ("%ddeg %d' %.3f\"", deg, min, sec);
+}
+
+/*
+ * use_meter: True return meters, False return feet
+ * measurement_symbol: Should be "m", "ft", or empty string (not NULL)
+ */
+gchar *
+metadata_format_gps_altitude (const gdouble value,
+ gboolean use_meter,
+ gchar *measurement_symbol)
+{
+ gdouble gps_value = value;
+
+ if (gps_value < 0.f)
+ gps_value *= -1.f;
+
+ if (! use_meter)
+ {
+ gps_value *= 3.28;
+ }
+
+ return g_strdup_printf ("%.2f%s", gps_value, measurement_symbol);
+}
diff --git a/plug-ins/metadata/metadata-tags.h b/plug-ins/metadata/metadata-tags.h
new file mode 100644
index 0000000..81209d5
--- /dev/null
+++ b/plug-ins/metadata/metadata-tags.h
@@ -0,0 +1,253 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __METADATA_TAGS_H__
+#define __METADATA_TAGS_H__
+
+#include "metadata-misc.h"
+
+#define TAG_TYPE_XMP 1
+#define TAG_TYPE_EXIF 2
+#define TAG_TYPE_IPTC 3
+
+enum
+{
+ GIMP_XMP_NONE = 0,
+ GIMP_XMP_TEXT,
+ GIMP_XMP_BAG,
+ GIMP_XMP_SEQ,
+ GIMP_XMP_LANG,
+ GIMP_XMP_ALT
+};
+
+enum
+{
+ COL_LICENSOR_NAME = 0,
+ COL_LICENSOR_ID,
+ COL_LICENSOR_PHONE1,
+ COL_LICENSOR_PHONE_TYPE1,
+ COL_LICENSOR_PHONE2,
+ COL_LICENSOR_PHONE_TYPE2,
+ COL_LICENSOR_EMAIL,
+ COL_LICENSOR_WEB,
+ COL_LICENSOR_NUM_COLS
+};
+
+enum
+{
+ COL_CR_OWNER_NAME = 0,
+ COL_CR_OWNER_ID,
+ COL_CR_OWNER_NUM_COLS
+};
+
+enum
+{
+ COL_IMG_CR8_NAME = 0,
+ COL_IMG_CR8_ID,
+ COL_IMG_CR8_NUM_COLS
+};
+
+enum
+{
+ COL_AOO_TITLE = 0,
+ COL_AOO_DATE_CREAT,
+ COL_AOO_CREATOR,
+ COL_AOO_SOURCE,
+ COL_AOO_SRC_INV_ID,
+ COL_AOO_CR_NOT,
+ COL_AOO_NUM_COLS
+};
+
+enum
+{
+ COL_REGISTRY_ORG_ID = 0,
+ COL_REGISTRY_ITEM_ID,
+ COL_REGISTRY_NUM_COLS
+};
+
+enum
+{
+ COL_LOC_SHO_SUB_LOC = 0,
+ COL_LOC_SHO_CITY,
+ COL_LOC_SHO_STATE_PROV,
+ COL_LOC_SHO_CNTRY,
+ COL_LOC_SHO_CNTRY_ISO,
+ COL_LOC_SHO_CNTRY_WRLD_REG,
+ COL_LOC_SHO_NUM_COLS
+};
+
+enum
+{
+ COL_ORG_IMG_CODE = 0,
+ ORG_IMG_CODE_REL_NUM_COLS
+};
+
+enum
+{
+ COL_ORG_IMG_NAME = 0,
+ ORG_IMG_NAME_REL_NUM_COLS
+};
+
+enum
+{
+ COL_MOD_REL_ID = 0,
+ MOD_REL_NUM_COLS
+};
+
+enum
+{
+ COL_PROP_REL_ID = 0,
+ PROP_REL_NUM_COLS
+};
+
+enum METADATA_SPECIAL_PROCESSING
+{
+ METADATA_NONE = 0,
+ METADATA_PHONETYPE,
+ METADATA_PREPROCESS_TEXT
+};
+
+extern const metadata_tag default_metadata_tags[];
+extern const gint n_default_metadata_tags;
+
+extern const metadata_tag equivalent_metadata_tags[];
+extern const gint n_equivalent_metadata_tags;
+
+/* Digital Source Type Combobox Items
+ * http://cv.iptc.org/newscodes/digitalsourcetype/
+ */
+extern const combobox_str_tag digitalsourcetype[];
+extern const gint n_digitalsourcetype;
+
+/* Model Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#ModelReleaseStatus
+ */
+extern const combobox_str_tag modelreleasestatus[];
+extern const gint n_modelreleasestatus;
+
+/* Property Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#PropertyReleaseStatus
+ */
+extern const combobox_str_tag propertyreleasestatus[];
+extern const gint n_propertyreleasestatus;
+
+/* Minor Model Age Disclosure Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#MinorModelAgeDisclosure
+ */
+extern const combobox_str_tag minormodelagedisclosure[];
+extern const gint n_minormodelagedisclosure;
+
+/* Urgency */
+extern const gchar *urgency[];
+extern const gint n_urgency;
+
+/* Marked */
+extern const combobox_int_tag marked[];
+extern const gint n_marked;
+
+/* Phone Types */
+extern const combobox_str_tag phone_types[];
+extern const gint n_phone_types;
+
+/* DICOM Patient Sex
+ * http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.7-1
+ * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/XMP.html#DICOM
+ * https://dicomiseasy.blogspot.ca/2011/11/introduction-to-dicom-chapter-iii-dicom.html
+ * http://dicom.nema.org/standard.html
+ */
+extern const combobox_str_tag dicom[];
+extern const gint n_dicom;
+
+/* GPS Altitude Ref */
+extern const gchar *gpsaltref[];
+extern const gint n_gpsaltref;
+
+/* GPS Latitude Ref */
+extern const gchar *gpslatref[];
+extern const gint n_gpslatref;
+
+/* GPS Longitude Ref */
+extern const gchar *gpslngref[];
+extern const gint n_gpslngref;
+
+/* GPS Measurement System */
+extern const gchar *gpsaltsys[];
+extern const gint n_gpsaltsys;
+
+extern const TranslateHeaderTag creatorContactInfoHeader;
+
+extern const TranslateTag creatorContactInfoTags[];
+
+extern const TranslateHeaderTag locationCreationInfoHeader;
+
+extern const TranslateTag locationCreationInfoTags[];
+
+extern const TranslateHeaderTag imageSupplierInfoHeader;
+
+extern const TranslateTag imageSupplierInfoTags[];
+
+/* Plus and IPTC extension tags */
+
+#define LICENSOR_HEADER "Xmp.plus.Licensor"
+extern const gchar *licensor[];
+extern const gint n_licensor;
+extern const gint licensor_special_handling[];
+
+#ifdef USE_TAGS
+#define IMAGESUPPLIER_HEADER "Xmp.plus.ImageSupplier"
+extern const gchar *imagesupplier[];
+extern const gint n_imagesupplier;
+#endif
+
+#define IMAGECREATOR_HEADER "Xmp.plus.ImageCreator"
+extern const gchar *imagecreator[];
+extern const gint n_imagecreator;
+
+#define COPYRIGHTOWNER_HEADER "Xmp.plus.CopyrightOwner"
+extern const gchar *copyrightowner[];
+extern const gint n_copyrightowner;
+
+#define REGISTRYID_HEADER "Xmp.iptcExt.RegistryId"
+extern const gchar *registryid[];
+extern const gchar *registryid_alternative[];
+extern const gint n_registryid;
+
+#define ARTWORKOROBJECT_HEADER "Xmp.iptcExt.ArtworkOrObject"
+extern const gchar *artworkorobject[];
+extern const gchar *artworkorobject_alternative[];
+extern const gint n_artworkorobject;
+
+#define LOCATIONSHOWN_HEADER "Xmp.iptcExt.LocationShown"
+extern const gchar *locationshown[];
+extern const gchar *locationshown_alternative[];
+extern const gint n_locationshown;
+
+#ifdef USE_TAGS
+#define LOCATIONCREATED_HEADER "Xmp.iptcExt.LocationCreated"
+extern const gchar *locationcreated[];
+extern const gint n_locationcreated;
+#endif
+
+
+gchar * metadata_format_gps_longitude_latitude (const gdouble value);
+gchar * metadata_format_gps_altitude (const gdouble value,
+ gboolean use_meter,
+ gchar *measurement_symbol);
+
+#endif /* __METADATA_TAGS_H__ */
diff --git a/plug-ins/metadata/metadata-viewer.c b/plug-ins/metadata/metadata-viewer.c
new file mode 100644
index 0000000..59719aa
--- /dev/null
+++ b/plug-ins/metadata/metadata-viewer.c
@@ -0,0 +1,683 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * metadata.c
+ * Copyright (C) 2013 Hartmut Kuhse
+ * Copyright (C) 2016 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 <gegl.h>
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-tags.h"
+
+#define PLUG_IN_PROC "plug-in-metadata-viewer"
+#define PLUG_IN_BINARY "metadata-viewer"
+#define PLUG_IN_ROLE "gimp-metadata"
+
+#define EXIF_PREFIX "Exif."
+#define IPTC_PREFIX "Iptc."
+#define XMP_PREFIX "Xmp."
+
+/* The length at which to truncate tag values, in characters. */
+#define TAG_VALUE_MAX_SIZE 1024
+
+/* The length at which to truncate raw data (i.e., tag values
+ * of type "Byte" or "Undefined"), in bytes.
+ */
+#define RAW_DATA_MAX_SIZE 16
+
+
+enum
+{
+ C_XMP_TAG = 0,
+ C_XMP_VALUE,
+ NUM_XMP_COLS
+};
+
+enum
+{
+ C_EXIF_TAG = 0,
+ C_EXIF_VALUE,
+ NUM_EXIF_COLS
+};
+
+enum
+{
+ C_IPTC_TAG = 0,
+ C_IPTC_VALUE,
+ NUM_IPTC_COLS
+};
+
+
+/* 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_viewer_dialog (gint32 image_id,
+ GimpMetadata *g_metadata,
+ GError **error);
+static void metadata_dialog_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder);
+static void metadata_dialog_add_multiple_values (GExiv2Metadata *metadata,
+ const gchar *tag,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column);
+static void metadata_dialog_append_tags (GExiv2Metadata *metadata,
+ gchar **tags,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column,
+ gboolean load_iptc);
+static void metadata_dialog_add_tag (GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag,
+ const gchar *value);
+static void metadata_dialog_add_translated_tag (GExiv2Metadata *metadata,
+ GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag);
+static gchar * metadata_dialog_format_tag_value (GExiv2Metadata *metadata,
+ const gchar *tag,
+ gboolean truncate);
+
+
+/* local variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* functions */
+
+MAIN ()
+
+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_("View metadata (Exif, IPTC, XMP)"),
+ "View metadata information attached to the "
+ "current image. This can include Exif, IPTC and/or "
+ "XMP information.",
+ "Hartmut Kuhse, Michael Natterer, Ben Touchette",
+ "Hartmut Kuhse, Michael Natterer, Ben Touchette",
+ "2013, 2017",
+ N_("_View Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (metadata_args), 0,
+ metadata_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Metadata");
+}
+
+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;
+
+ 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_viewer_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;
+}
+
+static gboolean
+metadata_viewer_dialog (gint32 image_id,
+ GimpMetadata *g_metadata,
+ GError **error)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *metadata_vbox;
+ GtkWidget *content_area;
+ gchar *ui_file;
+ gchar *title;
+ gchar *name;
+ GError *local_error = NULL;
+ GExiv2Metadata *metadata;
+
+ metadata = GEXIV2_METADATA(g_metadata);
+
+ builder = gtk_builder_new ();
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-metadata-viewer.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-viewer 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 Viewer: %s"), name);
+ g_free (name);
+
+ dialog = gimp_dialog_new (title,
+ "gimp-metadata-viewer-dialog",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+ NULL);
+
+ gtk_widget_set_size_request(dialog, 650, 500);
+
+ g_free (title);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ metadata_vbox = GTK_WIDGET (gtk_builder_get_object (builder,
+ "metadata-vbox"));
+ 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_set_metadata (metadata, builder);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ return TRUE;
+}
+
+
+/* private functions */
+
+static void
+metadata_dialog_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder)
+{
+ gchar **tags;
+ GtkListStore *store;
+
+ /* load exif tags */
+ tags = gexiv2_metadata_get_exif_tags (metadata);
+ store = GTK_LIST_STORE (gtk_builder_get_object (builder, "exif-liststore"));
+
+ metadata_dialog_append_tags (metadata, tags, store, C_EXIF_TAG, C_EXIF_VALUE, FALSE);
+
+ g_strfreev (tags);
+
+ /* load xmp tags */
+ tags = gexiv2_metadata_get_xmp_tags (metadata);
+ store = GTK_LIST_STORE (gtk_builder_get_object (builder, "xmp-liststore"));
+
+ metadata_dialog_append_tags (metadata, tags, store, C_XMP_TAG, C_XMP_VALUE, FALSE);
+
+ g_strfreev (tags);
+
+ /* load iptc tags */
+ tags = gexiv2_metadata_get_iptc_tags (metadata);
+ store = GTK_LIST_STORE (gtk_builder_get_object (builder, "iptc-liststore"));
+
+ metadata_dialog_append_tags (metadata, tags, store, C_IPTC_TAG, C_IPTC_VALUE, TRUE);
+
+ g_strfreev (tags);
+}
+
+static gchar *
+metadata_format_string_value (const gchar *value,
+ gboolean truncate)
+{
+ glong size;
+ gchar *result;
+
+ size = g_utf8_strlen (value, -1);
+
+ if (! truncate || size <= TAG_VALUE_MAX_SIZE)
+ {
+ result = g_strdup(value);
+ }
+ else
+ {
+ gchar *value_utf8_trunc;
+ GString *str;
+
+ value_utf8_trunc = g_utf8_substring (value, 0, TAG_VALUE_MAX_SIZE);
+ str = g_string_new (value_utf8_trunc);
+
+ g_free (value_utf8_trunc);
+
+ g_string_append (str, "... ");
+ g_string_append_printf (str,
+ _("(%lu more character(s))"),
+ size - TAG_VALUE_MAX_SIZE);
+
+ result = g_string_free (str, FALSE);
+ }
+ return result;
+}
+
+static inline gboolean
+metadata_tag_is_string (const gchar *tag)
+{
+ const gchar *tag_type;
+
+ tag_type = gexiv2_metadata_get_tag_type (tag);
+
+ return (g_strcmp0 (tag_type, "Byte") != 0 &&
+ g_strcmp0 (tag_type, "Undefined") != 0 &&
+ g_strcmp0 (tag_type, NULL) != 0);
+}
+
+static void
+metadata_dialog_add_tag (GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag,
+ const gchar *value)
+{
+ if (value)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ tag_column, tag,
+ value_column, value,
+ -1);
+ }
+}
+
+static void
+metadata_dialog_add_translated_tag (GExiv2Metadata *metadata,
+ GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag)
+{
+ gchar *value;
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata, tag);
+ metadata_dialog_add_tag (store, iter, tag_column, value_column,
+ tag, gettext (value));
+ g_free (value);
+}
+
+static gchar *
+metadata_interpret_user_comment (gchar *comment)
+{
+ /* Exiv2 can return unwanted text at the start of a comment
+ * taken from Exif.Photo.UserComment since 0.27.3.
+ * Let's remove that part and replace with an empty string
+ * if there is nothing else left as comment. */
+
+ if (comment && g_str_has_prefix (comment, "charset=Ascii "))
+ {
+ gchar *real_comment;
+
+ /* Skip "charset=Ascii " (length 14) to find the real comment */
+ real_comment = comment + 14;
+ if (real_comment[0] == '\0' ||
+ ! g_strcmp0 (real_comment, "binary comment"))
+ {
+ g_free (comment);
+ /* Make empty comment instead of returning NULL or else
+ * the exif value will not be shown at all. */
+ comment = g_strdup ("");
+ }
+ else
+ {
+ real_comment = g_strdup (real_comment);
+ g_free (comment);
+ return real_comment;
+ }
+ }
+
+ return comment;
+}
+
+static void
+metadata_dialog_add_multiple_values (GExiv2Metadata *metadata,
+ const gchar *tag,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column)
+{
+ gchar **values;
+
+ values = gexiv2_metadata_get_tag_multiple (GEXIV2_METADATA (metadata), tag);
+
+ if (values)
+ {
+ gint i;
+
+ for (i = 0; values[i] != NULL; i++)
+ {
+ gchar *value;
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+
+ value = metadata_format_string_value (values[i], /* truncate = */ TRUE);
+
+ gtk_list_store_set (store, &iter,
+ tag_column, tag,
+ value_column, value,
+ -1);
+
+ g_free (value);
+ }
+
+ g_strfreev (values);
+ }
+}
+
+static void
+metadata_dialog_append_tags (GExiv2Metadata *metadata,
+ gchar **tags,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column,
+ gboolean load_iptc)
+{
+ GtkTreeIter iter;
+ const gchar *tag;
+ const gchar *last_tag = NULL;
+ gboolean gps_done = FALSE;
+
+ while ((tag = *tags++))
+ {
+ gchar *value;
+ gchar **values;
+
+ /* We need special handling for iptc tags like "Keywords" which
+ * can appear multiple times. For now assuming that this can
+ * only happen for iptc tags of String and related types.
+ * See also: https://exiv2.org/iptc.html which only lists
+ * one Date type as repeatable (Iptc.Application2.ReferenceDate),
+ * and Date is handled here as string.
+ */
+ if (load_iptc && metadata_tag_is_string (tag))
+ {
+ if (last_tag && ! strcmp (tag, last_tag))
+ {
+ continue;
+ }
+ last_tag = tag;
+
+ metadata_dialog_add_multiple_values (GEXIV2_METADATA (metadata),
+ tag, store,
+ tag_column,
+ value_column);
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSLongitude", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLongitudeRef", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLatitude", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLatitudeRef", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSAltitude", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSAltitudeRef", tag))
+ {
+ /* We need special handling for some of the GPS tags to
+ * be able to show better values than the default. */
+ if (! gps_done)
+ {
+ gdouble lng, lat, alt;
+ gchar *str;
+ gchar *value;
+
+ if (gexiv2_metadata_get_gps_longitude (GEXIV2_METADATA (metadata),
+ &lng))
+ {
+ str = metadata_format_gps_longitude_latitude (lng);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLongitude",
+ str);
+ g_free (str);
+ }
+ metadata_dialog_add_translated_tag (metadata, store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLongitudeRef");
+
+ if (gexiv2_metadata_get_gps_latitude (GEXIV2_METADATA (metadata),
+ &lat))
+ {
+ str = metadata_format_gps_longitude_latitude (lat);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLatitude",
+ str);
+ g_free (str);
+ }
+ metadata_dialog_add_translated_tag (metadata, store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLatitudeRef");
+
+ if (gexiv2_metadata_get_gps_altitude (GEXIV2_METADATA (metadata),
+ &alt))
+ {
+ gchar *str2, *str3;
+
+ str = metadata_format_gps_altitude (alt, TRUE, _(" meter"));
+ str2 = metadata_format_gps_altitude (alt, FALSE, _(" feet"));
+ str3 = g_strdup_printf ("%s (%s)", str, str2);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSAltitude",
+ str3);
+ g_free (str);
+ g_free (str2);
+ g_free (str3);
+ value = gexiv2_metadata_get_tag_string (metadata,
+ "Exif.GPSInfo.GPSAltitudeRef");
+
+ if (value)
+ {
+ gint index;
+
+ if (value[0] == '0')
+ index = 1;
+ else if (value[0] == '1')
+ index = 2;
+ else
+ index = 0;
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSAltitudeRef",
+ gettext (gpsaltref[index]));
+ g_free (value);
+ }
+ }
+ gps_done = TRUE;
+ }
+ }
+ else if (! strcmp ("Exif.Photo.UserComment", tag))
+ {
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata, tag);
+ /* Can start with charset. Remove part that is not relevant. */
+ value = metadata_interpret_user_comment (value);
+
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ tag, value);
+ g_free (value);
+ }
+ else
+ {
+ if (g_str_has_prefix (tag, "Xmp.") &&
+ g_strcmp0 (gexiv2_metadata_get_tag_type (tag), "XmpText") != 0)
+ {
+ metadata_dialog_add_multiple_values (GEXIV2_METADATA (metadata),
+ tag, store,
+ tag_column,
+ value_column);
+ }
+ else
+ {
+ value = metadata_dialog_format_tag_value (metadata, tag,
+ /* truncate = */ TRUE);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ tag, value);
+ g_free (value);
+ }
+ }
+ }
+}
+
+static gchar *
+metadata_dialog_format_tag_value (GExiv2Metadata *metadata,
+ const gchar *tag,
+ gboolean truncate)
+{
+ gchar *result;
+
+ if (metadata_tag_is_string(tag))
+ {
+ gchar *value;
+ gchar *value_utf8;
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata, tag);
+ if (! g_utf8_validate (value, -1, NULL))
+ {
+ value_utf8 = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+ }
+ else
+ {
+ value_utf8 = g_strdup (value);
+ }
+
+ g_free (value);
+
+ result = metadata_format_string_value (value_utf8, truncate);
+ }
+ else
+ {
+ GBytes *bytes;
+ const guchar *data;
+ gsize size;
+ gsize display_size;
+ GString *str;
+ gint i;
+
+ bytes = gexiv2_metadata_get_tag_raw (metadata, tag);
+ data = g_bytes_get_data (bytes, &size);
+
+ if (! truncate)
+ display_size = size;
+ else
+ display_size = MIN (size, RAW_DATA_MAX_SIZE);
+
+ str = g_string_sized_new (3 * display_size);
+
+ for (i = 0; i < display_size; i++)
+ g_string_append_printf (str, i == 0 ? "%02x" : " %02x", data[i]);
+
+ if (display_size < size)
+ {
+ g_string_append (str, " ... ");
+ g_string_append_printf (str,
+ _("(%llu more byte(s))"),
+ (unsigned long long) (size - display_size));
+ }
+
+ result = g_string_free (str, FALSE);
+
+ g_bytes_unref (bytes);
+ }
+
+ return result;
+}
diff --git a/plug-ins/metadata/metadata-xml.c b/plug-ins/metadata/metadata-xml.c
new file mode 100644
index 0000000..62c70b4
--- /dev/null
+++ b/plug-ins/metadata/metadata-xml.c
@@ -0,0 +1,1147 @@
+/* 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 <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-misc.h"
+#include "metadata-xml.h"
+#include "metadata-tags.h"
+
+extern gboolean gimpmetadata;
+extern gboolean force_write;
+
+gboolean xmptag;
+gboolean iptctag;
+gboolean tagvalue;
+gboolean taglistvalue;
+gboolean tagname;
+gboolean tagmode;
+gboolean listelement;
+gboolean element;
+gchar *str_tag_value;
+gchar *str_tag_name;
+gchar *str_tag_mode;
+gchar *str_element;
+gchar *list_tag_data[256][256];
+gint row_count = 0;
+gint item_count = 0;
+
+
+static void get_list_elements (GString *xmldata,
+ int element_count,
+ gchar **rowtagdata);
+
+
+void
+xml_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ if (strcmp (element_name, "gimp-metadata") == 0)
+ {
+ gimpmetadata = TRUE;
+ }
+ else if (strcmp (element_name, "iptc-tag") == 0)
+ {
+ item_count = 0;
+ row_count = 0;
+ iptctag = TRUE;
+ }
+ else if (strcmp (element_name, "xmp-tag") == 0)
+ {
+ item_count = 0;
+ row_count = 0;
+ xmptag = TRUE;
+ }
+ else if (strcmp (element_name, "tag-value") == 0)
+ {
+ tagvalue = TRUE;
+ }
+ else if (strcmp (element_name, "tag-list-value") == 0)
+ {
+ taglistvalue = TRUE;
+ }
+ else if (strcmp (element_name, "tag-name") == 0)
+ {
+ tagname = TRUE;
+ }
+ else if (strcmp (element_name, "tag-mode") == 0)
+ {
+ tagmode = TRUE;
+ }
+ else if (strcmp (element_name, "list-element") == 0)
+ {
+ listelement = TRUE;
+ row_count += 1;
+ }
+ else if (strcmp (element_name, "element") == 0)
+ {
+ element = TRUE;
+ item_count += 1;
+ }
+}
+
+void
+xml_parser_data (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ if (tagvalue)
+ {
+ if (str_tag_value)
+ g_free(str_tag_value);
+
+ if (text)
+ str_tag_value = g_strdup(text);
+ else
+ str_tag_value = g_strconcat("", NULL);
+ }
+ else if (tagname)
+ {
+ if (str_tag_name)
+ g_free(str_tag_name);
+
+ if (text)
+ str_tag_name = g_strdup(text);
+ else
+ str_tag_name = g_strconcat("", NULL);
+ }
+ else if (tagmode)
+ {
+ if (str_tag_mode)
+ g_free(str_tag_mode);
+
+ if (text)
+ str_tag_mode = g_strdup(text);
+ else
+ str_tag_mode = g_strconcat("", NULL);
+ }
+ else if (element)
+ {
+ if (str_element)
+ g_free(str_element);
+
+ if (text)
+ str_element = g_strdup(text);
+ else
+ str_element = g_strconcat("", NULL);
+ }
+}
+
+void
+set_tag_ui (metadata_editor *args,
+ gint index,
+ gchar *name,
+ gchar *value,
+ gchar* mode)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (args->builder, str_tag_name));
+
+ if (!strcmp ("single", mode))
+ {
+ GtkEntry *entry_widget;
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
+ entry_widget = GTK_ENTRY (widget);
+ gtk_entry_set_text (entry_widget, value_utf);
+ g_free (value_utf);
+ }
+ else if (!strcmp ("multi", mode))
+ {
+ GtkTextView *text_view;
+ GtkTextBuffer *buffer;
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
+ text_view = GTK_TEXT_VIEW (widget);
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_set_text (buffer, value_utf, -1);
+ g_free (value_utf);
+ }
+ else if (!strcmp ("combo", mode))
+ {
+ gint32 value;
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
+ value = atoi(value_utf);
+ gtk_combo_box_set_active (GTK_COMBO_BOX(widget), value);
+ g_free (value_utf);
+ }
+ else if (!strcmp ("list", mode))
+ {
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint number_of_rows;
+ gint row;
+ gint item;
+
+ liststore = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView *)widget));
+ treemodel = GTK_TREE_MODEL (liststore);
+ number_of_rows =
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);
+
+ /* Clear all current values */
+ for (row = number_of_rows; row > -1; row--)
+ {
+ if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row))
+ {
+ gtk_list_store_remove(liststore, &iter);
+ }
+ }
+ /* Add new values values */
+ if (!strcmp (LICENSOR_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LICENSOR_NAME, list_tag_data[row][1],
+ COL_LICENSOR_ID, list_tag_data[row][2],
+ COL_LICENSOR_PHONE1, list_tag_data[row][3],
+ COL_LICENSOR_PHONE_TYPE1, list_tag_data[row][4],
+ COL_LICENSOR_PHONE2, list_tag_data[row][5],
+ COL_LICENSOR_PHONE_TYPE2, list_tag_data[row][6],
+ COL_LICENSOR_EMAIL, list_tag_data[row][7],
+ COL_LICENSOR_WEB, list_tag_data[row][8],
+ -1);
+ for (item = 1; item < n_licensor + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ 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, NULL,
+ COL_LICENSOR_PHONE2, NULL,
+ COL_LICENSOR_PHONE_TYPE2, NULL,
+ COL_LICENSOR_EMAIL, NULL,
+ COL_LICENSOR_WEB, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (IMAGECREATOR_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_IMG_CR8_NAME, list_tag_data[row][1],
+ COL_IMG_CR8_ID, list_tag_data[row][2],
+ -1);
+ for (item = 1; item < n_imagecreator + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_IMG_CR8_NAME, NULL,
+ COL_IMG_CR8_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (ARTWORKOROBJECT_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_AOO_TITLE, list_tag_data[row][1],
+ COL_AOO_DATE_CREAT, list_tag_data[row][2],
+ COL_AOO_CREATOR, list_tag_data[row][3],
+ COL_AOO_SOURCE, list_tag_data[row][4],
+ COL_AOO_SRC_INV_ID, list_tag_data[row][5],
+ COL_AOO_CR_NOT, list_tag_data[row][6],
+ -1);
+ for (item = 1; item < n_artworkorobject + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ 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);
+ }
+ }
+ }
+ else if (!strcmp (REGISTRYID_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_REGISTRY_ORG_ID, list_tag_data[row][1],
+ COL_REGISTRY_ITEM_ID, list_tag_data[row][2],
+ -1);
+ for (item = 1; item < n_registryid + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_REGISTRY_ORG_ID, NULL,
+ COL_REGISTRY_ITEM_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (COPYRIGHTOWNER_HEADER, name))
+ {
+ if (row_count > 0)
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_CR_OWNER_NAME, list_tag_data[row][1],
+ COL_CR_OWNER_ID, list_tag_data[row][2],
+ -1);
+ for (item = 1; item < n_copyrightowner + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_CR_OWNER_NAME, NULL,
+ COL_CR_OWNER_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (LOCATIONSHOWN_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LOC_SHO_SUB_LOC, list_tag_data[row][1],
+ COL_LOC_SHO_CITY, list_tag_data[row][2],
+ COL_LOC_SHO_STATE_PROV, list_tag_data[row][3],
+ COL_LOC_SHO_CNTRY, list_tag_data[row][4],
+ COL_LOC_SHO_CNTRY_ISO, list_tag_data[row][5],
+ COL_LOC_SHO_CNTRY_WRLD_REG, list_tag_data[row][6],
+ -1);
+ for (item = 1; item < n_locationshown + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ 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 if (!strcmp ("Xmp.iptcExt.OrganisationInImageName", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_NAME, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_NAME, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageCode", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_CODE, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_CODE, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.plus.PropertyReleaseID", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_PROP_REL_ID, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_PROP_REL_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.plus.ModelReleaseID", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_MOD_REL_ID, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_MOD_REL_ID, NULL,
+ -1);
+ }
+ }
+ }
+ }
+}
+
+const gchar *
+get_tag_ui_text (metadata_editor *args,
+ gchar *name,
+ gchar *mode)
+{
+ GObject *object;
+
+ object = gtk_builder_get_object (args->builder, name);
+
+ if (! strcmp ("single", mode))
+ {
+ GtkEntry *entry = GTK_ENTRY (object);
+ return gtk_entry_get_text (entry);
+ }
+ else if (!strcmp ("multi", mode))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ return gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+ }
+
+ return NULL;
+}
+
+static void
+get_list_elements (GString *xmldata, int element_count, gchar **rowtagdata)
+{
+ gint list_idx;
+
+ g_string_append (xmldata, "\t\t\t<list-element>\n");
+
+ for (list_idx = 0; list_idx < element_count; list_idx++)
+ {
+ g_string_append (xmldata, "\t\t\t\t<element>");
+
+ if (rowtagdata && rowtagdata[list_idx] && strlen(rowtagdata[list_idx]) > 0)
+ {
+ g_string_append (xmldata, rowtagdata[list_idx]);
+ }
+
+ g_string_append (xmldata, "</element>\n");
+ }
+ g_string_append (xmldata, "\t\t\t</list-element>\n");
+}
+
+gchar *
+get_tag_ui_list (metadata_editor *args, gchar *name, gchar *mode)
+{
+ GObject *object;
+ GtkWidget *widget;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GString *xmldata;
+ gint number_of_rows;
+ gint row;
+ gint has_data;
+ gchar *tagdata[256][256];
+
+ has_data = FALSE;
+ xmldata = g_string_new ("");
+
+ object = gtk_builder_get_object (args->builder, name);
+ widget = GTK_WIDGET(object);
+
+ liststore = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView *)widget));
+ treemodel = GTK_TREE_MODEL (liststore);
+ number_of_rows =
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);
+
+ for (row = 0; row < number_of_rows; row++)
+ {
+ if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row))
+ {
+ if (!strcmp (LICENSOR_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_LICENSOR_NAME, &tagdata[row][0],
+ COL_LICENSOR_ID, &tagdata[row][1],
+ COL_LICENSOR_PHONE1, &tagdata[row][2],
+ COL_LICENSOR_PHONE_TYPE1, &tagdata[row][3],
+ COL_LICENSOR_PHONE2, &tagdata[row][4],
+ COL_LICENSOR_PHONE_TYPE2, &tagdata[row][5],
+ COL_LICENSOR_EMAIL, &tagdata[row][6],
+ COL_LICENSOR_WEB, &tagdata[row][7],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
+ (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
+ (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
+ (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
+ (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0) ||
+ (tagdata[row][6] != NULL && strlen(tagdata[row][6]) > 0) ||
+ (tagdata[row][7] != NULL && strlen(tagdata[row][7]) > 0))
+ {
+
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 8, tagdata[row]);
+ }
+ }
+ else if (!strcmp (COPYRIGHTOWNER_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_CR_OWNER_NAME, &tagdata[row][0],
+ COL_CR_OWNER_ID, &tagdata[row][1],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
+ {
+ has_data = TRUE;
+
+ g_string_append (xmldata, "\t\t\t<list-element>\n");
+ g_string_append (xmldata, "\t\t\t\t<element>");
+ g_string_append (xmldata, tagdata[row][0]);
+ g_string_append (xmldata, "</element>\n");
+ g_string_append (xmldata, "\t\t\t\t<element>");
+ g_string_append (xmldata, tagdata[row][1]);
+ g_string_append (xmldata, "</element>\n");
+ g_string_append (xmldata, "\t\t\t</list-element>\n");
+ }
+ }
+ else if (!strcmp (IMAGECREATOR_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_IMG_CR8_NAME, &tagdata[row][0],
+ COL_IMG_CR8_ID, &tagdata[row][1],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 2, tagdata[row]);
+ }
+ }
+ else if (!strcmp (ARTWORKOROBJECT_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_AOO_TITLE, &tagdata[row][0],
+ COL_AOO_DATE_CREAT, &tagdata[row][1],
+ COL_AOO_CREATOR, &tagdata[row][2],
+ COL_AOO_SOURCE, &tagdata[row][3],
+ COL_AOO_SRC_INV_ID, &tagdata[row][4],
+ COL_AOO_CR_NOT, &tagdata[row][5],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
+ (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
+ (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
+ (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
+ (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 6, tagdata[row]);
+ }
+ }
+ else if (!strcmp (REGISTRYID_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_REGISTRY_ORG_ID, &tagdata[row][0],
+ COL_REGISTRY_ITEM_ID, &tagdata[row][1],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 2, tagdata[row]);
+ }
+ }
+ else if (!strcmp (LOCATIONSHOWN_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_LOC_SHO_SUB_LOC, &tagdata[row][0],
+ COL_LOC_SHO_CITY, &tagdata[row][1],
+ COL_LOC_SHO_STATE_PROV, &tagdata[row][2],
+ COL_LOC_SHO_CNTRY, &tagdata[row][3],
+ COL_LOC_SHO_CNTRY_ISO, &tagdata[row][4],
+ COL_LOC_SHO_CNTRY_WRLD_REG, &tagdata[row][5],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
+ (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
+ (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
+ (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
+ (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 6, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageName", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_ORG_IMG_NAME, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageCode", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_ORG_IMG_CODE, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.plus.PropertyReleaseID", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_PROP_REL_ID, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.plus.ModelReleaseID", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_MOD_REL_ID, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ }
+ }
+
+ if (has_data == TRUE)
+ {
+ gchar *xml;
+
+ xml = g_strdup (xmldata->str);
+ g_string_free(xmldata, TRUE);
+ return xml;
+ }
+
+ g_string_free(xmldata, TRUE);
+
+ return NULL;
+}
+
+gint
+get_tag_ui_combo (metadata_editor *args, gchar *name, gchar *mode)
+{
+ GObject *object;
+ GtkComboBoxText *combo;
+ gint value;
+
+ object = gtk_builder_get_object (args->builder, name);
+
+ combo = GTK_COMBO_BOX_TEXT (object);
+ value = gtk_combo_box_get_active (GTK_COMBO_BOX(combo));
+
+ return value;
+}
+
+void
+xml_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ metadata_editor *args;
+ int i;
+
+ args = user_data;
+
+ if (strcmp (element_name, "gimp-metadata") == 0)
+ {
+ gimpmetadata = FALSE;
+ }
+ else if (strcmp (element_name, "iptc-tag") == 0)
+ {
+ iptctag = FALSE;
+#ifdef _ENABLE_IPTC_TAG_
+ if (str_tag_name && str_tag_value)
+ {
+ /* make sure to only allow supported tags */
+ for (i = 0; i < n_equivalent_metadata_tags; i++)
+ {
+ if (strcmp(equivalent_metadata_tags[i].tag, str_tag_name) == 0)
+ {
+#ifdef _SET_IPTC_TAG_
+ set_tag_ui (args, i, str_tag_name, str_tag_value,
+ equivalent_metadata_tags[i].mode);
+#endif
+ if (force_write == TRUE)
+ gexiv2_metadata_set_tag_string (args->metadata,
+ str_tag_name,
+ str_tag_value);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ else if (strcmp (element_name, "xmp-tag") == 0)
+ {
+ xmptag = FALSE;
+ if (strcmp (str_tag_mode, "list") != 0)
+ {
+ if (str_tag_name && str_tag_value)
+ {
+ /* make sure to only allow supported tags */
+ for (i = 0; i < n_default_metadata_tags; i++)
+ {
+ if (strcmp(default_metadata_tags[i].tag, str_tag_name) == 0)
+ {
+ set_tag_ui (args, i, str_tag_name, str_tag_value,
+ default_metadata_tags[i].mode);
+#ifdef _ENABLE_FORCE_WRITE_
+ if (force_write == TRUE)
+ gexiv2_metadata_set_tag_string (args->metadata,
+ str_tag_name,
+ str_tag_value);
+#endif
+ break;
+ }
+ }
+ }
+
+ }
+ else if (strcmp (str_tag_mode, "list") == 0)
+ {
+ if (row_count > 0)
+ {
+ /* make sure to only allow supported tags */
+ for (i = 0; i < n_default_metadata_tags; i++)
+ {
+ if (strcmp(default_metadata_tags[i].tag, str_tag_name) == 0)
+ {
+ set_tag_ui (args, i, str_tag_name, str_tag_value,
+ default_metadata_tags[i].mode);
+#ifdef _ENABLE_FORCE_WRITE_
+ if (force_write == TRUE)
+ gexiv2_metadata_set_tag_string (args->metadata,
+ str_tag_name,
+ str_tag_value);
+#endif
+ break;
+ }
+ }
+ }
+ row_count = 0;
+ item_count = 0;
+ }
+ }
+ else if (strcmp (element_name, "tag-value") == 0)
+ {
+ tagvalue = FALSE;
+ }
+ else if (strcmp (element_name, "tag-list-value") == 0)
+ {
+ taglistvalue = FALSE;
+ }
+ else if (strcmp (element_name, "tag-name") == 0)
+ {
+ tagname = FALSE;
+ }
+ else if (strcmp (element_name, "tag-mode") == 0)
+ {
+ tagmode = FALSE;
+ }
+ else if (strcmp (element_name, "list-element") == 0)
+ {
+ listelement = FALSE;
+ item_count = 0;
+ }
+ else if (strcmp (element_name, "element") == 0)
+ {
+ element = FALSE;
+ list_tag_data[row_count][item_count] = g_strdup(str_element);
+ }
+}
+
+gboolean
+xml_parser_parse_file (GimpXmlParser *parser,
+ const gchar *filename,
+ GError **error)
+{
+ GIOChannel *io;
+ gboolean success;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ io = g_io_channel_new_file (filename, "r", error);
+ if (!io)
+ return FALSE;
+
+ success = xml_parser_parse_io_channel (parser, io, error);
+
+ g_io_channel_unref (io);
+
+ return success;
+}
+
+GimpXmlParser *
+xml_parser_new (const GMarkupParser *markup_parser,
+ gpointer user_data)
+{
+ GimpXmlParser *parser;
+
+ g_return_val_if_fail (markup_parser != NULL, NULL);
+
+ parser = g_slice_new (GimpXmlParser);
+
+ parser->context = g_markup_parse_context_new (markup_parser,
+ 0, user_data, NULL);
+
+ return parser;
+}
+
+void
+xml_parser_free (GimpXmlParser *parser)
+{
+ g_return_if_fail (parser != NULL);
+
+ g_markup_parse_context_free (parser->context);
+ g_slice_free (GimpXmlParser, parser);
+}
+
+gboolean
+parse_encoding (const gchar *text,
+ gint text_len,
+ gchar **encoding)
+{
+ const gchar *start;
+ const gchar *end;
+ gint i;
+
+ g_return_val_if_fail (text, FALSE);
+
+ if (text_len < 20)
+ return FALSE;
+
+ start = g_strstr_len (text, text_len, "<?xml");
+ if (!start)
+ return FALSE;
+
+ end = g_strstr_len (start, text_len - (start - text), "?>");
+ if (!end)
+ return FALSE;
+
+ *encoding = NULL;
+
+ text_len = end - start;
+ if (text_len < 12)
+ return TRUE;
+
+ start = g_strstr_len (start + 1, text_len - 1, "encoding");
+ if (!start)
+ return TRUE;
+
+ start += 8;
+
+ while (start < end && *start == ' ')
+ start++;
+
+ if (*start != '=')
+ return TRUE;
+
+ start++;
+
+ while (start < end && *start == ' ')
+ start++;
+
+ if (*start != '\"' && *start != '\'')
+ return TRUE;
+
+ text_len = end - start;
+ if (text_len < 1)
+ return TRUE;
+
+ for (i = 1; i < text_len; i++)
+ if (start[i] == start[0])
+ break;
+
+ if (i == text_len || i < 3)
+ return TRUE;
+
+ *encoding = g_strndup (start + 1, i - 1);
+
+ return TRUE;
+}
+
+gboolean
+xml_parser_parse_io_channel (GimpXmlParser *parser,
+ GIOChannel *io,
+ GError **error)
+{
+ GIOStatus status;
+ gchar buffer[4096];
+ gsize len = 0;
+ gsize bytes;
+ const gchar *io_encoding;
+ gchar *encoding = NULL;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (io != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ io_encoding = g_io_channel_get_encoding (io);
+ if (g_strcmp0 (io_encoding, "UTF-8"))
+ {
+ g_warning ("xml_parser_parse_io_channel():\n"
+ "The encoding has already been set on this GIOChannel!");
+ return FALSE;
+ }
+
+ /* try to determine the encoding */
+
+ g_io_channel_set_encoding (io, NULL, NULL);
+
+ while (len < sizeof (buffer))
+ {
+ status = g_io_channel_read_chars (io, buffer + len, 1, &bytes, error);
+ len += bytes;
+
+ if (status == G_IO_STATUS_ERROR)
+ return FALSE;
+ if (status == G_IO_STATUS_EOF)
+ break;
+
+ if (parse_encoding (buffer, len, &encoding))
+ break;
+ }
+
+ if (encoding)
+ {
+ if (! g_io_channel_set_encoding (io, encoding, error))
+ return FALSE;
+
+ if (encoding)
+ g_free (encoding);
+ }
+ else
+ {
+ g_io_channel_set_encoding (io, "UTF-8", NULL);
+ }
+
+ while (TRUE)
+ {
+ if (!g_markup_parse_context_parse (parser->context, buffer, len, error))
+ return FALSE;
+
+ status = g_io_channel_read_chars (io,
+ buffer, sizeof (buffer), &len, error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_ERROR:
+ return FALSE;
+ case G_IO_STATUS_EOF:
+ return g_markup_parse_context_end_parse (parser->context, error);
+ case G_IO_STATUS_NORMAL:
+ case G_IO_STATUS_AGAIN:
+ break;
+ }
+ }
+}
+
diff --git a/plug-ins/metadata/metadata-xml.h b/plug-ins/metadata/metadata-xml.h
new file mode 100644
index 0000000..12ac77b
--- /dev/null
+++ b/plug-ins/metadata/metadata-xml.h
@@ -0,0 +1,98 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __METADATA_XML_H__
+#define __METADATA_XML_H__
+
+#include "metadata-misc.h"
+
+struct _GimpXmlParser
+{
+ GMarkupParseContext *context;
+};
+
+typedef struct _GimpXmlParser GimpXmlParser;
+
+void
+xml_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+
+void
+xml_parser_data (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+
+void
+set_tag_ui (metadata_editor *args,
+ int index,
+ gchar *name,
+ gchar *value,
+ gchar *mode);
+
+const gchar *
+get_tag_ui_text (metadata_editor *args,
+ gchar *name,
+ gchar *mode);
+
+gchar *
+get_tag_ui_list (metadata_editor *args,
+ gchar *name,
+ gchar *mode);
+
+gint
+get_tag_ui_combo (metadata_editor *args,
+ gchar *name,
+ gchar *mode);
+
+void
+xml_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+
+gboolean
+xml_parser_parse_file (GimpXmlParser *parser,
+ const gchar *filename,
+ GError **error);
+
+void
+xml_parser_free (GimpXmlParser *parser);
+
+gboolean
+parse_encoding (const gchar *text,
+ gint text_len,
+ gchar **encoding);
+
+gboolean
+xml_parser_parse_io_channel (GimpXmlParser *parser,
+ GIOChannel *io,
+ GError **error);
+
+GimpXmlParser *
+xml_parser_new (const GMarkupParser *markup_parser,
+ gpointer user_data);
+
+#endif /* __METADATA_XML_H__ */
+