diff options
Diffstat (limited to 'plug-ins/metadata')
-rw-r--r-- | plug-ins/metadata/Makefile.am | 82 | ||||
-rw-r--r-- | plug-ins/metadata/Makefile.in | 1114 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-editor.c | 4757 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-editor.h | 27 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-impexp.c | 244 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-impexp.h | 30 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-misc.h | 70 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-tags.c | 515 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-tags.h | 253 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-viewer.c | 683 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-xml.c | 1147 | ||||
-rw-r--r-- | plug-ins/metadata/metadata-xml.h | 98 |
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__ */ + |