diff options
author | Perberos <[email protected]> | 2011-11-06 19:30:49 -0300 |
---|---|---|
committer | Perberos <[email protected]> | 2011-11-06 19:30:49 -0300 |
commit | a8d28a6ce7e0c56dacba5d527d9134573a008902 (patch) | |
tree | 8852602004b5a13cc5d1ce3ecd7a314be81d1198 /src | |
download | eom-a8d28a6ce7e0c56dacba5d527d9134573a008902.tar.bz2 eom-a8d28a6ce7e0c56dacba5d527d9134573a008902.tar.xz |
inicial
Diffstat (limited to 'src')
96 files changed, 36749 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8d9e15f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,239 @@ +if ENABLE_JPEG +jpeg_LIB = $(top_builddir)/jpegutils/libeom-jpegutils.la +endif + +toolbar_LIB = $(top_builddir)/cut-n-paste/toolbar-editor/libtoolbareditor.la + +screensaver_LIB = $(top_builddir)/cut-n-paste/totem-screensaver/libtotemscrsaver.la + +noinst_LTLIBRARIES = libeom.la + +bin_PROGRAMS = eom + +headerdir = $(prefix)/include/[email protected][email protected]/eom +header_DATA = $(INST_H_FILES) + +MARSHAL_OUTPUT = \ + eom-marshal.h \ + eom-marshal.c + +NOINST_H_FILES = \ + eom-session.h \ + eom-util.h \ + eom-pixbuf-util.h \ + eom-preferences-dialog.h \ + eom-config-keys.h \ + eom-image-jpeg.h \ + eom-image-private.h \ + eom-uri-converter.h \ + eom-metadata-reader.h \ + eom-metadata-reader-jpg.h \ + eom-metadata-reader-png.h \ + eom-save-as-dialog-helper.h \ + eom-print-image-setup.h \ + eom-print-preview.h \ + eom-print.h \ + eom-module.h \ + eom-plugin-manager.h \ + eom-plugin-engine.h \ + uta.h \ + eom-close-confirmation-dialog.h \ + zoom.h + +if ENABLE_PYTHON +NOINST_H_FILES += \ + eom-python-module.h \ + eom-python-plugin.h +endif + +INST_H_FILES = \ + eom-application.h \ + eom-debug.h \ + eom-window.h \ + eom-sidebar.h \ + eom-dialog.h \ + eom-properties-dialog.h \ + eom-error-message-area.h \ + eom-file-chooser.h \ + eom-statusbar.h \ + eom-thumb-nav.h \ + eom-transform.h \ + eom-image.h \ + eom-enums.h \ + eom-image-save-info.h \ + eom-scroll-view.h \ + eom-thumb-view.h \ + eom-list-store.h \ + eom-thumbnail.h \ + eom-job-queue.h \ + eom-jobs.h \ + eom-plugin.h + +libeom_la_SOURCES = \ + eom-application.c \ + eom-session.c \ + eom-debug.c \ + eom-util.c \ + eom-pixbuf-util.c \ + eom-window.c \ + eom-sidebar.c \ + eom-dialog.c \ + eom-preferences-dialog.c \ + eom-properties-dialog.c \ + eom-error-message-area.c \ + eom-file-chooser.c \ + eom-statusbar.c \ + eom-thumb-nav.c \ + eom-transform.c \ + eom-image.c \ + eom-image-jpeg.c \ + eom-image-save-info.c \ + eom-scroll-view.c \ + eom-thumb-view.c \ + eom-list-store.c \ + eom-thumbnail.c \ + eom-job-queue.c \ + eom-jobs.c \ + eom-uri-converter.c \ + eom-metadata-reader.c \ + eom-metadata-reader-jpg.c \ + eom-metadata-reader-png.c \ + eom-save-as-dialog-helper.c \ + eom-print-image-setup.c \ + eom-print-preview.c \ + eom-print.c \ + eom-module.c \ + eom-close-confirmation-dialog.c \ + eom-plugin.c \ + eom-plugin-manager.c \ + eom-plugin-engine.c \ + uta.c \ + zoom.c \ + $(BUILT_SOURCES) \ + $(NOINST_H_FILES) \ + $(INST_H_FILES) + +if HAVE_EXIF +INST_H_FILES += \ + eom-exif-util.h \ + eom-exif-details.h +libeom_la_SOURCES += \ + eom-exif-util.c \ + eom-exif-details.c +endif + +if ENABLE_PYTHON +libeom_la_SOURCES += \ + eom-python-module.c \ + eom-python-module.h \ + eom-python-plugin.c \ + eom-python-plugin.h +endif + +if HAVE_EXEMPI +# We need to make sure eom-exif-details.h +# is only listed once in INST_H_FILES +# or the build will break with automake-1.11 +if !HAVE_EXIF +INST_H_FILES += \ + eom-exif-details.h +endif !HAVE_EXIF +libeom_la_SOURCES += \ + eom-exif-details.c +endif HAVE_EXEMPI + +libeom_la_CFLAGS = \ + -I$(top_srcdir)/jpegutils \ + -I$(top_srcdir)/cut-n-paste/toolbar-editor \ + -I$(top_srcdir)/cut-n-paste/totem-screensaver \ + $(EOM_CFLAGS) \ + $(WARN_CFLAGS) \ + -DG_LOG_DOMAIN=\"EOM\" \ + -DEOM_PREFIX=\""${prefix}"\" \ + -DEOM_DATA_DIR=\""$(pkgdatadir)"\" \ + -DEOM_LOCALE_DIR=\""$(datadir)/locale"\" \ + -DEOM_PIXMAPS_DIR=\""$(datadir)/pixmaps/eom"\" \ + -DEOM_PLUGIN_DIR=\""$(libdir)/eom/plugins"\" + +libeom_la_LIBADD = \ + $(EOM_LIBS) + +if HAVE_LCMS +libeom_la_CFLAGS += \ + $(X11_CFLAGS) + +libeom_la_LIBADD += \ + $(X11_LIBS) +endif + +if ENABLE_PYTHON +libeom_la_CFLAGS += \ + $(NO_STRICT_ALIASING_CFLAGS) \ + $(PYGTK_CFLAGS) \ + $(PYTHON_CFLAGS) \ + $(AM_CFLAGS) + +libeom_la_LIBADD += \ + $(top_builddir)/bindings/python/eom.la +endif + +libeom_la_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*" + +eom_SOURCES = main.c + +eom_CFLAGS = \ + -I$(top_srcdir)/cut-n-paste/toolbar-editor \ + -I$(top_srcdir)/cut-n-paste/totem-screensaver \ + $(EOM_CFLAGS) \ + -DEOM_DATA_DIR=\""$(pkgdatadir)"\" \ + -DEOM_LOCALE_DIR=\""$(datadir)/locale"\" + +eom_LDADD = \ + libeom.la \ + $(EOM_LIBS) \ + $(LIBJPEG) \ + $(toolbar_LIB) \ + $(screensaver_LIB) \ + $(jpeg_LIB) + +eom_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*" + +BUILT_SOURCES = \ + eom-enum-types.c \ + eom-enum-types.h \ + $(MARSHAL_OUTPUT) + +eom-enum-types.h: eom-enum-types.h.template $(INST_H_FILES) $(GLIB_MKENUMS) + $(AM_V_GEN)(cd $(srcdir) && $(GLIB_MKENUMS) --template eom-enum-types.h.template $(INST_H_FILES)) > [email protected] + +eom-enum-types.c: eom-enum-types.c.template $(INST_H_FILES) $(GLIB_MKENUMS) + $(AM_V_GEN)(cd $(srcdir) && $(GLIB_MKENUMS) --template eom-enum-types.c.template $(INST_H_FILES)) > [email protected] + +eom-marshal.h: eom-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --header --internal --prefix=eom_marshal > [email protected] + +eom-marshal.c: eom-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --body --header --prefix=eom_marshal > [email protected] + +EXTRA_DIST = \ + eom-enum-types.h.template \ + eom-enum-types.c.template \ + eom-marshal.list + +if HAVE_DBUS + +BUILT_SOURCES += eom-application-service.h + +EXTRA_DIST += eom-application-service.xml + +eom-application-service.h: eom-application-service.xml + $(AM_V_GEN)dbus-binding-tool --prefix=eom_application --mode=glib-server --output=eom-application-service.h $< + +endif + +CLEANFILES = $(BUILT_SOURCES) + +dist-hook: + cd $(distdir); rm -f $(BUILT_SOURCES) + +-include $(top_srcdir)/git.mk diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..deacf79 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1326 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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. + + + + +VPATH = @[email protected] +pkgdatadir = $(datadir)/@[email protected] +pkgincludedir = $(includedir)/@[email protected] +pkglibdir = $(libdir)/@[email protected] +pkglibexecdir = $(libexecdir)/@[email protected] +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 = @[email protected] +host_triplet = @[email protected] +bin_PROGRAMS = eom$(EXEEXT) [email protected][email protected]__append_1 = \ [email protected][email protected] eom-python-module.h \ [email protected][email protected] eom-python-plugin.h + [email protected][email protected]__append_2 = \ [email protected][email protected] eom-exif-util.h \ [email protected][email protected] eom-exif-details.h + [email protected][email protected]__append_3 = \ [email protected][email protected] eom-exif-util.c \ [email protected][email protected] eom-exif-details.c + [email protected][email protected]__append_4 = \ [email protected][email protected] eom-python-module.c \ [email protected][email protected] eom-python-module.h \ [email protected][email protected] eom-python-plugin.c \ [email protected][email protected] eom-python-plugin.h + + +# We need to make sure eom-exif-details.h +# is only listed once in INST_H_FILES +# or the build will break with automake-1.11 + [email protected][email protected]__append_6 = \ [email protected][email protected] eom-exif-details.c + [email protected][email protected]__append_7 = \ [email protected][email protected] $(X11_CFLAGS) + [email protected][email protected]__append_8 = \ [email protected][email protected] $(X11_LIBS) + [email protected][email protected]__append_9 = \ [email protected][email protected] $(NO_STRICT_ALIASING_CFLAGS) \ [email protected][email protected] $(PYGTK_CFLAGS) \ [email protected][email protected] $(PYTHON_CFLAGS) \ [email protected][email protected] $(AM_CFLAGS) + [email protected][email protected]__append_10 = \ [email protected][email protected] $(top_builddir)/bindings/python/eom.la + [email protected][email protected]__append_11 = eom-application-service.h [email protected][email protected]__append_12 = eom-application-service.xml +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = [email protected][email protected]__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +libeom_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ + $(am__append_10) +am__libeom_la_SOURCES_DIST = eom-application.c eom-session.c \ + eom-debug.c eom-util.c eom-pixbuf-util.c eom-window.c \ + eom-sidebar.c eom-dialog.c eom-preferences-dialog.c \ + eom-properties-dialog.c eom-error-message-area.c \ + eom-file-chooser.c eom-statusbar.c eom-thumb-nav.c \ + eom-transform.c eom-image.c eom-image-jpeg.c \ + eom-image-save-info.c eom-scroll-view.c eom-thumb-view.c \ + eom-list-store.c eom-thumbnail.c eom-job-queue.c eom-jobs.c \ + eom-uri-converter.c eom-metadata-reader.c \ + eom-metadata-reader-jpg.c eom-metadata-reader-png.c \ + eom-save-as-dialog-helper.c eom-print-image-setup.c \ + eom-print-preview.c eom-print.c eom-module.c \ + eom-close-confirmation-dialog.c eom-plugin.c \ + eom-plugin-manager.c eom-plugin-engine.c uta.c zoom.c \ + eom-enum-types.c eom-enum-types.h eom-marshal.h eom-marshal.c \ + eom-application-service.h eom-session.h eom-util.h \ + eom-pixbuf-util.h eom-preferences-dialog.h eom-config-keys.h \ + eom-image-jpeg.h eom-image-private.h eom-uri-converter.h \ + eom-metadata-reader.h eom-metadata-reader-jpg.h \ + eom-metadata-reader-png.h eom-save-as-dialog-helper.h \ + eom-print-image-setup.h eom-print-preview.h eom-print.h \ + eom-module.h eom-plugin-manager.h eom-plugin-engine.h uta.h \ + eom-close-confirmation-dialog.h zoom.h eom-python-module.h \ + eom-python-plugin.h eom-application.h eom-debug.h eom-window.h \ + eom-sidebar.h eom-dialog.h eom-properties-dialog.h \ + eom-error-message-area.h eom-file-chooser.h eom-statusbar.h \ + eom-thumb-nav.h eom-transform.h eom-image.h eom-enums.h \ + eom-image-save-info.h eom-scroll-view.h eom-thumb-view.h \ + eom-list-store.h eom-thumbnail.h eom-job-queue.h eom-jobs.h \ + eom-plugin.h eom-exif-util.h eom-exif-details.h \ + eom-exif-util.c eom-exif-details.c eom-python-module.c \ + eom-python-plugin.c +am__objects_1 = libeom_la-eom-marshal.lo +am__objects_2 = +am__objects_3 = libeom_la-eom-enum-types.lo $(am__objects_1) \ + $(am__objects_2) +am__objects_4 = $(am__objects_2) +am__objects_5 = $(am__objects_2) $(am__objects_2) [email protected][email protected]__objects_6 = libeom_la-eom-exif-util.lo \ [email protected][email protected] libeom_la-eom-exif-details.lo [email protected][email protected]__objects_7 = libeom_la-eom-python-module.lo \ [email protected][email protected] libeom_la-eom-python-plugin.lo [email protected][email protected]__objects_8 = libeom_la-eom-exif-details.lo +am_libeom_la_OBJECTS = libeom_la-eom-application.lo \ + libeom_la-eom-session.lo libeom_la-eom-debug.lo \ + libeom_la-eom-util.lo libeom_la-eom-pixbuf-util.lo \ + libeom_la-eom-window.lo libeom_la-eom-sidebar.lo \ + libeom_la-eom-dialog.lo libeom_la-eom-preferences-dialog.lo \ + libeom_la-eom-properties-dialog.lo \ + libeom_la-eom-error-message-area.lo \ + libeom_la-eom-file-chooser.lo libeom_la-eom-statusbar.lo \ + libeom_la-eom-thumb-nav.lo libeom_la-eom-transform.lo \ + libeom_la-eom-image.lo libeom_la-eom-image-jpeg.lo \ + libeom_la-eom-image-save-info.lo libeom_la-eom-scroll-view.lo \ + libeom_la-eom-thumb-view.lo libeom_la-eom-list-store.lo \ + libeom_la-eom-thumbnail.lo libeom_la-eom-job-queue.lo \ + libeom_la-eom-jobs.lo libeom_la-eom-uri-converter.lo \ + libeom_la-eom-metadata-reader.lo \ + libeom_la-eom-metadata-reader-jpg.lo \ + libeom_la-eom-metadata-reader-png.lo \ + libeom_la-eom-save-as-dialog-helper.lo \ + libeom_la-eom-print-image-setup.lo \ + libeom_la-eom-print-preview.lo libeom_la-eom-print.lo \ + libeom_la-eom-module.lo \ + libeom_la-eom-close-confirmation-dialog.lo \ + libeom_la-eom-plugin.lo libeom_la-eom-plugin-manager.lo \ + libeom_la-eom-plugin-engine.lo libeom_la-uta.lo \ + libeom_la-zoom.lo $(am__objects_3) $(am__objects_4) \ + $(am__objects_5) $(am__objects_6) $(am__objects_7) \ + $(am__objects_8) +libeom_la_OBJECTS = $(am_libeom_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libeom_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libeom_la_CFLAGS) \ + $(CFLAGS) $(libeom_la_LDFLAGS) $(LDFLAGS) -o [email protected] +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(headerdir)" +PROGRAMS = $(bin_PROGRAMS) +am_eom_OBJECTS = eom-main.$(OBJEXT) +eom_OBJECTS = $(am_eom_OBJECTS) +eom_DEPENDENCIES = libeom.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(toolbar_LIB) $(screensaver_LIB) \ + $(jpeg_LIB) +eom_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(eom_CFLAGS) $(CFLAGS) \ + $(eom_LDFLAGS) $(LDFLAGS) -o [email protected] +DEFAULT_INCLUDES = [email protected][email protected] -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +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_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " [email protected]; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o [email protected] +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " [email protected]; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " [email protected]; +SOURCES = $(libeom_la_SOURCES) $(eom_SOURCES) +DIST_SOURCES = $(am__libeom_la_SOURCES_DIST) $(eom_SOURCES) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +DATA = $(header_DATA) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @[email protected] +ACLOCAL_AMFLAGS = @[email protected] +ALL_LINGUAS = @[email protected] +AMTAR = @[email protected] +AM_DEFAULT_VERBOSITY = @[email protected] +AR = @[email protected] +AUTOCONF = @[email protected] +AUTOHEADER = @[email protected] +AUTOMAKE = @[email protected] +AWK = @[email protected] +CATALOGS = @[email protected] +CATOBJEXT = @[email protected] +CC = @[email protected] +CCDEPMODE = @[email protected] +CFLAGS = @[email protected] +CPP = @[email protected] +CPPFLAGS = @[email protected] +CYGPATH_W = @[email protected] +DATADIRNAME = @[email protected] +DBUS_BINDING_TOOL = @[email protected] +DBUS_CFLAGS = @[email protected] +DBUS_LIBS = @[email protected] +DEFS = @[email protected] +DEPDIR = @[email protected] +DISABLE_DEPRECATED = @[email protected] +DISTCHECK_CONFIGURE_FLAGS = @[email protected] +DLLTOOL = @[email protected] +DOC_USER_FORMATS = @[email protected] +DSYMUTIL = @[email protected] +DUMPBIN = @[email protected] +ECHO_C = @[email protected] +ECHO_N = @[email protected] +ECHO_T = @[email protected] +EGREP = @[email protected] +EOM_API_VERSION = @[email protected] +EOM_CFLAGS = @[email protected] +EOM_DOC_EXIF_START = @[email protected] +EOM_DOC_EXIF_STOP = @[email protected] +EOM_LIBS = @[email protected] +EOM_MAJOR_VERSION = @[email protected] +EOM_MICRO_VERSION = @[email protected] +EOM_MINOR_VERSION = @[email protected] +EXEEXT = @[email protected] +EXEMPI_CFLAGS = @[email protected] +EXEMPI_LIBS = @[email protected] +EXIF_CFLAGS = @[email protected] +EXIF_LIBS = @[email protected] +FGREP = @[email protected] +GETTEXT_PACKAGE = @[email protected] +GLIB_GENMARSHAL = @[email protected] +GLIB_MKENUMS = @[email protected] +GMOFILES = @[email protected] +GMSGFMT = @[email protected] +GREP = @[email protected] +GTKDOC_CHECK = @[email protected] +GTKDOC_DEPS_CFLAGS = @[email protected] +GTKDOC_DEPS_LIBS = @[email protected] +GTKDOC_MKPDF = @[email protected] +GTKDOC_REBASE = @[email protected] +HELP_DIR = @[email protected] +HTML_DIR = @[email protected] +INSTALL = @[email protected] +INSTALL_DATA = @[email protected] +INSTALL_PROGRAM = @[email protected] +INSTALL_SCRIPT = @[email protected] +INSTALL_STRIP_PROGRAM = @[email protected] +INSTOBJEXT = @[email protected] +INTLLIBS = @[email protected] +INTLTOOL_EXTRACT = @[email protected] +INTLTOOL_MERGE = @[email protected] +INTLTOOL_PERL = @[email protected] +INTLTOOL_UPDATE = @[email protected] +LCMS_CFLAGS = @[email protected] +LCMS_LIBS = @[email protected] +LD = @[email protected] +LDFLAGS = @[email protected] +LIBJPEG = @[email protected] +LIBOBJS = @[email protected] +LIBS = @[email protected] +LIBTOOL = @[email protected] +LIBXML2_CFLAGS = @[email protected] +LIBXML2_LIBS = @[email protected] +LIPO = @[email protected] +LN_S = @[email protected] +LTLIBOBJS = @[email protected] +MAINT = @[email protected] +MAKEINFO = @[email protected] +MANIFEST_TOOL = @[email protected] +MATECONFTOOL = @[email protected] +MATECONF_SCHEMA_CONFIG_SOURCE = @[email protected] +MATECONF_SCHEMA_FILE_DIR = @[email protected] +MKDIR_P = @[email protected] +MKINSTALLDIRS = @[email protected] +MSGFMT = @[email protected] +MSGFMT_OPTS = @[email protected] +MSGMERGE = @[email protected] +NM = @[email protected] +NMEDIT = @[email protected] +NO_STRICT_ALIASING_CFLAGS = @[email protected] +OBJDUMP = @[email protected] +OBJEXT = @[email protected] +OMF_DIR = @[email protected] +OTOOL = @[email protected] +OTOOL64 = @[email protected] +PACKAGE = @[email protected] +PACKAGE_BUGREPORT = @[email protected] +PACKAGE_NAME = @[email protected] +PACKAGE_STRING = @[email protected] +PACKAGE_TARNAME = @[email protected] +PACKAGE_URL = @[email protected] +PACKAGE_VERSION = @[email protected] +PATH_SEPARATOR = @[email protected] +PKG_CONFIG = @[email protected] +PKG_CONFIG_LIBDIR = @[email protected] +PKG_CONFIG_PATH = @[email protected] +POFILES = @[email protected] +POSUB = @[email protected] +PO_IN_DATADIR_FALSE = @[email protected] +PO_IN_DATADIR_TRUE = @[email protected] +PYGOBJECT_CODEGEN = @[email protected] +PYGOBJECT_DEFSDIR = @[email protected] +PYGOBJECT_H2DEF = @[email protected] +PYGTK_CFLAGS = @[email protected] +PYGTK_DEFSDIR = @[email protected] +PYGTK_LIBS = @[email protected] +PYTHON = @[email protected] +PYTHON_CFLAGS = @[email protected] +PYTHON_EXEC_PREFIX = @[email protected] +PYTHON_EXTRA_LIBS = @[email protected] +PYTHON_LIBS = @[email protected] +PYTHON_LIB_LOC = @[email protected] +PYTHON_PLATFORM = @[email protected] +PYTHON_PREFIX = @[email protected] +PYTHON_VERSION = @[email protected] +RANLIB = @[email protected] +RSVG_CFLAGS = @[email protected] +RSVG_LIBS = @[email protected] +SED = @[email protected] +SET_MAKE = @[email protected] +SHELL = @[email protected] +STRIP = @[email protected] +USE_NLS = @[email protected] +VERSION = @[email protected] +WARN_CFLAGS = @[email protected] +X11_CFLAGS = @[email protected] +X11_LIBS = @[email protected] +XGETTEXT = @[email protected] +XMKMF = @[email protected] +X_CFLAGS = @[email protected] +X_EXTRA_LIBS = @[email protected] +X_LIBS = @[email protected] +X_PRE_LIBS = @[email protected] +abs_builddir = @[email protected] +abs_srcdir = @[email protected] +abs_top_builddir = @[email protected] +abs_top_srcdir = @[email protected] +ac_ct_AR = @[email protected] +ac_ct_CC = @[email protected] +ac_ct_DUMPBIN = @[email protected] +am__include = @[email protected] +am__leading_dot = @[email protected] +am__quote = @[email protected] +am__tar = @[email protected] +am__untar = @[email protected] +bindir = @[email protected] +build = @[email protected] +build_alias = @[email protected] +build_cpu = @[email protected] +build_os = @[email protected] +build_vendor = @[email protected] +builddir = @[email protected] +datadir = @[email protected] +datarootdir = @[email protected] +docdir = @[email protected] +dvidir = @[email protected] +exec_prefix = @[email protected] +host = @[email protected] +host_alias = @[email protected] +host_cpu = @[email protected] +host_os = @[email protected] +host_vendor = @[email protected] +htmldir = @[email protected] +includedir = @[email protected] +infodir = @[email protected] +install_sh = @[email protected] +libdir = @[email protected] +libexecdir = @[email protected] +localedir = @[email protected] +localstatedir = @[email protected] +mandir = @[email protected] +mkdir_p = @[email protected] +oldincludedir = @[email protected] +pdfdir = @[email protected] +pkgpyexecdir = @[email protected] +pkgpythondir = @[email protected] +prefix = @[email protected] +program_transform_name = @[email protected] +psdir = @[email protected] +pyexecdir = @[email protected] +pythondir = @[email protected] +sbindir = @[email protected] +sharedstatedir = @[email protected] +srcdir = @[email protected] +sysconfdir = @[email protected] +target_alias = @[email protected] +top_build_prefix = @[email protected] +top_builddir = @[email protected] +top_srcdir = @[email protected] [email protected][email protected]_LIB = $(top_builddir)/jpegutils/libeom-jpegutils.la +toolbar_LIB = $(top_builddir)/cut-n-paste/toolbar-editor/libtoolbareditor.la +screensaver_LIB = $(top_builddir)/cut-n-paste/totem-screensaver/libtotemscrsaver.la +noinst_LTLIBRARIES = libeom.la +headerdir = $(prefix)/include/[email protected][email protected]/eom +header_DATA = $(INST_H_FILES) +MARSHAL_OUTPUT = \ + eom-marshal.h \ + eom-marshal.c + +NOINST_H_FILES = eom-session.h eom-util.h eom-pixbuf-util.h \ + eom-preferences-dialog.h eom-config-keys.h eom-image-jpeg.h \ + eom-image-private.h eom-uri-converter.h eom-metadata-reader.h \ + eom-metadata-reader-jpg.h eom-metadata-reader-png.h \ + eom-save-as-dialog-helper.h eom-print-image-setup.h \ + eom-print-preview.h eom-print.h eom-module.h \ + eom-plugin-manager.h eom-plugin-engine.h uta.h \ + eom-close-confirmation-dialog.h zoom.h $(am__append_1) +INST_H_FILES = eom-application.h eom-debug.h eom-window.h \ + eom-sidebar.h eom-dialog.h eom-properties-dialog.h \ + eom-error-message-area.h eom-file-chooser.h eom-statusbar.h \ + eom-thumb-nav.h eom-transform.h eom-image.h eom-enums.h \ + eom-image-save-info.h eom-scroll-view.h eom-thumb-view.h \ + eom-list-store.h eom-thumbnail.h eom-job-queue.h eom-jobs.h \ + eom-plugin.h $(am__append_2) $(am__append_5) +libeom_la_SOURCES = eom-application.c eom-session.c eom-debug.c \ + eom-util.c eom-pixbuf-util.c eom-window.c eom-sidebar.c \ + eom-dialog.c eom-preferences-dialog.c eom-properties-dialog.c \ + eom-error-message-area.c eom-file-chooser.c eom-statusbar.c \ + eom-thumb-nav.c eom-transform.c eom-image.c eom-image-jpeg.c \ + eom-image-save-info.c eom-scroll-view.c eom-thumb-view.c \ + eom-list-store.c eom-thumbnail.c eom-job-queue.c eom-jobs.c \ + eom-uri-converter.c eom-metadata-reader.c \ + eom-metadata-reader-jpg.c eom-metadata-reader-png.c \ + eom-save-as-dialog-helper.c eom-print-image-setup.c \ + eom-print-preview.c eom-print.c eom-module.c \ + eom-close-confirmation-dialog.c eom-plugin.c \ + eom-plugin-manager.c eom-plugin-engine.c uta.c zoom.c \ + $(BUILT_SOURCES) $(NOINST_H_FILES) $(INST_H_FILES) \ + $(am__append_3) $(am__append_4) $(am__append_6) +libeom_la_CFLAGS = -I$(top_srcdir)/jpegutils \ + -I$(top_srcdir)/cut-n-paste/toolbar-editor \ + -I$(top_srcdir)/cut-n-paste/totem-screensaver $(EOM_CFLAGS) \ + $(WARN_CFLAGS) -DG_LOG_DOMAIN=\"EOM\" \ + -DEOM_PREFIX=\""${prefix}"\" \ + -DEOM_DATA_DIR=\""$(pkgdatadir)"\" \ + -DEOM_LOCALE_DIR=\""$(datadir)/locale"\" \ + -DEOM_PIXMAPS_DIR=\""$(datadir)/pixmaps/eom"\" \ + -DEOM_PLUGIN_DIR=\""$(libdir)/eom/plugins"\" $(am__append_7) \ + $(am__append_9) +libeom_la_LIBADD = $(EOM_LIBS) $(am__append_8) $(am__append_10) +libeom_la_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*" +eom_SOURCES = main.c +eom_CFLAGS = \ + -I$(top_srcdir)/cut-n-paste/toolbar-editor \ + -I$(top_srcdir)/cut-n-paste/totem-screensaver \ + $(EOM_CFLAGS) \ + -DEOM_DATA_DIR=\""$(pkgdatadir)"\" \ + -DEOM_LOCALE_DIR=\""$(datadir)/locale"\" + +eom_LDADD = \ + libeom.la \ + $(EOM_LIBS) \ + $(LIBJPEG) \ + $(toolbar_LIB) \ + $(screensaver_LIB) \ + $(jpeg_LIB) + +eom_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*" +BUILT_SOURCES = eom-enum-types.c eom-enum-types.h $(MARSHAL_OUTPUT) \ + $(am__append_11) +EXTRA_DIST = eom-enum-types.h.template eom-enum-types.c.template \ + eom-marshal.list $(am__append_12) +CLEANFILES = $(BUILT_SOURCES) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @[email protected] $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f [email protected]; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: 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)/[email protected] $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/[email protected] $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @[email protected] $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @[email protected] $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libeom.la: $(libeom_la_OBJECTS) $(libeom_la_DEPENDENCIES) + $(AM_V_CCLD)$(libeom_la_LINK) $(libeom_la_OBJECTS) $(libeom_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + 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)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || 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)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_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 +eom$(EXEEXT): $(eom_OBJECTS) $(eom_DEPENDENCIES) + @rm -f eom$(EXEEXT) + $(AM_V_CCLD)$(eom_LINK) $(eom_OBJECTS) $(eom_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/libeo[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/libeom_la-eom-[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] [email protected][email protected]@[email protected] @[email protected]/$(DEPDIR)/[email protected][email protected] + +.c.o: [email protected][email protected] $(AM_V_CC)$(COMPILE) -MT [email protected] -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o [email protected] $< [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po [email protected][email protected]@[email protected] source='$<' object='[email protected]' libtool=no @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(COMPILE) -c $< + +.c.obj: [email protected][email protected] $(AM_V_CC)$(COMPILE) -MT [email protected] -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o [email protected] `$(CYGPATH_W) '$<'` [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po [email protected][email protected]@[email protected] source='$<' object='[email protected]' libtool=no @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: [email protected][email protected] $(AM_V_CC)$(LTCOMPILE) -MT [email protected] -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o [email protected] $< [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo [email protected][email protected]@[email protected] source='$<' object='[email protected]' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] + +libeom_la-eom-application.lo: eom-application.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-application.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-application.Tpo -c -o libeom_la-eom-application.lo `test -f 'eom-application.c' || echo '$(srcdir)/'`eom-application.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-application.Tpo $(DEPDIR)/libeom_la-eom-application.Plo [email protected][email protected]@[email protected] source='eom-application.c' object='libeom_la-eom-application.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-application.lo `test -f 'eom-application.c' || echo '$(srcdir)/'`eom-application.c + +libeom_la-eom-session.lo: eom-session.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-session.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-session.Tpo -c -o libeom_la-eom-session.lo `test -f 'eom-session.c' || echo '$(srcdir)/'`eom-session.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-session.Tpo $(DEPDIR)/libeom_la-eom-session.Plo [email protected][email protected]@[email protected] source='eom-session.c' object='libeom_la-eom-session.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-session.lo `test -f 'eom-session.c' || echo '$(srcdir)/'`eom-session.c + +libeom_la-eom-debug.lo: eom-debug.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-debug.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-debug.Tpo -c -o libeom_la-eom-debug.lo `test -f 'eom-debug.c' || echo '$(srcdir)/'`eom-debug.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-debug.Tpo $(DEPDIR)/libeom_la-eom-debug.Plo [email protected][email protected]@[email protected] source='eom-debug.c' object='libeom_la-eom-debug.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-debug.lo `test -f 'eom-debug.c' || echo '$(srcdir)/'`eom-debug.c + +libeom_la-eom-util.lo: eom-util.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-util.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-util.Tpo -c -o libeom_la-eom-util.lo `test -f 'eom-util.c' || echo '$(srcdir)/'`eom-util.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-util.Tpo $(DEPDIR)/libeom_la-eom-util.Plo [email protected][email protected]@[email protected] source='eom-util.c' object='libeom_la-eom-util.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-util.lo `test -f 'eom-util.c' || echo '$(srcdir)/'`eom-util.c + +libeom_la-eom-pixbuf-util.lo: eom-pixbuf-util.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-pixbuf-util.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-pixbuf-util.Tpo -c -o libeom_la-eom-pixbuf-util.lo `test -f 'eom-pixbuf-util.c' || echo '$(srcdir)/'`eom-pixbuf-util.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-pixbuf-util.Tpo $(DEPDIR)/libeom_la-eom-pixbuf-util.Plo [email protected][email protected]@[email protected] source='eom-pixbuf-util.c' object='libeom_la-eom-pixbuf-util.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-pixbuf-util.lo `test -f 'eom-pixbuf-util.c' || echo '$(srcdir)/'`eom-pixbuf-util.c + +libeom_la-eom-window.lo: eom-window.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-window.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-window.Tpo -c -o libeom_la-eom-window.lo `test -f 'eom-window.c' || echo '$(srcdir)/'`eom-window.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-window.Tpo $(DEPDIR)/libeom_la-eom-window.Plo [email protected][email protected]@[email protected] source='eom-window.c' object='libeom_la-eom-window.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-window.lo `test -f 'eom-window.c' || echo '$(srcdir)/'`eom-window.c + +libeom_la-eom-sidebar.lo: eom-sidebar.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-sidebar.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-sidebar.Tpo -c -o libeom_la-eom-sidebar.lo `test -f 'eom-sidebar.c' || echo '$(srcdir)/'`eom-sidebar.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-sidebar.Tpo $(DEPDIR)/libeom_la-eom-sidebar.Plo [email protected][email protected]@[email protected] source='eom-sidebar.c' object='libeom_la-eom-sidebar.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-sidebar.lo `test -f 'eom-sidebar.c' || echo '$(srcdir)/'`eom-sidebar.c + +libeom_la-eom-dialog.lo: eom-dialog.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-dialog.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-dialog.Tpo -c -o libeom_la-eom-dialog.lo `test -f 'eom-dialog.c' || echo '$(srcdir)/'`eom-dialog.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-dialog.Tpo $(DEPDIR)/libeom_la-eom-dialog.Plo [email protected][email protected]@[email protected] source='eom-dialog.c' object='libeom_la-eom-dialog.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-dialog.lo `test -f 'eom-dialog.c' || echo '$(srcdir)/'`eom-dialog.c + +libeom_la-eom-preferences-dialog.lo: eom-preferences-dialog.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-preferences-dialog.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-preferences-dialog.Tpo -c -o libeom_la-eom-preferences-dialog.lo `test -f 'eom-preferences-dialog.c' || echo '$(srcdir)/'`eom-preferences-dialog.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-preferences-dialog.Tpo $(DEPDIR)/libeom_la-eom-preferences-dialog.Plo [email protected][email protected]@[email protected] source='eom-preferences-dialog.c' object='libeom_la-eom-preferences-dialog.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-preferences-dialog.lo `test -f 'eom-preferences-dialog.c' || echo '$(srcdir)/'`eom-preferences-dialog.c + +libeom_la-eom-properties-dialog.lo: eom-properties-dialog.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-properties-dialog.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-properties-dialog.Tpo -c -o libeom_la-eom-properties-dialog.lo `test -f 'eom-properties-dialog.c' || echo '$(srcdir)/'`eom-properties-dialog.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-properties-dialog.Tpo $(DEPDIR)/libeom_la-eom-properties-dialog.Plo [email protected][email protected]@[email protected] source='eom-properties-dialog.c' object='libeom_la-eom-properties-dialog.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-properties-dialog.lo `test -f 'eom-properties-dialog.c' || echo '$(srcdir)/'`eom-properties-dialog.c + +libeom_la-eom-error-message-area.lo: eom-error-message-area.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-error-message-area.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-error-message-area.Tpo -c -o libeom_la-eom-error-message-area.lo `test -f 'eom-error-message-area.c' || echo '$(srcdir)/'`eom-error-message-area.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-error-message-area.Tpo $(DEPDIR)/libeom_la-eom-error-message-area.Plo [email protected][email protected]@[email protected] source='eom-error-message-area.c' object='libeom_la-eom-error-message-area.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-error-message-area.lo `test -f 'eom-error-message-area.c' || echo '$(srcdir)/'`eom-error-message-area.c + +libeom_la-eom-file-chooser.lo: eom-file-chooser.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-file-chooser.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-file-chooser.Tpo -c -o libeom_la-eom-file-chooser.lo `test -f 'eom-file-chooser.c' || echo '$(srcdir)/'`eom-file-chooser.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-file-chooser.Tpo $(DEPDIR)/libeom_la-eom-file-chooser.Plo [email protected][email protected]@[email protected] source='eom-file-chooser.c' object='libeom_la-eom-file-chooser.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-file-chooser.lo `test -f 'eom-file-chooser.c' || echo '$(srcdir)/'`eom-file-chooser.c + +libeom_la-eom-statusbar.lo: eom-statusbar.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-statusbar.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-statusbar.Tpo -c -o libeom_la-eom-statusbar.lo `test -f 'eom-statusbar.c' || echo '$(srcdir)/'`eom-statusbar.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-statusbar.Tpo $(DEPDIR)/libeom_la-eom-statusbar.Plo [email protected][email protected]@[email protected] source='eom-statusbar.c' object='libeom_la-eom-statusbar.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-statusbar.lo `test -f 'eom-statusbar.c' || echo '$(srcdir)/'`eom-statusbar.c + +libeom_la-eom-thumb-nav.lo: eom-thumb-nav.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-thumb-nav.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-thumb-nav.Tpo -c -o libeom_la-eom-thumb-nav.lo `test -f 'eom-thumb-nav.c' || echo '$(srcdir)/'`eom-thumb-nav.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-thumb-nav.Tpo $(DEPDIR)/libeom_la-eom-thumb-nav.Plo [email protected][email protected]@[email protected] source='eom-thumb-nav.c' object='libeom_la-eom-thumb-nav.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-thumb-nav.lo `test -f 'eom-thumb-nav.c' || echo '$(srcdir)/'`eom-thumb-nav.c + +libeom_la-eom-transform.lo: eom-transform.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-transform.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-transform.Tpo -c -o libeom_la-eom-transform.lo `test -f 'eom-transform.c' || echo '$(srcdir)/'`eom-transform.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-transform.Tpo $(DEPDIR)/libeom_la-eom-transform.Plo [email protected][email protected]@[email protected] source='eom-transform.c' object='libeom_la-eom-transform.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-transform.lo `test -f 'eom-transform.c' || echo '$(srcdir)/'`eom-transform.c + +libeom_la-eom-image.lo: eom-image.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-image.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-image.Tpo -c -o libeom_la-eom-image.lo `test -f 'eom-image.c' || echo '$(srcdir)/'`eom-image.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-image.Tpo $(DEPDIR)/libeom_la-eom-image.Plo [email protected][email protected]@[email protected] source='eom-image.c' object='libeom_la-eom-image.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-image.lo `test -f 'eom-image.c' || echo '$(srcdir)/'`eom-image.c + +libeom_la-eom-image-jpeg.lo: eom-image-jpeg.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-image-jpeg.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-image-jpeg.Tpo -c -o libeom_la-eom-image-jpeg.lo `test -f 'eom-image-jpeg.c' || echo '$(srcdir)/'`eom-image-jpeg.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-image-jpeg.Tpo $(DEPDIR)/libeom_la-eom-image-jpeg.Plo [email protected][email protected]@[email protected] source='eom-image-jpeg.c' object='libeom_la-eom-image-jpeg.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-image-jpeg.lo `test -f 'eom-image-jpeg.c' || echo '$(srcdir)/'`eom-image-jpeg.c + +libeom_la-eom-image-save-info.lo: eom-image-save-info.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-image-save-info.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-image-save-info.Tpo -c -o libeom_la-eom-image-save-info.lo `test -f 'eom-image-save-info.c' || echo '$(srcdir)/'`eom-image-save-info.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-image-save-info.Tpo $(DEPDIR)/libeom_la-eom-image-save-info.Plo [email protected][email protected]@[email protected] source='eom-image-save-info.c' object='libeom_la-eom-image-save-info.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-image-save-info.lo `test -f 'eom-image-save-info.c' || echo '$(srcdir)/'`eom-image-save-info.c + +libeom_la-eom-scroll-view.lo: eom-scroll-view.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-scroll-view.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-scroll-view.Tpo -c -o libeom_la-eom-scroll-view.lo `test -f 'eom-scroll-view.c' || echo '$(srcdir)/'`eom-scroll-view.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-scroll-view.Tpo $(DEPDIR)/libeom_la-eom-scroll-view.Plo [email protected][email protected]@[email protected] source='eom-scroll-view.c' object='libeom_la-eom-scroll-view.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-scroll-view.lo `test -f 'eom-scroll-view.c' || echo '$(srcdir)/'`eom-scroll-view.c + +libeom_la-eom-thumb-view.lo: eom-thumb-view.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-thumb-view.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-thumb-view.Tpo -c -o libeom_la-eom-thumb-view.lo `test -f 'eom-thumb-view.c' || echo '$(srcdir)/'`eom-thumb-view.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-thumb-view.Tpo $(DEPDIR)/libeom_la-eom-thumb-view.Plo [email protected][email protected]@[email protected] source='eom-thumb-view.c' object='libeom_la-eom-thumb-view.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-thumb-view.lo `test -f 'eom-thumb-view.c' || echo '$(srcdir)/'`eom-thumb-view.c + +libeom_la-eom-list-store.lo: eom-list-store.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-list-store.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-list-store.Tpo -c -o libeom_la-eom-list-store.lo `test -f 'eom-list-store.c' || echo '$(srcdir)/'`eom-list-store.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-list-store.Tpo $(DEPDIR)/libeom_la-eom-list-store.Plo [email protected][email protected]@[email protected] source='eom-list-store.c' object='libeom_la-eom-list-store.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-list-store.lo `test -f 'eom-list-store.c' || echo '$(srcdir)/'`eom-list-store.c + +libeom_la-eom-thumbnail.lo: eom-thumbnail.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-thumbnail.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-thumbnail.Tpo -c -o libeom_la-eom-thumbnail.lo `test -f 'eom-thumbnail.c' || echo '$(srcdir)/'`eom-thumbnail.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-thumbnail.Tpo $(DEPDIR)/libeom_la-eom-thumbnail.Plo [email protected][email protected]@[email protected] source='eom-thumbnail.c' object='libeom_la-eom-thumbnail.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-thumbnail.lo `test -f 'eom-thumbnail.c' || echo '$(srcdir)/'`eom-thumbnail.c + +libeom_la-eom-job-queue.lo: eom-job-queue.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-job-queue.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-job-queue.Tpo -c -o libeom_la-eom-job-queue.lo `test -f 'eom-job-queue.c' || echo '$(srcdir)/'`eom-job-queue.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-job-queue.Tpo $(DEPDIR)/libeom_la-eom-job-queue.Plo [email protected][email protected]@[email protected] source='eom-job-queue.c' object='libeom_la-eom-job-queue.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-job-queue.lo `test -f 'eom-job-queue.c' || echo '$(srcdir)/'`eom-job-queue.c + +libeom_la-eom-jobs.lo: eom-jobs.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-jobs.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-jobs.Tpo -c -o libeom_la-eom-jobs.lo `test -f 'eom-jobs.c' || echo '$(srcdir)/'`eom-jobs.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-jobs.Tpo $(DEPDIR)/libeom_la-eom-jobs.Plo [email protected][email protected]@[email protected] source='eom-jobs.c' object='libeom_la-eom-jobs.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-jobs.lo `test -f 'eom-jobs.c' || echo '$(srcdir)/'`eom-jobs.c + +libeom_la-eom-uri-converter.lo: eom-uri-converter.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-uri-converter.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-uri-converter.Tpo -c -o libeom_la-eom-uri-converter.lo `test -f 'eom-uri-converter.c' || echo '$(srcdir)/'`eom-uri-converter.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-uri-converter.Tpo $(DEPDIR)/libeom_la-eom-uri-converter.Plo [email protected][email protected]@[email protected] source='eom-uri-converter.c' object='libeom_la-eom-uri-converter.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-uri-converter.lo `test -f 'eom-uri-converter.c' || echo '$(srcdir)/'`eom-uri-converter.c + +libeom_la-eom-metadata-reader.lo: eom-metadata-reader.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-metadata-reader.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-metadata-reader.Tpo -c -o libeom_la-eom-metadata-reader.lo `test -f 'eom-metadata-reader.c' || echo '$(srcdir)/'`eom-metadata-reader.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-metadata-reader.Tpo $(DEPDIR)/libeom_la-eom-metadata-reader.Plo [email protected][email protected]@[email protected] source='eom-metadata-reader.c' object='libeom_la-eom-metadata-reader.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-metadata-reader.lo `test -f 'eom-metadata-reader.c' || echo '$(srcdir)/'`eom-metadata-reader.c + +libeom_la-eom-metadata-reader-jpg.lo: eom-metadata-reader-jpg.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-metadata-reader-jpg.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-metadata-reader-jpg.Tpo -c -o libeom_la-eom-metadata-reader-jpg.lo `test -f 'eom-metadata-reader-jpg.c' || echo '$(srcdir)/'`eom-metadata-reader-jpg.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-metadata-reader-jpg.Tpo $(DEPDIR)/libeom_la-eom-metadata-reader-jpg.Plo [email protected][email protected]@[email protected] source='eom-metadata-reader-jpg.c' object='libeom_la-eom-metadata-reader-jpg.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-metadata-reader-jpg.lo `test -f 'eom-metadata-reader-jpg.c' || echo '$(srcdir)/'`eom-metadata-reader-jpg.c + +libeom_la-eom-metadata-reader-png.lo: eom-metadata-reader-png.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-metadata-reader-png.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-metadata-reader-png.Tpo -c -o libeom_la-eom-metadata-reader-png.lo `test -f 'eom-metadata-reader-png.c' || echo '$(srcdir)/'`eom-metadata-reader-png.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-metadata-reader-png.Tpo $(DEPDIR)/libeom_la-eom-metadata-reader-png.Plo [email protected][email protected]@[email protected] source='eom-metadata-reader-png.c' object='libeom_la-eom-metadata-reader-png.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-metadata-reader-png.lo `test -f 'eom-metadata-reader-png.c' || echo '$(srcdir)/'`eom-metadata-reader-png.c + +libeom_la-eom-save-as-dialog-helper.lo: eom-save-as-dialog-helper.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-save-as-dialog-helper.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-save-as-dialog-helper.Tpo -c -o libeom_la-eom-save-as-dialog-helper.lo `test -f 'eom-save-as-dialog-helper.c' || echo '$(srcdir)/'`eom-save-as-dialog-helper.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-save-as-dialog-helper.Tpo $(DEPDIR)/libeom_la-eom-save-as-dialog-helper.Plo [email protected][email protected]@[email protected] source='eom-save-as-dialog-helper.c' object='libeom_la-eom-save-as-dialog-helper.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-save-as-dialog-helper.lo `test -f 'eom-save-as-dialog-helper.c' || echo '$(srcdir)/'`eom-save-as-dialog-helper.c + +libeom_la-eom-print-image-setup.lo: eom-print-image-setup.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-print-image-setup.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-print-image-setup.Tpo -c -o libeom_la-eom-print-image-setup.lo `test -f 'eom-print-image-setup.c' || echo '$(srcdir)/'`eom-print-image-setup.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-print-image-setup.Tpo $(DEPDIR)/libeom_la-eom-print-image-setup.Plo [email protected][email protected]@[email protected] source='eom-print-image-setup.c' object='libeom_la-eom-print-image-setup.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-print-image-setup.lo `test -f 'eom-print-image-setup.c' || echo '$(srcdir)/'`eom-print-image-setup.c + +libeom_la-eom-print-preview.lo: eom-print-preview.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-print-preview.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-print-preview.Tpo -c -o libeom_la-eom-print-preview.lo `test -f 'eom-print-preview.c' || echo '$(srcdir)/'`eom-print-preview.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-print-preview.Tpo $(DEPDIR)/libeom_la-eom-print-preview.Plo [email protected][email protected]@[email protected] source='eom-print-preview.c' object='libeom_la-eom-print-preview.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-print-preview.lo `test -f 'eom-print-preview.c' || echo '$(srcdir)/'`eom-print-preview.c + +libeom_la-eom-print.lo: eom-print.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-print.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-print.Tpo -c -o libeom_la-eom-print.lo `test -f 'eom-print.c' || echo '$(srcdir)/'`eom-print.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-print.Tpo $(DEPDIR)/libeom_la-eom-print.Plo [email protected][email protected]@[email protected] source='eom-print.c' object='libeom_la-eom-print.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-print.lo `test -f 'eom-print.c' || echo '$(srcdir)/'`eom-print.c + +libeom_la-eom-module.lo: eom-module.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-module.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-module.Tpo -c -o libeom_la-eom-module.lo `test -f 'eom-module.c' || echo '$(srcdir)/'`eom-module.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-module.Tpo $(DEPDIR)/libeom_la-eom-module.Plo [email protected][email protected]@[email protected] source='eom-module.c' object='libeom_la-eom-module.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-module.lo `test -f 'eom-module.c' || echo '$(srcdir)/'`eom-module.c + +libeom_la-eom-close-confirmation-dialog.lo: eom-close-confirmation-dialog.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-close-confirmation-dialog.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-close-confirmation-dialog.Tpo -c -o libeom_la-eom-close-confirmation-dialog.lo `test -f 'eom-close-confirmation-dialog.c' || echo '$(srcdir)/'`eom-close-confirmation-dialog.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-close-confirmation-dialog.Tpo $(DEPDIR)/libeom_la-eom-close-confirmation-dialog.Plo [email protected][email protected]@[email protected] source='eom-close-confirmation-dialog.c' object='libeom_la-eom-close-confirmation-dialog.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-close-confirmation-dialog.lo `test -f 'eom-close-confirmation-dialog.c' || echo '$(srcdir)/'`eom-close-confirmation-dialog.c + +libeom_la-eom-plugin.lo: eom-plugin.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-plugin.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-plugin.Tpo -c -o libeom_la-eom-plugin.lo `test -f 'eom-plugin.c' || echo '$(srcdir)/'`eom-plugin.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-plugin.Tpo $(DEPDIR)/libeom_la-eom-plugin.Plo [email protected][email protected]@[email protected] source='eom-plugin.c' object='libeom_la-eom-plugin.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-plugin.lo `test -f 'eom-plugin.c' || echo '$(srcdir)/'`eom-plugin.c + +libeom_la-eom-plugin-manager.lo: eom-plugin-manager.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-plugin-manager.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-plugin-manager.Tpo -c -o libeom_la-eom-plugin-manager.lo `test -f 'eom-plugin-manager.c' || echo '$(srcdir)/'`eom-plugin-manager.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-plugin-manager.Tpo $(DEPDIR)/libeom_la-eom-plugin-manager.Plo [email protected][email protected]@[email protected] source='eom-plugin-manager.c' object='libeom_la-eom-plugin-manager.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-plugin-manager.lo `test -f 'eom-plugin-manager.c' || echo '$(srcdir)/'`eom-plugin-manager.c + +libeom_la-eom-plugin-engine.lo: eom-plugin-engine.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-plugin-engine.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-plugin-engine.Tpo -c -o libeom_la-eom-plugin-engine.lo `test -f 'eom-plugin-engine.c' || echo '$(srcdir)/'`eom-plugin-engine.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-plugin-engine.Tpo $(DEPDIR)/libeom_la-eom-plugin-engine.Plo [email protected][email protected]@[email protected] source='eom-plugin-engine.c' object='libeom_la-eom-plugin-engine.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-plugin-engine.lo `test -f 'eom-plugin-engine.c' || echo '$(srcdir)/'`eom-plugin-engine.c + +libeom_la-uta.lo: uta.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-uta.lo -MD -MP -MF $(DEPDIR)/libeom_la-uta.Tpo -c -o libeom_la-uta.lo `test -f 'uta.c' || echo '$(srcdir)/'`uta.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-uta.Tpo $(DEPDIR)/libeom_la-uta.Plo [email protected][email protected]@[email protected] source='uta.c' object='libeom_la-uta.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-uta.lo `test -f 'uta.c' || echo '$(srcdir)/'`uta.c + +libeom_la-zoom.lo: zoom.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-zoom.lo -MD -MP -MF $(DEPDIR)/libeom_la-zoom.Tpo -c -o libeom_la-zoom.lo `test -f 'zoom.c' || echo '$(srcdir)/'`zoom.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-zoom.Tpo $(DEPDIR)/libeom_la-zoom.Plo [email protected][email protected]@[email protected] source='zoom.c' object='libeom_la-zoom.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-zoom.lo `test -f 'zoom.c' || echo '$(srcdir)/'`zoom.c + +libeom_la-eom-enum-types.lo: eom-enum-types.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-enum-types.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-enum-types.Tpo -c -o libeom_la-eom-enum-types.lo `test -f 'eom-enum-types.c' || echo '$(srcdir)/'`eom-enum-types.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-enum-types.Tpo $(DEPDIR)/libeom_la-eom-enum-types.Plo [email protected][email protected]@[email protected] source='eom-enum-types.c' object='libeom_la-eom-enum-types.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-enum-types.lo `test -f 'eom-enum-types.c' || echo '$(srcdir)/'`eom-enum-types.c + +libeom_la-eom-marshal.lo: eom-marshal.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-marshal.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-marshal.Tpo -c -o libeom_la-eom-marshal.lo `test -f 'eom-marshal.c' || echo '$(srcdir)/'`eom-marshal.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-marshal.Tpo $(DEPDIR)/libeom_la-eom-marshal.Plo [email protected][email protected]@[email protected] source='eom-marshal.c' object='libeom_la-eom-marshal.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-marshal.lo `test -f 'eom-marshal.c' || echo '$(srcdir)/'`eom-marshal.c + +libeom_la-eom-exif-util.lo: eom-exif-util.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-exif-util.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-exif-util.Tpo -c -o libeom_la-eom-exif-util.lo `test -f 'eom-exif-util.c' || echo '$(srcdir)/'`eom-exif-util.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-exif-util.Tpo $(DEPDIR)/libeom_la-eom-exif-util.Plo [email protected][email protected]@[email protected] source='eom-exif-util.c' object='libeom_la-eom-exif-util.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-exif-util.lo `test -f 'eom-exif-util.c' || echo '$(srcdir)/'`eom-exif-util.c + +libeom_la-eom-exif-details.lo: eom-exif-details.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-exif-details.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-exif-details.Tpo -c -o libeom_la-eom-exif-details.lo `test -f 'eom-exif-details.c' || echo '$(srcdir)/'`eom-exif-details.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-exif-details.Tpo $(DEPDIR)/libeom_la-eom-exif-details.Plo [email protected][email protected]@[email protected] source='eom-exif-details.c' object='libeom_la-eom-exif-details.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-exif-details.lo `test -f 'eom-exif-details.c' || echo '$(srcdir)/'`eom-exif-details.c + +libeom_la-eom-python-module.lo: eom-python-module.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-python-module.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-python-module.Tpo -c -o libeom_la-eom-python-module.lo `test -f 'eom-python-module.c' || echo '$(srcdir)/'`eom-python-module.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-python-module.Tpo $(DEPDIR)/libeom_la-eom-python-module.Plo [email protected][email protected]@[email protected] source='eom-python-module.c' object='libeom_la-eom-python-module.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-python-module.lo `test -f 'eom-python-module.c' || echo '$(srcdir)/'`eom-python-module.c + +libeom_la-eom-python-plugin.lo: eom-python-plugin.c [email protected][email protected] $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -MT libeom_la-eom-python-plugin.lo -MD -MP -MF $(DEPDIR)/libeom_la-eom-python-plugin.Tpo -c -o libeom_la-eom-python-plugin.lo `test -f 'eom-python-plugin.c' || echo '$(srcdir)/'`eom-python-plugin.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-python-plugin.Tpo $(DEPDIR)/libeom_la-eom-python-plugin.Plo [email protected][email protected]@[email protected] source='eom-python-plugin.c' object='libeom_la-eom-python-plugin.lo' libtool=yes @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libeom_la_CFLAGS) $(CFLAGS) -c -o libeom_la-eom-python-plugin.lo `test -f 'eom-python-plugin.c' || echo '$(srcdir)/'`eom-python-plugin.c + +eom-main.o: main.c [email protected][email protected] $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(eom_CFLAGS) $(CFLAGS) -MT eom-main.o -MD -MP -MF $(DEPDIR)/eom-main.Tpo -c -o eom-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/eom-main.Tpo $(DEPDIR)/eom-main.Po [email protected][email protected]@[email protected] source='main.c' object='eom-main.o' libtool=no @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(eom_CFLAGS) $(CFLAGS) -c -o eom-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +eom-main.obj: main.c [email protected][email protected] $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(eom_CFLAGS) $(CFLAGS) -MT eom-main.obj -MD -MP -MF $(DEPDIR)/eom-main.Tpo -c -o eom-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` [email protected][email protected] $(AM_V_at)$(am__mv) $(DEPDIR)/eom-main.Tpo $(DEPDIR)/eom-main.Po [email protected][email protected]@[email protected] source='main.c' object='eom-main.obj' libtool=no @[email protected] [email protected][email protected]@[email protected] DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @[email protected] [email protected][email protected] $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(eom_CFLAGS) $(CFLAGS) -c -o eom-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-headerDATA: $(header_DATA) + @$(NORMAL_INSTALL) + test -z "$(headerdir)" || $(MKDIR_P) "$(DESTDIR)$(headerdir)" + @list='$(header_DATA)'; test -n "$(headerdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(headerdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(headerdir)" || exit $$?; \ + done + +uninstall-headerDATA: + @$(NORMAL_UNINSTALL) + @list='$(header_DATA)'; test -n "$(headerdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(headerdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(headerdir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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) \ + "[email protected]" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(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 + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(headerdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -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-headerDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +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 -rf ./$(DEPDIR) + -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-binPROGRAMS uninstall-headerDATA + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool clean-noinstLTLIBRARIES ctags \ + dist-hook distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-headerDATA install-html \ + install-html-am install-info install-info-am install-man \ + 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 uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-headerDATA + + +eom-enum-types.h: eom-enum-types.h.template $(INST_H_FILES) $(GLIB_MKENUMS) + $(AM_V_GEN)(cd $(srcdir) && $(GLIB_MKENUMS) --template eom-enum-types.h.template $(INST_H_FILES)) > [email protected] + +eom-enum-types.c: eom-enum-types.c.template $(INST_H_FILES) $(GLIB_MKENUMS) + $(AM_V_GEN)(cd $(srcdir) && $(GLIB_MKENUMS) --template eom-enum-types.c.template $(INST_H_FILES)) > [email protected] + +eom-marshal.h: eom-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --header --internal --prefix=eom_marshal > [email protected] + +eom-marshal.c: eom-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --body --header --prefix=eom_marshal > [email protected] + [email protected][email protected]: eom-application-service.xml [email protected][email protected] $(AM_V_GEN)dbus-binding-tool --prefix=eom_application --mode=glib-server --output=eom-application-service.h $< + +dist-hook: + cd $(distdir); rm -f $(BUILT_SOURCES) + +-include $(top_srcdir)/git.mk + +# 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/src/eom-application-service.xml b/src/eom-application-service.xml new file mode 100644 index 0000000..6089812 --- /dev/null +++ b/src/eom-application-service.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/org/mate/eom/Eom"> + + <interface name="org.mate.eom.Application"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="eom_application"/> + + <method name="OpenWindow"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="eom_application_open_window"/> + <arg type="u" name="timestamp" direction="in"/> + <arg type="y" name="flags" direction="in"/> + </method> + + <method name="OpenUris"> + <annotation name="org.freedesktop.DBus.Glib.CSymbol" value="eom_application_open_uris"/> + <arg type="as" name="uris" direction="in"/> + <arg type="u" name="timestamp" direction="in"/> + <arg type="y" name="flags" direction="in"/> + </method> + + </interface> + +</node> diff --git a/src/eom-application.c b/src/eom-application.c new file mode 100644 index 0000000..2da184d --- /dev/null +++ b/src/eom-application.c @@ -0,0 +1,566 @@ +/* Eye Of Mate - Application Facade + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on evince code (shell/ev-application.h) by: + * - Martin Kretzschmar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eom-image.h" +#include "eom-session.h" +#include "eom-window.h" +#include "eom-application.h" +#include "eom-util.h" + +#ifdef HAVE_DBUS +#include "totem-scrsaver.h" +#endif + +#include <string.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#ifdef HAVE_DBUS +#include "eom-application-service.h" +#include <dbus/dbus-glib-bindings.h> + +#define APPLICATION_SERVICE_NAME "org.mate.eom.ApplicationService" +#endif + +static void eom_application_load_accelerators (void); +static void eom_application_save_accelerators (void); + +#define EOM_APPLICATION_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_APPLICATION, EomApplicationPrivate)) + +G_DEFINE_TYPE (EomApplication, eom_application, G_TYPE_OBJECT); + +#ifdef HAVE_DBUS + +/** + * eom_application_register_service: + * @application: An #EomApplication. + * + * Registers #EomApplication<!-- -->'s DBus service, to allow + * remote calls. If the DBus service is already registered, + * or there is any other connection error, returns %FALSE. + * + * Returns: %TRUE if the service was registered succesfully. %FALSE + * otherwise. + **/ +gboolean +eom_application_register_service (EomApplication *application) +{ + static DBusGConnection *connection = NULL; + DBusGProxy *driver_proxy; + GError *err = NULL; + guint request_name_result; + + if (connection) { + g_warning ("Service already registered."); + return FALSE; + } + + connection = dbus_g_bus_get (DBUS_BUS_STARTER, &err); + + if (connection == NULL) { + g_warning ("Service registration failed."); + g_error_free (err); + + return FALSE; + } + + driver_proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + if (!org_freedesktop_DBus_request_name (driver_proxy, + APPLICATION_SERVICE_NAME, + DBUS_NAME_FLAG_DO_NOT_QUEUE, + &request_name_result, &err)) { + g_warning ("Service registration failed."); + g_clear_error (&err); + } + + g_object_unref (driver_proxy); + + if (request_name_result == DBUS_REQUEST_NAME_REPLY_EXISTS) { + return FALSE; + } + + dbus_g_object_type_install_info (EOM_TYPE_APPLICATION, + &dbus_glib_eom_application_object_info); + + dbus_g_connection_register_g_object (connection, + "/org/mate/eom/Eom", + G_OBJECT (application)); + + application->scr_saver = totem_scrsaver_new (); + g_object_set (application->scr_saver, + "reason", _("Running in fullscreen mode"), + NULL); + + return TRUE; +} +#endif /* ENABLE_DBUS */ + +static void +eom_application_class_init (EomApplicationClass *eom_application_class) +{ +} + +static void +eom_application_init (EomApplication *eom_application) +{ + const gchar *dot_dir = eom_util_dot_dir (); + + eom_session_init (eom_application); + + eom_application->toolbars_model = egg_toolbars_model_new (); + + egg_toolbars_model_load_names (eom_application->toolbars_model, + EOM_DATA_DIR "/eom-toolbar.xml"); + + if (G_LIKELY (dot_dir != NULL)) + eom_application->toolbars_file = g_build_filename + (dot_dir, "eom_toolbar.xml", NULL); + + if (!dot_dir || !egg_toolbars_model_load_toolbars (eom_application->toolbars_model, + eom_application->toolbars_file)) { + + egg_toolbars_model_load_toolbars (eom_application->toolbars_model, + EOM_DATA_DIR "/eom-toolbar.xml"); + } + + egg_toolbars_model_set_flags (eom_application->toolbars_model, 0, + EGG_TB_MODEL_NOT_REMOVABLE); + + eom_application_load_accelerators (); +} + +/** + * eom_application_get_instance: + * + * Returns a singleton instance of #EomApplication currently running. + * If not running yet, it will create one. + * + * Returns: a running #EomApplication. + **/ +EomApplication * +eom_application_get_instance (void) +{ + static EomApplication *instance; + + if (!instance) { + instance = EOM_APPLICATION (g_object_new (EOM_TYPE_APPLICATION, NULL)); + } + + return instance; +} + +static EomWindow * +eom_application_get_empty_window (EomApplication *application) +{ + EomWindow *empty_window = NULL; + GList *windows; + GList *l; + + g_return_val_if_fail (EOM_IS_APPLICATION (application), NULL); + + windows = eom_application_get_windows (application); + + for (l = windows; l != NULL; l = l->next) { + EomWindow *window = EOM_WINDOW (l->data); + + if (eom_window_is_empty (window)) { + empty_window = window; + break; + } + } + + g_list_free (windows); + + return empty_window; +} + +/** + * eom_application_open_window: + * @application: An #EomApplication. + * @timestamp: The timestamp of the user interaction which triggered this call + * (see gtk_window_present_with_time()). + * @flags: A set of #EomStartupFlags influencing a new windows' state. + * @error: Return location for a #GError, or NULL to ignore errors. + * + * Opens and presents an empty #EomWindow to the user. If there is + * an empty window already open, this will be used. Otherwise, a + * new one will be instantiated. + * + * Returns: %FALSE if @application is invalid, %TRUE otherwise + **/ +gboolean +eom_application_open_window (EomApplication *application, + guint32 timestamp, + EomStartupFlags flags, + GError **error) +{ + GtkWidget *new_window = NULL; + + new_window = GTK_WIDGET (eom_application_get_empty_window (application)); + + if (new_window == NULL) { + new_window = eom_window_new (flags); + } + + g_return_val_if_fail (EOM_IS_APPLICATION (application), FALSE); + + gtk_window_present_with_time (GTK_WINDOW (new_window), + timestamp); + + return TRUE; +} + +static EomWindow * +eom_application_get_file_window (EomApplication *application, GFile *file) +{ + EomWindow *file_window = NULL; + GList *windows; + GList *l; + + g_return_val_if_fail (file != NULL, NULL); + g_return_val_if_fail (EOM_IS_APPLICATION (application), NULL); + + windows = gtk_window_list_toplevels (); + + for (l = windows; l != NULL; l = l->next) { + if (EOM_IS_WINDOW (l->data)) { + EomWindow *window = EOM_WINDOW (l->data); + + if (!eom_window_is_empty (window)) { + EomImage *image = eom_window_get_image (window); + GFile *window_file; + + window_file = eom_image_get_file (image); + if (g_file_equal (window_file, file)) { + file_window = window; + break; + } + } + } + } + + g_list_free (windows); + + return file_window; +} + +static void +eom_application_show_window (EomWindow *window, gpointer user_data) +{ + gtk_window_present_with_time (GTK_WINDOW (window), + GPOINTER_TO_UINT (user_data)); +} + +/** + * eom_application_open_file_list: + * @application: An #EomApplication. + * @file_list: A list of #GFile<!-- -->s. + * @timestamp: The timestamp of the user interaction which triggered this call + * (see gtk_window_present_with_time()). + * @flags: A set of #EomStartupFlags influencing a new windows' state. + * @error: Return location for a #GError, or NULL to ignore errors. + * + * Opens a list of files in a #EomWindow. If an #EomWindow displaying the first + * image in the list is already open, this will be used. Otherwise, an empty + * #EomWindow is used, either already existing or newly created. + * + * Returns: Currently always %TRUE. + **/ +gboolean +eom_application_open_file_list (EomApplication *application, + GSList *file_list, + guint timestamp, + EomStartupFlags flags, + GError **error) +{ + EomWindow *new_window = NULL; + + if (file_list != NULL) + new_window = eom_application_get_file_window (application, + (GFile *) file_list->data); + + if (new_window != NULL) { + gtk_window_present_with_time (GTK_WINDOW (new_window), + timestamp); + return TRUE; + } + + new_window = eom_application_get_empty_window (application); + + if (new_window == NULL) { + new_window = EOM_WINDOW (eom_window_new (flags)); + } + + g_signal_connect (new_window, + "prepared", + G_CALLBACK (eom_application_show_window), + GUINT_TO_POINTER (timestamp)); + + eom_window_open_file_list (new_window, file_list); + + return TRUE; +} + +/** + * eom_application_open_uri_list: + * @application: An #EomApplication. + * @uri_list: A list of URIs. + * @timestamp: The timestamp of the user interaction which triggered this call + * (see gtk_window_present_with_time()). + * @flags: A set of #EomStartupFlags influencing a new windows' state. + * @error: Return location for a #GError, or NULL to ignore errors. + * + * Opens a list of images, from a list of URIs. See + * eom_application_open_file_list() for details. + * + * Returns: Currently always %TRUE. + **/ +gboolean +eom_application_open_uri_list (EomApplication *application, + GSList *uri_list, + guint timestamp, + EomStartupFlags flags, + GError **error) +{ + GSList *file_list = NULL; + + g_return_val_if_fail (EOM_IS_APPLICATION (application), FALSE); + + file_list = eom_util_string_list_to_file_list (uri_list); + + return eom_application_open_file_list (application, + file_list, + timestamp, + flags, + error); +} + +#ifdef HAVE_DBUS +/** + * eom_application_open_uris: + * @application: an #EomApplication + * @uris: A #GList of URI strings. + * @timestamp: The timestamp of the user interaction which triggered this call + * (see gtk_window_present_with_time()). + * @flags: A set of #EomStartupFlags influencing a new windows' state. + * @error: Return location for a #GError, or NULL to ignore errors. + * + * Opens a list of images, from a list of URI strings. See + * eom_application_open_file_list() for details. + * + * Returns: Currently always %TRUE. + **/ +gboolean +eom_application_open_uris (EomApplication *application, + gchar **uris, + guint timestamp, + EomStartupFlags flags, + GError **error) +{ + GSList *file_list = NULL; + + file_list = eom_util_strings_to_file_list (uris); + + return eom_application_open_file_list (application, file_list, timestamp, + flags, error); +} +#endif + +/** + * eom_application_shutdown: + * @application: An #EomApplication. + * + * Takes care of shutting down the Eye of MATE, and quits. + **/ +void +eom_application_shutdown (EomApplication *application) +{ + g_return_if_fail (EOM_IS_APPLICATION (application)); + + if (application->toolbars_model) { + g_object_unref (application->toolbars_model); + application->toolbars_model = NULL; + + g_free (application->toolbars_file); + application->toolbars_file = NULL; + } + + eom_application_save_accelerators (); + + g_object_unref (application); + + gtk_main_quit (); +} + +/** + * eom_application_get_windows: + * @application: An #EomApplication. + * + * Gets the list of existing #EomApplication<!-- -->s. The windows + * in this list are not individually referenced, you need to keep + * your own references if you want to perform actions that may destroy + * them. + * + * Returns: A new list of #EomWindow<!-- -->s. + **/ +GList * +eom_application_get_windows (EomApplication *application) +{ + GList *l, *toplevels; + GList *windows = NULL; + + g_return_val_if_fail (EOM_IS_APPLICATION (application), NULL); + + toplevels = gtk_window_list_toplevels (); + + for (l = toplevels; l != NULL; l = l->next) { + if (EOM_IS_WINDOW (l->data)) { + windows = g_list_append (windows, l->data); + } + } + + g_list_free (toplevels); + + return windows; +} + +/** + * eom_application_get_toolbars_model: + * @application: An #EomApplication. + * + * Retrieves the #EggToolbarsModel for the toolbar in #EomApplication. + * + * Returns: An #EggToolbarsModel. + **/ +EggToolbarsModel * +eom_application_get_toolbars_model (EomApplication *application) +{ + g_return_val_if_fail (EOM_IS_APPLICATION (application), NULL); + + return application->toolbars_model; +} + +/** + * eom_application_save_toolbars_model: + * @application: An #EomApplication. + * + * Causes the saving of the model of the toolbar in #EomApplication to a file. + **/ +void +eom_application_save_toolbars_model (EomApplication *application) +{ + if (G_LIKELY(application->toolbars_file != NULL)) + egg_toolbars_model_save_toolbars (application->toolbars_model, + application->toolbars_file, + "1.0"); +} + +/** + * eom_application_reset_toolbars_model: + * @app: an #EomApplication + * + * Restores the toolbars model to the defaults. + **/ +void +eom_application_reset_toolbars_model (EomApplication *app) +{ + g_return_if_fail (EOM_IS_APPLICATION (app)); + + g_object_unref (app->toolbars_model); + + app->toolbars_model = egg_toolbars_model_new (); + + egg_toolbars_model_load_names (app->toolbars_model, + EOM_DATA_DIR "/eom-toolbar.xml"); + egg_toolbars_model_load_toolbars (app->toolbars_model, + EOM_DATA_DIR "/eom-toolbar.xml"); + egg_toolbars_model_set_flags (app->toolbars_model, 0, + EGG_TB_MODEL_NOT_REMOVABLE); +} + +#ifdef HAVE_DBUS +/** + * eom_application_screensaver_enable: + * @application: an #EomApplication. + * + * Enables the screensaver. Usually necessary after a call to + * eom_application_screensaver_disable(). + **/ +void +eom_application_screensaver_enable (EomApplication *application) +{ + if (application->scr_saver) + totem_scrsaver_enable (application->scr_saver); +} + +/** + * eom_application_screensaver_disable: + * @application: an #EomApplication. + * + * Disables the screensaver. Useful when the application is in fullscreen or + * similar mode. + **/ +void +eom_application_screensaver_disable (EomApplication *application) +{ + if (application->scr_saver) + totem_scrsaver_disable (application->scr_saver); +} +#endif + +static void +eom_application_load_accelerators (void) +{ + gchar *accelfile = g_build_filename (g_get_home_dir (), + ".mate2", + "accels", + "eom", NULL); + + /* gtk_accel_map_load does nothing if the file does not exist */ + gtk_accel_map_load (accelfile); + g_free (accelfile); +} + +static void +eom_application_save_accelerators (void) +{ + gchar *accelfile = g_build_filename (g_get_home_dir (), + ".mate2", + "accels", + "eom", NULL); + + gtk_accel_map_save (accelfile); + g_free (accelfile); +} diff --git a/src/eom-application.h b/src/eom-application.h new file mode 100644 index 0000000..8f4485a --- /dev/null +++ b/src/eom-application.h @@ -0,0 +1,118 @@ +/* Eye Of Mate - Application Facade + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on evince code (shell/ev-application.h) by: + * - Martin Kretzschmar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_APPLICATION_H__ +#define __EOM_APPLICATION_H__ + +#include "eom-window.h" +#include "egg-toolbars-model.h" + +#ifdef HAVE_DBUS +#include "totem-scrsaver.h" +#endif + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _EomApplication EomApplication; +typedef struct _EomApplicationClass EomApplicationClass; +typedef struct _EomApplicationPrivate EomApplicationPrivate; + +#define EOM_TYPE_APPLICATION (eom_application_get_type ()) +#define EOM_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_APPLICATION, EomApplication)) +#define EOM_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_APPLICATION, EomApplicationClass)) +#define EOM_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_APPLICATION)) +#define EOM_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_APPLICATION)) +#define EOM_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_APPLICATION, EomApplicationClass)) + +#define EOM_APP (eom_application_get_instance ()) + +struct _EomApplication { + GObject base_instance; + + EggToolbarsModel *toolbars_model; + gchar *toolbars_file; +#ifdef HAVE_DBUS + TotemScrsaver *scr_saver; +#endif +}; + +struct _EomApplicationClass { + GObjectClass parent_class; +}; + +GType eom_application_get_type (void) G_GNUC_CONST; + +EomApplication *eom_application_get_instance (void); + +#ifdef HAVE_DBUS +gboolean eom_application_register_service (EomApplication *application); +#endif + +void eom_application_shutdown (EomApplication *application); + +gboolean eom_application_open_window (EomApplication *application, + guint timestamp, + EomStartupFlags flags, + GError **error); + +gboolean eom_application_open_uri_list (EomApplication *application, + GSList *uri_list, + guint timestamp, + EomStartupFlags flags, + GError **error); + +gboolean eom_application_open_file_list (EomApplication *application, + GSList *file_list, + guint timestamp, + EomStartupFlags flags, + GError **error); + +#ifdef HAVE_DBUS +gboolean eom_application_open_uris (EomApplication *application, + gchar **uris, + guint timestamp, + EomStartupFlags flags, + GError **error); +#endif + +GList *eom_application_get_windows (EomApplication *application); + +EggToolbarsModel *eom_application_get_toolbars_model (EomApplication *application); + +void eom_application_save_toolbars_model (EomApplication *application); + +void eom_application_reset_toolbars_model (EomApplication *app); + +#ifdef HAVE_DBUS +void eom_application_screensaver_enable (EomApplication *application); + +void eom_application_screensaver_disable (EomApplication *application); +#endif + +G_END_DECLS + +#endif /* __EOM_APPLICATION_H__ */ diff --git a/src/eom-close-confirmation-dialog.c b/src/eom-close-confirmation-dialog.c new file mode 100644 index 0000000..0798c5e --- /dev/null +++ b/src/eom-close-confirmation-dialog.c @@ -0,0 +1,698 @@ +/* + * eom-close-confirmation-dialog.c + * This file is part of eom + * + * Author: Marcus Carlson <[email protected]> + * + * Based on gedit code (gedit/gedit-close-confirmation.c) by gedit Team + * + * Copyright (C) 2004-2009 MATE Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "eom-close-confirmation-dialog.h" +#include <eom-window.h> + +/* Properties */ +enum +{ + PROP_0, + PROP_UNSAVED_IMAGES +}; + +/* Mode */ +enum +{ + SINGLE_IMG_MODE, + MULTIPLE_IMGS_MODE +}; + +/* Columns */ +enum +{ + SAVE_COLUMN, + IMAGE_COLUMN, + NAME_COLUMN, + IMG_COLUMN, /* a handy pointer to the image */ + N_COLUMNS +}; + +struct _EomCloseConfirmationDialogPrivate +{ + GList *unsaved_images; + + GList *selected_images; + + GtkTreeModel *list_store; + GtkCellRenderer *toggle_renderer; +}; + + +#define EOM_CLOSE_CONFIRMATION_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, \ + EomCloseConfirmationDialogPrivate)) + +#define GET_MODE(priv) (((priv->unsaved_images != NULL) && \ + (priv->unsaved_images->next == NULL)) ? \ + SINGLE_IMG_MODE : MULTIPLE_IMGS_MODE) + +#define IMAGE_COLUMN_HEIGHT 40 + +G_DEFINE_TYPE(EomCloseConfirmationDialog, eom_close_confirmation_dialog, GTK_TYPE_DIALOG) + +static void set_unsaved_image (EomCloseConfirmationDialog *dlg, + const GList *list); + +static GList *get_selected_imgs (GtkTreeModel *store); + +static GdkPixbuf * +eom_close_confirmation_dialog_get_icon (const gchar *icon_name) +{ + GError *error = NULL; + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf; + + icon_theme = gtk_icon_theme_get_default (); + + pixbuf = gtk_icon_theme_load_icon (icon_theme, + icon_name, + IMAGE_COLUMN_HEIGHT, + 0, + &error); + + if (!pixbuf) { + g_warning ("Couldn't load icon: %s", error->message); + g_error_free (error); + } + + return pixbuf; +} + +static GdkPixbuf* +get_nothumb_pixbuf (void) +{ + static GOnce nothumb_once = G_ONCE_INIT; + g_once (¬humb_once, (GThreadFunc) eom_close_confirmation_dialog_get_icon, "image-x-generic"); + return GDK_PIXBUF (g_object_ref (nothumb_once.retval)); +} + +/* Since we connect in the costructor we are sure this handler will be called + * before the user ones + */ +static void +response_cb (EomCloseConfirmationDialog *dlg, + gint response_id, + gpointer data) +{ + EomCloseConfirmationDialogPrivate *priv; + + g_return_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg)); + + priv = dlg->priv; + + if (priv->selected_images != NULL) + g_list_free (priv->selected_images); + + if (response_id == GTK_RESPONSE_YES) + { + if (GET_MODE (priv) == SINGLE_IMG_MODE) + { + priv->selected_images = + g_list_copy (priv->unsaved_images); + } + else + { + g_return_if_fail (priv->list_store); + + priv->selected_images = + get_selected_imgs (priv->list_store); + } + } + else + priv->selected_images = NULL; +} + +static void +add_buttons (EomCloseConfirmationDialog *dlg) +{ + gtk_dialog_add_button (GTK_DIALOG (dlg), + _("Close _without Saving"), + GTK_RESPONSE_NO); + + gtk_dialog_add_button (GTK_DIALOG (dlg), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + gtk_dialog_add_button (GTK_DIALOG (dlg), + GTK_STOCK_SAVE, + GTK_RESPONSE_YES); + + gtk_dialog_set_default_response (GTK_DIALOG (dlg), + GTK_RESPONSE_YES); +} + +static void +eom_close_confirmation_dialog_init (EomCloseConfirmationDialog *dlg) +{ + AtkObject *atk_obj; + + dlg->priv = EOM_CLOSE_CONFIRMATION_DIALOG_GET_PRIVATE (dlg); + + gtk_container_set_border_width (GTK_CONTAINER (dlg), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), 14); + gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); + gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dlg), TRUE); + + gtk_window_set_title (GTK_WINDOW (dlg), ""); + + gtk_window_set_modal (GTK_WINDOW (dlg), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE); + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (dlg)); + atk_object_set_role (atk_obj, ATK_ROLE_ALERT); + atk_object_set_name (atk_obj, _("Question")); + + g_signal_connect (dlg, + "response", + G_CALLBACK (response_cb), + NULL); +} + +static void +eom_close_confirmation_dialog_finalize (GObject *object) +{ + EomCloseConfirmationDialogPrivate *priv; + + priv = EOM_CLOSE_CONFIRMATION_DIALOG (object)->priv; + + if (priv->unsaved_images != NULL) + g_list_free (priv->unsaved_images); + + if (priv->selected_images != NULL) + g_list_free (priv->selected_images); + + /* Call the parent's destructor */ + G_OBJECT_CLASS (eom_close_confirmation_dialog_parent_class)->finalize (object); +} + +static void +eom_close_confirmation_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EomCloseConfirmationDialog *dlg; + + dlg = EOM_CLOSE_CONFIRMATION_DIALOG (object); + + switch (prop_id) + { + case PROP_UNSAVED_IMAGES: + set_unsaved_image (dlg, g_value_get_pointer (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +eom_close_confirmation_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EomCloseConfirmationDialogPrivate *priv; + + priv = EOM_CLOSE_CONFIRMATION_DIALOG (object)->priv; + + switch( prop_id ) + { + case PROP_UNSAVED_IMAGES: + g_value_set_pointer (value, priv->unsaved_images); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +eom_close_confirmation_dialog_class_init (EomCloseConfirmationDialogClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = eom_close_confirmation_dialog_set_property; + gobject_class->get_property = eom_close_confirmation_dialog_get_property; + gobject_class->finalize = eom_close_confirmation_dialog_finalize; + + g_type_class_add_private (klass, sizeof (EomCloseConfirmationDialogPrivate)); + + g_object_class_install_property (gobject_class, + PROP_UNSAVED_IMAGES, + g_param_spec_pointer ("unsaved_images", + "Unsaved Images", + "List of Unsaved Images", + (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY))); +} + +static GList * +get_selected_imgs (GtkTreeModel *store) +{ + GList *list; + gboolean valid; + GtkTreeIter iter; + + list = NULL; + valid = gtk_tree_model_get_iter_first (store, &iter); + + while (valid) + { + gboolean to_save; + EomImage *img; + + gtk_tree_model_get (store, &iter, + SAVE_COLUMN, &to_save, + IMG_COLUMN, &img, + -1); + if (to_save) + list = g_list_prepend (list, img); + + valid = gtk_tree_model_iter_next (store, &iter); + } + + list = g_list_reverse (list); + + return list; +} + +GList * +eom_close_confirmation_dialog_get_selected_images (EomCloseConfirmationDialog *dlg) +{ + g_return_val_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL); + + return g_list_copy (dlg->priv->selected_images); +} + +GtkWidget * +eom_close_confirmation_dialog_new (GtkWindow *parent, + GList *unsaved_images) +{ + GtkWidget *dlg; + GtkWindowGroup *wg; + + g_return_val_if_fail (unsaved_images != NULL, NULL); + + dlg = GTK_WIDGET (g_object_new (EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, + "unsaved_images", unsaved_images, + NULL)); + g_return_val_if_fail (dlg != NULL, NULL); + + if (parent != NULL) + { + wg = gtk_window_get_group (parent); + + /* gtk_window_get_group returns a default group when the given + * window doesn't have a group. Explicitly add the window to + * the group here to make sure it's actually in the returned + * group. It makes no difference if it is already. */ + gtk_window_group_add_window (wg, parent); + gtk_window_group_add_window (wg, GTK_WINDOW (dlg)); + + gtk_window_set_transient_for (GTK_WINDOW (dlg), parent); + } + + return dlg; +} + +GtkWidget * +eom_close_confirmation_dialog_new_single (GtkWindow *parent, + EomImage *image) +{ + GtkWidget *dlg; + GList *unsaved_images; + g_return_val_if_fail (image != NULL, NULL); + + unsaved_images = g_list_prepend (NULL, image); + + dlg = eom_close_confirmation_dialog_new (parent, + unsaved_images); + + g_list_free (unsaved_images); + + return dlg; +} + +static gchar * +get_text_secondary_label (EomImage *image) +{ + gchar *secondary_msg; + + secondary_msg = g_strdup (_("If you don't save, your changes will be lost.")); + + return secondary_msg; +} + +static void +build_single_img_dialog (EomCloseConfirmationDialog *dlg) +{ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *primary_label; + GtkWidget *secondary_label; + GtkWidget *image; + EomImage *img; + const gchar *image_name; + gchar *str; + gchar *markup_str; + + g_return_if_fail (dlg->priv->unsaved_images->data != NULL); + img = EOM_IMAGE (dlg->priv->unsaved_images->data); + + /* Image */ + image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + + /* Primary label */ + primary_label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); + gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (primary_label), 0.0, 0.5); + gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + + image_name = eom_image_get_caption (img); + + str = g_markup_printf_escaped (_("Save changes to image \"%s\" before closing?"), + image_name); + markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL); + g_free (str); + + gtk_label_set_markup (GTK_LABEL (primary_label), markup_str); + g_free (markup_str); + + /* Secondary label */ + str = get_text_secondary_label (img); + + secondary_label = gtk_label_new (str); + g_free (str); + gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (secondary_label), 0.0, 0.5); + gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), secondary_label, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), + hbox, + FALSE, + FALSE, + 0); + + add_buttons (dlg); + + gtk_widget_show_all (hbox); +} + +static void +populate_model (GtkTreeModel *store, GList *imgs) +{ + GtkTreeIter iter; + + while (imgs != NULL) + { + EomImage *img; + const gchar *name; + GdkPixbuf *buf = NULL; + GdkPixbuf *buf_scaled = NULL; + int width; + double ratio; + + img = EOM_IMAGE (imgs->data); + + name = eom_image_get_caption (img); + buf = eom_image_get_thumbnail (img); + + if (buf) { + ratio = IMAGE_COLUMN_HEIGHT / (double) gdk_pixbuf_get_height (buf); + width = (int) (gdk_pixbuf_get_width (buf) * ratio); + buf_scaled = gdk_pixbuf_scale_simple (buf, width, IMAGE_COLUMN_HEIGHT, GDK_INTERP_BILINEAR); + } else + buf_scaled = get_nothumb_pixbuf (); + + gtk_list_store_append (GTK_LIST_STORE (store), &iter); + gtk_list_store_set (GTK_LIST_STORE (store), &iter, + SAVE_COLUMN, TRUE, + IMAGE_COLUMN, buf_scaled, + NAME_COLUMN, name, + IMG_COLUMN, img, + -1); + + imgs = g_list_next (imgs); + g_object_unref (buf_scaled); + } +} + +static void +save_toggled (GtkCellRendererToggle *renderer, gchar *path_str, GtkTreeModel *store) +{ + GtkTreePath *path = gtk_tree_path_new_from_string (path_str); + GtkTreeIter iter; + gboolean active; + + gtk_tree_model_get_iter (store, &iter, path); + gtk_tree_model_get (store, &iter, SAVE_COLUMN, &active, -1); + + active ^= 1; + + gtk_list_store_set (GTK_LIST_STORE (store), &iter, + SAVE_COLUMN, active, -1); + + gtk_tree_path_free (path); +} + +static GtkWidget * +create_treeview (EomCloseConfirmationDialogPrivate *priv) +{ + GtkListStore *store; + GtkWidget *treeview; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + treeview = gtk_tree_view_new (); + gtk_widget_set_size_request (treeview, 260, 120); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); + gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), FALSE); + + /* Create and populate the model */ + store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); + populate_model (GTK_TREE_MODEL (store), priv->unsaved_images); + + /* Set model to the treeview */ + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); + g_object_unref (store); + + priv->list_store = GTK_TREE_MODEL (store); + + /* Add columns */ + priv->toggle_renderer = renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect (renderer, "toggled", + G_CALLBACK (save_toggled), store); + + column = gtk_tree_view_column_new_with_attributes ("Save?", + renderer, + "active", + SAVE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + renderer = gtk_cell_renderer_pixbuf_new (); + + column = gtk_tree_view_column_new_with_attributes ("Image", + renderer, + "pixbuf", + IMAGE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Name", + renderer, + "text", + NAME_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + return treeview; +} + +static void +build_multiple_imgs_dialog (EomCloseConfirmationDialog *dlg) +{ + EomCloseConfirmationDialogPrivate *priv; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *vbox; + GtkWidget *primary_label; + GtkWidget *vbox2; + GtkWidget *select_label; + GtkWidget *scrolledwindow; + GtkWidget *treeview; + GtkWidget *secondary_label; + gchar *str; + gchar *markup_str; + + priv = dlg->priv; + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), + hbox, TRUE, TRUE, 0); + + /* Image */ + image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + + /* Primary label */ + primary_label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); + gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (primary_label), 0.0, 0.5); + gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + + str = g_strdup_printf ( + ngettext ("There is %d image with unsaved changes. " + "Save changes before closing?", + "There are %d images with unsaved changes. " + "Save changes before closing?", + g_list_length (priv->unsaved_images)), + g_list_length (priv->unsaved_images)); + + markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL); + g_free (str); + + gtk_label_set_markup (GTK_LABEL (primary_label), markup_str); + g_free (markup_str); + gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0); + + vbox2 = gtk_vbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + + select_label = gtk_label_new_with_mnemonic (_("S_elect the images you want to save:")); + + gtk_box_pack_start (GTK_BOX (vbox2), select_label, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (select_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (select_label), 0.0, 0.5); + + scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), + GTK_SHADOW_IN); + + treeview = create_treeview (priv); + gtk_container_add (GTK_CONTAINER (scrolledwindow), treeview); + + /* Secondary label */ + secondary_label = gtk_label_new (_("If you don't save, " + "all your changes will be lost.")); + + gtk_box_pack_start (GTK_BOX (vbox2), secondary_label, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); + gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + + gtk_label_set_mnemonic_widget (GTK_LABEL (select_label), treeview); + + add_buttons (dlg); + + gtk_widget_show_all (hbox); +} + +static void +set_unsaved_image (EomCloseConfirmationDialog *dlg, + const GList *list) +{ + EomCloseConfirmationDialogPrivate *priv; + + g_return_if_fail (list != NULL); + + priv = dlg->priv; + g_return_if_fail (priv->unsaved_images == NULL); + + priv->unsaved_images = g_list_copy ((GList *)list); + + if (GET_MODE (priv) == SINGLE_IMG_MODE) + { + build_single_img_dialog (dlg); + } + else + { + build_multiple_imgs_dialog (dlg); + } +} + +const GList * +eom_close_confirmation_dialog_get_unsaved_images (EomCloseConfirmationDialog *dlg) +{ + g_return_val_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL); + + return dlg->priv->unsaved_images; +} + +void +eom_close_confirmation_dialog_set_sensitive (EomCloseConfirmationDialog *dlg, + gboolean value) +{ + GtkWidget *action_area; + + g_return_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg)); + + action_area = gtk_dialog_get_action_area (GTK_DIALOG (dlg)); + gtk_widget_set_sensitive (action_area, value); + + if (dlg->priv->toggle_renderer) + gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (dlg->priv->toggle_renderer), value); +} diff --git a/src/eom-close-confirmation-dialog.h b/src/eom-close-confirmation-dialog.h new file mode 100644 index 0000000..3c5f44e --- /dev/null +++ b/src/eom-close-confirmation-dialog.h @@ -0,0 +1,79 @@ +/* + * eom-close-confirmation-dialog.h + * This file is part of eom + * + * Author: Marcus Carlson <[email protected]> + * + * Based on gedit code (gedit/gedit-close-confirmation.h) by gedit Team + * + * Copyright (C) 2004-2009 MATE Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_CLOSE_CONFIRMATION_DIALOG_H__ +#define __EOM_CLOSE_CONFIRMATION_DIALOG_H__ + +#include <glib.h> +#include <gtk/gtk.h> + +#include <eom-image.h> + +#define EOM_TYPE_CLOSE_CONFIRMATION_DIALOG (eom_close_confirmation_dialog_get_type ()) +#define EOM_CLOSE_CONFIRMATION_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, EomCloseConfirmationDialog)) +#define EOM_CLOSE_CONFIRMATION_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, EomCloseConfirmationDialogClass)) +#define EOM_IS_CLOSE_CONFIRMATION_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG)) +#define EOM_IS_CLOSE_CONFIRMATION_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG)) +#define EOM_CLOSE_CONFIRMATION_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, EomCloseConfirmationDialogClass)) + +typedef struct _EomCloseConfirmationDialog EomCloseConfirmationDialog; +typedef struct _EomCloseConfirmationDialogClass EomCloseConfirmationDialogClass; +typedef struct _EomCloseConfirmationDialogPrivate EomCloseConfirmationDialogPrivate; + +struct _EomCloseConfirmationDialog +{ + GtkDialog parent; + + /*< private > */ + EomCloseConfirmationDialogPrivate *priv; +}; + +struct _EomCloseConfirmationDialogClass +{ + GtkDialogClass parent_class; +}; + +G_GNUC_INTERNAL +GType eom_close_confirmation_dialog_get_type (void) G_GNUC_CONST; + +G_GNUC_INTERNAL +GtkWidget *eom_close_confirmation_dialog_new (GtkWindow *parent, + GList *unsaved_documents); +G_GNUC_INTERNAL +GtkWidget *eom_close_confirmation_dialog_new_single (GtkWindow *parent, + EomImage *image); + +G_GNUC_INTERNAL +const GList *eom_close_confirmation_dialog_get_unsaved_images (EomCloseConfirmationDialog *dlg); + +G_GNUC_INTERNAL +GList *eom_close_confirmation_dialog_get_selected_images (EomCloseConfirmationDialog *dlg); + +G_GNUC_INTERNAL +void eom_close_confirmation_dialog_set_sensitive (EomCloseConfirmationDialog *dlg, gboolean value); + +#endif /* __EOM_CLOSE_CONFIRMATION_DIALOG_H__ */ + diff --git a/src/eom-config-keys.h b/src/eom-config-keys.h new file mode 100644 index 0000000..7d7074c --- /dev/null +++ b/src/eom-config-keys.h @@ -0,0 +1,63 @@ +/* Eye Of Mate - MateConf Keys Macros + * + * Copyright (C) 2000-2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on code by: + * - Federico Mena-Quintero <[email protected]> + * - Jens Finke <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_CONFIG_KEYS_H__ +#define __EOM_CONFIG_KEYS_H__ + +#define EOM_CONF_DIR "/apps/eom" + +#define EOM_CONF_DESKTOP_WALLPAPER "/desktop/mate/background/picture_filename" +#define EOM_CONF_DESKTOP_CAN_SAVE "/desktop/mate/lockdown/disable_save_to_disk" +#define EOM_CONF_DESKTOP_CAN_PRINT "/desktop/mate/lockdown/disable_printing" +#define EOM_CONF_DESKTOP_CAN_SETUP_PAGE "/desktop/mate/lockdown/disable_print_setup" + +#define EOM_CONF_VIEW_BACKGROUND_COLOR "/apps/eom/view/background-color" +#define EOM_CONF_VIEW_INTERPOLATE "/apps/eom/view/interpolate" +#define EOM_CONF_VIEW_EXTRAPOLATE "/apps/eom/view/extrapolate" +#define EOM_CONF_VIEW_SCROLL_WHEEL_ZOOM "/apps/eom/view/scroll_wheel_zoom" +#define EOM_CONF_VIEW_ZOOM_MULTIPLIER "/apps/eom/view/zoom_multiplier" +#define EOM_CONF_VIEW_AUTOROTATE "/apps/eom/view/autorotate" +#define EOM_CONF_VIEW_TRANSPARENCY "/apps/eom/view/transparency" +#define EOM_CONF_VIEW_TRANS_COLOR "/apps/eom/view/trans_color" +#define EOM_CONF_VIEW_USE_BG_COLOR "/apps/eom/view/use-background-color" + +#define EOM_CONF_FULLSCREEN_LOOP "/apps/eom/full_screen/loop" +#define EOM_CONF_FULLSCREEN_UPSCALE "/apps/eom/full_screen/upscale" +#define EOM_CONF_FULLSCREEN_SECONDS "/apps/eom/full_screen/seconds" + +#define EOM_CONF_UI_TOOLBAR "/apps/eom/ui/toolbar" +#define EOM_CONF_UI_STATUSBAR "/apps/eom/ui/statusbar" +#define EOM_CONF_UI_IMAGE_COLLECTION "/apps/eom/ui/image_collection" +#define EOM_CONF_UI_IMAGE_COLLECTION_POSITION "/apps/eom/ui/image_collection_position" +#define EOM_CONF_UI_IMAGE_COLLECTION_RESIZABLE "/apps/eom/ui/image_collection_resizable" +#define EOM_CONF_UI_SIDEBAR "/apps/eom/ui/sidebar" +#define EOM_CONF_UI_SCROLL_BUTTONS "/apps/eom/ui/scroll_buttons" +#define EOM_CONF_UI_DISABLE_TRASH_CONFIRMATION "/apps/eom/ui/disable_trash_confirmation" +#define EOM_CONF_UI_FILECHOOSER_XDG_FALLBACK "/apps/eom/ui/filechooser_xdg_fallback" +#define EOM_CONF_UI_PROPSDIALOG_NETBOOK_MODE "/apps/eom/ui/propsdialog_netbook_mode" + +#define EOM_CONF_PLUGINS_ACTIVE_PLUGINS "/apps/eom/plugins/active_plugins" + +#endif /* __EOM_CONFIG_KEYS_H__ */ diff --git a/src/eom-debug.c b/src/eom-debug.c new file mode 100644 index 0000000..6d373af --- /dev/null +++ b/src/eom-debug.c @@ -0,0 +1,160 @@ +/* Eye Of Mate - Debugging + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-debug.h) by: + * - Alex Roberts <[email protected]> + * - Evan Lawrence <[email protected]> + * - Chema Celorio <[email protected]> + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> + +#include "eom-debug.h" + +#define ENABLE_PROFILING + +#ifdef ENABLE_PROFILING +static GTimer *timer = NULL; +static gdouble last = 0.0; +#endif + +static EomDebugSection debug = EOM_NO_DEBUG; + +void +eom_debug_init (void) +{ + if (g_getenv ("EOM_DEBUG") != NULL) + { + /* Enable all debugging */ + debug = ~EOM_NO_DEBUG; + goto out; + } + + if (g_getenv ("EOM_DEBUG_WINDOW") != NULL) + debug = debug | EOM_DEBUG_WINDOW; + + if (g_getenv ("EOM_DEBUG_VIEW") != NULL) + debug = debug | EOM_DEBUG_VIEW; + + if (g_getenv ("EOM_DEBUG_JOBS") != NULL) + debug = debug | EOM_DEBUG_JOBS; + + if (g_getenv ("EOM_DEBUG_THUMBNAIL") != NULL) + debug = debug | EOM_DEBUG_THUMBNAIL; + + if (g_getenv ("EOM_DEBUG_IMAGE_DATA") != NULL) + debug = debug | EOM_DEBUG_IMAGE_DATA; + + if (g_getenv ("EOM_DEBUG_IMAGE_LOAD") != NULL) + debug = debug | EOM_DEBUG_IMAGE_LOAD; + + if (g_getenv ("EOM_DEBUG_IMAGE_SAVE") != NULL) + debug = debug | EOM_DEBUG_IMAGE_SAVE; + + if (g_getenv ("EOM_DEBUG_LIST_STORE") != NULL) + debug = debug | EOM_DEBUG_LIST_STORE; + + if (g_getenv ("EOM_DEBUG_PREFERENCES") != NULL) + debug = debug | EOM_DEBUG_PREFERENCES; + + if (g_getenv ("EOM_DEBUG_PRINTING") != NULL) + debug = debug | EOM_DEBUG_PRINTING; + + if (g_getenv ("EOM_DEBUG_LCMS") != NULL) + debug = debug | EOM_DEBUG_LCMS; + + if (g_getenv ("EOM_DEBUG_PLUGINS") != NULL) + debug = debug | EOM_DEBUG_PLUGINS; + +out: + +#ifdef ENABLE_PROFILING + if (debug != EOM_NO_DEBUG) + timer = g_timer_new (); +#endif + return; +} + +void +eom_debug_message (EomDebugSection section, + const gchar *file, + gint line, + const gchar *function, + const gchar *format, ...) +{ + if (G_UNLIKELY (debug & section)) + { +#ifdef ENABLE_PROFILING + gdouble seconds; +#endif + + va_list args; + gchar *msg; + + g_return_if_fail (format != NULL); + + va_start (args, format); + msg = g_strdup_vprintf (format, args); + va_end (args); + +#ifdef ENABLE_PROFILING + g_return_if_fail (timer != NULL); + + seconds = g_timer_elapsed (timer, NULL); + g_print ("[%f (%f)] %s:%d (%s) %s\n", + seconds, seconds - last, file, line, function, msg); + last = seconds; +#else + g_print ("%s:%d (%s) %s\n", file, line, function, msg); +#endif + + fflush (stdout); + + g_free (msg); + } +} + +void eom_debug (EomDebugSection section, + const gchar *file, + gint line, + const gchar *function) +{ + if (G_UNLIKELY (debug & section)) + { +#ifdef ENABLE_PROFILING + gdouble seconds; + + g_return_if_fail (timer != NULL); + + seconds = g_timer_elapsed (timer, NULL); + g_print ("[%f (%f)] %s:%d (%s)\n", + seconds, seconds - last, file, line, function); + last = seconds; +#else + g_print ("%s:%d (%s)\n", file, line, function); +#endif + fflush (stdout); + } +} diff --git a/src/eom-debug.h b/src/eom-debug.h new file mode 100644 index 0000000..bde758c --- /dev/null +++ b/src/eom-debug.h @@ -0,0 +1,75 @@ +/* Eye Of Mate - Debugging + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-debug.h) by: + * - Alex Roberts <[email protected]> + * - Evan Lawrence <[email protected]> + * - Chema Celorio <[email protected]> + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_DEBUG_H__ +#define __EOM_DEBUG_H__ + +#include <glib.h> + +typedef enum { + EOM_NO_DEBUG = 0, + EOM_DEBUG_WINDOW = 1 << 0, + EOM_DEBUG_VIEW = 1 << 1, + EOM_DEBUG_JOBS = 1 << 2, + EOM_DEBUG_THUMBNAIL = 1 << 3, + EOM_DEBUG_IMAGE_DATA = 1 << 4, + EOM_DEBUG_IMAGE_LOAD = 1 << 5, + EOM_DEBUG_IMAGE_SAVE = 1 << 6, + EOM_DEBUG_LIST_STORE = 1 << 7, + EOM_DEBUG_PREFERENCES = 1 << 8, + EOM_DEBUG_PRINTING = 1 << 9, + EOM_DEBUG_LCMS = 1 << 10, + EOM_DEBUG_PLUGINS = 1 << 11 +} EomDebugSection; + +#define DEBUG_WINDOW EOM_DEBUG_WINDOW, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_VIEW EOM_DEBUG_VIEW, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_JOBS EOM_DEBUG_JOBS, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_THUMBNAIL EOM_DEBUG_THUMBNAIL, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_IMAGE_DATA EOM_DEBUG_IMAGE_DATA, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_IMAGE_LOAD EOM_DEBUG_IMAGE_LOAD, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_IMAGE_SAVE EOM_DEBUG_IMAGE_SAVE, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_LIST_STORE EOM_DEBUG_LIST_STORE, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_PREFERENCES EOM_DEBUG_PREFERENCES, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_PRINTING EOM_DEBUG_PRINTING, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_LCMS EOM_DEBUG_LCMS, __FILE__, __LINE__, G_STRFUNC +#define DEBUG_PLUGINS EOM_DEBUG_PLUGINS, __FILE__, __LINE__, G_STRFUNC + +void eom_debug_init (void); + +void eom_debug (EomDebugSection section, + const gchar *file, + gint line, + const gchar *function); + +void eom_debug_message (EomDebugSection section, + const gchar *file, + gint line, + const gchar *function, + const gchar *format, ...) G_GNUC_PRINTF(5, 6); + +#endif /* __EOM_DEBUG_H__ */ diff --git a/src/eom-dialog.c b/src/eom-dialog.c new file mode 100644 index 0000000..fac74a9 --- /dev/null +++ b/src/eom-dialog.c @@ -0,0 +1,237 @@ +/* Eye Of Mate - Image Dialog + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eom-dialog.h" + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#define EOM_DIALOG_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_DIALOG, EomDialogPrivate)) + +G_DEFINE_TYPE (EomDialog, eom_dialog, G_TYPE_OBJECT); + +enum { + PROP_0, + PROP_PARENT_WINDOW, +}; + +struct _EomDialogPrivate { + GtkWidget *dlg; + GtkBuilder *xml; + GtkWindow *parent; +}; + +static void +eom_dialog_construct_impl (EomDialog *dialog, + const gchar *glade_file, + const gchar *dlg_node) +{ + EomDialogPrivate *priv; + gchar *filename; + + g_return_if_fail (dialog != NULL); + g_return_if_fail (EOM_IS_DIALOG (dialog)); + + priv = dialog->priv; + + filename = g_build_filename (EOM_DATA_DIR, glade_file, NULL); + + priv->xml = gtk_builder_new (); + gtk_builder_set_translation_domain (priv->xml, GETTEXT_PACKAGE); + g_assert (gtk_builder_add_from_file (priv->xml, filename, NULL)); + + g_free (filename); + + priv->dlg = GTK_WIDGET (gtk_builder_get_object (priv->xml, dlg_node)); + + if (priv->parent != NULL) { + gtk_window_set_transient_for (GTK_WINDOW (priv->dlg), + priv->parent); + } +} + +static void +eom_dialog_show_impl (EomDialog *dialog) +{ + g_return_if_fail (dialog != NULL); + g_return_if_fail (EOM_IS_DIALOG (dialog)); + + gtk_window_present (GTK_WINDOW (dialog->priv->dlg)); +} + +static void +eom_dialog_hide_impl (EomDialog *dialog) +{ + g_return_if_fail (dialog != NULL); + g_return_if_fail (EOM_IS_DIALOG (dialog)); + + gtk_widget_hide (dialog->priv->dlg); +} + +static void +eom_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EomDialog *dialog = EOM_DIALOG (object); + + switch (prop_id) { + case PROP_PARENT_WINDOW: + dialog->priv->parent = g_value_get_object (value); + break; + } +} + +static void +eom_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EomDialog *dialog = EOM_DIALOG (object); + + switch (prop_id) { + case PROP_PARENT_WINDOW: + g_value_set_object (value, dialog->priv->parent); + break; + } +} + +static void +eom_dialog_dispose (GObject *object) +{ + EomDialog *dialog; + EomDialogPrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (EOM_IS_DIALOG (object)); + + dialog = EOM_DIALOG (object); + priv = dialog->priv; + + if (priv->dlg) { + gtk_widget_destroy (priv->dlg); + priv->dlg = NULL; + } + + if (priv->xml) { + g_object_unref (priv->xml); + priv->xml = NULL; + } + + G_OBJECT_CLASS (eom_dialog_parent_class)->dispose (object); +} + +static void +eom_dialog_class_init (EomDialogClass *class) +{ + GObjectClass *g_object_class = (GObjectClass *) class; + + g_object_class->dispose = eom_dialog_dispose; + g_object_class->set_property = eom_dialog_set_property; + g_object_class->get_property = eom_dialog_get_property; + + class->construct = eom_dialog_construct_impl; + class->show = eom_dialog_show_impl; + class->hide = eom_dialog_hide_impl; + + g_object_class_install_property (g_object_class, + PROP_PARENT_WINDOW, + g_param_spec_object ("parent-window", + "Parent window", + "Parent window", + GTK_TYPE_WINDOW, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (g_object_class, sizeof (EomDialogPrivate)); +} + +static void +eom_dialog_init (EomDialog *dialog) +{ + dialog->priv = EOM_DIALOG_GET_PRIVATE (dialog); + + dialog->priv->dlg = NULL; + dialog->priv->xml = NULL; + dialog->priv->parent = NULL; +} + +void +eom_dialog_construct (EomDialog *dialog, + const gchar *glade_file, + const gchar *dlg_node) +{ + EomDialogClass *klass = EOM_DIALOG_GET_CLASS (dialog); + klass->construct (dialog, glade_file, dlg_node); +} + +void +eom_dialog_show (EomDialog *dialog) +{ + EomDialogClass *klass = EOM_DIALOG_GET_CLASS (dialog); + klass->show (dialog); +} + +void +eom_dialog_hide (EomDialog *dialog) +{ + EomDialogClass *klass = EOM_DIALOG_GET_CLASS (dialog); + klass->hide (dialog); +} + +void +eom_dialog_get_controls (EomDialog *dialog, + const gchar *property_id, + ...) +{ + EomDialogPrivate *priv; + GtkWidget **wptr; + va_list varargs; + + g_return_if_fail (dialog != NULL); + g_return_if_fail (EOM_IS_DIALOG (dialog)); + + priv = dialog->priv; + + va_start (varargs, property_id); + + while (property_id != NULL) + { + wptr = va_arg (varargs, GtkWidget **); + *wptr = GTK_WIDGET (gtk_builder_get_object (priv->xml, + property_id)); + + property_id = va_arg (varargs, const gchar *); + } + + va_end (varargs); +} diff --git a/src/eom-dialog.h b/src/eom-dialog.h new file mode 100644 index 0000000..3022dee --- /dev/null +++ b/src/eom-dialog.h @@ -0,0 +1,76 @@ +/* Eye Of Mate - EOM Dialog + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_DIALOG_H__ +#define __EOM_DIALOG_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _EomDialog EomDialog; +typedef struct _EomDialogClass EomDialogClass; +typedef struct _EomDialogPrivate EomDialogPrivate; + +#define EOM_TYPE_DIALOG (eom_dialog_get_type ()) +#define EOM_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_DIALOG, EomDialog)) +#define EOM_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_DIALOG, EomDialogClass)) +#define EOM_IS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_DIALOG)) +#define EOM_IS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_DIALOG)) +#define EOM_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_DIALOG, EomDialogClass)) + +struct _EomDialog { + GObject object; + + EomDialogPrivate *priv; +}; + +struct _EomDialogClass { + GObjectClass parent_class; + + void (* construct) (EomDialog *dialog, + const gchar *glade_file, + const gchar *dlg_node); + + void (* show) (EomDialog *dialog); + + void (* hide) (EomDialog *dialog); +}; + +GType eom_dialog_get_type (void) G_GNUC_CONST; + +void eom_dialog_construct (EomDialog *dialog, + const gchar *glade_file, + const gchar *dlg_node); + +void eom_dialog_show (EomDialog *dialog); + +void eom_dialog_hide (EomDialog *dialog); + +void eom_dialog_get_controls (EomDialog *dialog, + const gchar *property_id, + ...); + +G_END_DECLS + +#endif /* __EOM_DIALOG_H__ */ diff --git a/src/eom-enum-types.c.template b/src/eom-enum-types.c.template new file mode 100644 index 0000000..a7212e0 --- /dev/null +++ b/src/eom-enum-types.c.template @@ -0,0 +1,39 @@ +/*** BEGIN file-header ***/ +#include "eom-enum-types.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@[email protected]" */ +#include "@[email protected]" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType [email protected][email protected]_get_type (void) +{ + static GType the_type = 0; + + if (the_type == 0) + { + static const [email protected]@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @[email protected], + "@[email protected]", + "@[email protected]" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + the_type = [email protected]@_register_static ( + g_intern_static_string ("@[email protected]"), + values); + } + return the_type; +} + +/*** END value-tail ***/ diff --git a/src/eom-enum-types.h.template b/src/eom-enum-types.h.template new file mode 100644 index 0000000..e70c3b0 --- /dev/null +++ b/src/eom-enum-types.h.template @@ -0,0 +1,27 @@ +/*** BEGIN file-header ***/ +#ifndef __EOM_ENUM_TYPES_H__ +#define __EOM_ENUM_TYPES_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* Enumerations from "@[email protected]" */ + +/*** END file-production ***/ + +/*** BEGIN enumeration-production ***/ +#define [email protected]@ (@[email protected]_get_type()) +G_GNUC_INTERNAL GType @[email protected]_get_type (void) G_GNUC_CONST; + +/*** END enumeration-production ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __EOM_ENUM_TYPES_H__ */ +/*** END file-tail ***/ + diff --git a/src/eom-enums.h b/src/eom-enums.h new file mode 100644 index 0000000..b39e978 --- /dev/null +++ b/src/eom-enums.h @@ -0,0 +1,37 @@ +/* Eye of MATE - General enumerations. + * + * Copyright (C) 2007-2008 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_ENUMS__ +#define __EOM_ENUMS__ + +typedef enum { + EOM_IMAGE_DATA_IMAGE = 1 << 0, + EOM_IMAGE_DATA_DIMENSION = 1 << 1, + EOM_IMAGE_DATA_EXIF = 1 << 2, + EOM_IMAGE_DATA_XMP = 1 << 3 +} EomImageData; + +#define EOM_IMAGE_DATA_ALL (EOM_IMAGE_DATA_IMAGE | \ + EOM_IMAGE_DATA_DIMENSION | \ + EOM_IMAGE_DATA_EXIF | \ + EOM_IMAGE_DATA_XMP) + +#endif diff --git a/src/eom-error-message-area.c b/src/eom-error-message-area.c new file mode 100644 index 0000000..f0a2baf --- /dev/null +++ b/src/eom-error-message-area.c @@ -0,0 +1,192 @@ +/* Eye Of Mate - Erro Message Area + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-message-area.h) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eom-error-message-area.h" +#include "eom-image.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +static void +set_message_area_text_and_icon (GtkInfoBar *message_area, + const gchar *icon_stock_id, + const gchar *primary_text, + const gchar *secondary_text) +{ + GtkWidget *hbox_content; + GtkWidget *image; + GtkWidget *vbox; + gchar *primary_markup; + gchar *secondary_markup; + GtkWidget *primary_label; + GtkWidget *secondary_label; + + hbox_content = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox_content); + + image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG); + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + + primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); + primary_label = gtk_label_new (primary_markup); + g_free (primary_markup); + + gtk_widget_show (primary_label); + + gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); + gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (primary_label), FALSE); + gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); + + gtk_widget_set_can_focus (primary_label, TRUE); + + gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + + if (secondary_text != NULL) { + secondary_markup = g_strdup_printf ("<small>%s</small>", + secondary_text); + secondary_label = gtk_label_new (secondary_markup); + g_free (secondary_markup); + + gtk_widget_show (secondary_label); + + gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); + + gtk_widget_set_can_focus (secondary_label, TRUE); + + gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); + } + + gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (message_area))), hbox_content, TRUE, TRUE, 0); +} + +static GtkWidget * +create_error_message_area (const gchar *primary_text, + const gchar *secondary_text, + gboolean recoverable) +{ + GtkWidget *message_area; + + if (recoverable) + message_area = gtk_info_bar_new_with_buttons (_("_Retry"), + GTK_RESPONSE_OK, + NULL); + else + message_area = gtk_info_bar_new (); + + gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), + GTK_MESSAGE_ERROR); + + set_message_area_text_and_icon (GTK_INFO_BAR (message_area), + GTK_STOCK_DIALOG_ERROR, + primary_text, + secondary_text); + + return message_area; +} + +GtkWidget * +eom_image_load_error_message_area_new (const gchar *caption, + const GError *error) +{ + GtkWidget *message_area; + gchar *error_message = NULL; + gchar *message_details = NULL; + gchar *pango_escaped_caption = NULL; + + g_return_val_if_fail (caption != NULL, NULL); + g_return_val_if_fail (error != NULL, NULL); + + /* Escape the caption string with respect to pango markup. + This is necessary because otherwise characters like "&" will + be interpreted as the beginning of a pango entity inside + the message area GtkLabel. */ + pango_escaped_caption = g_markup_escape_text (caption, -1); + error_message = g_strdup_printf (_("Could not load image '%s'."), + pango_escaped_caption); + + message_details = g_strdup (error->message); + + message_area = create_error_message_area (error_message, + message_details, + TRUE); + + g_free (pango_escaped_caption); + g_free (error_message); + g_free (message_details); + + return message_area; +} + +GtkWidget * +eom_no_images_error_message_area_new (GFile *file) +{ + GtkWidget *message_area; + gchar *error_message = NULL; + + if (file != NULL) { + gchar *uri_str, *unescaped_str, *pango_escaped_str; + + uri_str = g_file_get_uri (file); + /* Unescape URI with respect to rules defined in RFC 3986. */ + unescaped_str = g_uri_unescape_string (uri_str, NULL); + + /* Escape the URI string with respect to pango markup. + This is necessary because the URI string can contain + for example "&" which will otherwise be interpreted + as a pango markup entity when inserted into a GtkLabel. */ + pango_escaped_str = g_markup_escape_text (unescaped_str, -1); + error_message = g_strdup_printf (_("No images found in '%s'."), + pango_escaped_str); + + g_free (pango_escaped_str); + g_free (uri_str); + g_free (unescaped_str); + } else { + error_message = g_strdup (_("The given locations contain no images.")); + } + + message_area = create_error_message_area (error_message, + NULL, + FALSE); + + g_free (error_message); + + return message_area; +} diff --git a/src/eom-error-message-area.h b/src/eom-error-message-area.h new file mode 100644 index 0000000..8b285ba --- /dev/null +++ b/src/eom-error-message-area.h @@ -0,0 +1,37 @@ +/* Eye Of Mate - Erro Message Area + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-message-area.h) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_ERROR_MESSAGE_AREA__ +#define __EOM_ERROR_MESSAGE_AREA__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <gio/gio.h> + +GtkWidget *eom_image_load_error_message_area_new (const gchar *caption, + const GError *error); + +GtkWidget *eom_no_images_error_message_area_new (GFile *file); + +#endif /* __EOM_ERROR_MESSAGE_AREA__ */ diff --git a/src/eom-exif-details.c b/src/eom-exif-details.c new file mode 100644 index 0000000..2385e6a --- /dev/null +++ b/src/eom-exif-details.c @@ -0,0 +1,572 @@ +/* Eye Of Mate - EOM Image Exif Details + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eom-exif-details.h" +#include "eom-util.h" + +#if HAVE_EXIF +#include <libexif/exif-entry.h> +#include <libexif/exif-utils.h> +#endif +#if HAVE_EXEMPI +#include <exempi/xmp.h> +#include <exempi/xmpconsts.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <string.h> + +#define EOM_EXIF_DETAILS_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_EXIF_DETAILS, EomExifDetailsPrivate)) + +G_DEFINE_TYPE (EomExifDetails, eom_exif_details, GTK_TYPE_TREE_VIEW) + +typedef enum { + EXIF_CATEGORY_CAMERA, + EXIF_CATEGORY_IMAGE_DATA, + EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS, + EXIF_CATEGORY_MAKER_NOTE, + EXIF_CATEGORY_OTHER, +#ifdef HAVE_EXEMPI + XMP_CATEGORY_EXIF, + XMP_CATEGORY_IPTC, + XMP_CATEGORY_RIGHTS, + XMP_CATEGORY_OTHER +#endif +} ExifCategory; + +typedef struct { + char *label; + char *path; +} ExifCategoryInfo; + +static ExifCategoryInfo exif_categories[] = { + { N_("Camera"), "0" }, + { N_("Image Data"), "1" }, + { N_("Image Taking Conditions"), "2" }, + { N_("Maker Note"), "3" }, + { N_("Other"), "4" }, +#ifdef HAVE_EXEMPI + { N_("XMP Exif"), "5" }, + { N_("XMP IPTC"), "6" }, + { N_("XMP Rights Management"), "7" }, + { N_("XMP Other"), "8" }, +#endif + { NULL, NULL } +}; + +typedef struct { + int id; + ExifCategory category; +} ExifTagCategory; + +#ifdef HAVE_EXIF +static ExifTagCategory exif_tag_category_map[] = { + { EXIF_TAG_INTEROPERABILITY_INDEX, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_INTEROPERABILITY_VERSION, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_IMAGE_WIDTH, EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_IMAGE_LENGTH , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_BITS_PER_SAMPLE ,EXIF_CATEGORY_CAMERA }, + { EXIF_TAG_COMPRESSION , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_PHOTOMETRIC_INTERPRETATION , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_FILL_ORDER , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_DOCUMENT_NAME , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_IMAGE_DESCRIPTION , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_MAKE , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_MODEL , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_STRIP_OFFSETS , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_ORIENTATION , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_SAMPLES_PER_PIXEL , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_ROWS_PER_STRIP , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_STRIP_BYTE_COUNTS , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_X_RESOLUTION , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_Y_RESOLUTION , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_PLANAR_CONFIGURATION , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_RESOLUTION_UNIT , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_TRANSFER_FUNCTION , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_SOFTWARE , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_DATE_TIME , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_ARTIST , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_WHITE_POINT , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_PRIMARY_CHROMATICITIES, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_TRANSFER_RANGE , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_JPEG_PROC , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_JPEG_INTERCHANGE_FORMAT, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, }, + { EXIF_TAG_YCBCR_COEFFICIENTS , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_YCBCR_SUB_SAMPLING , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_YCBCR_POSITIONING , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_REFERENCE_BLACK_WHITE, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_RELATED_IMAGE_FILE_FORMAT,EXIF_CATEGORY_OTHER}, + { EXIF_TAG_RELATED_IMAGE_WIDTH , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_RELATED_IMAGE_LENGTH , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_CFA_REPEAT_PATTERN_DIM, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_CFA_PATTERN , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_BATTERY_LEVEL , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_COPYRIGHT , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_EXPOSURE_TIME , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_FNUMBER , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_IPTC_NAA , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_EXIF_IFD_POINTER , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_INTER_COLOR_PROFILE , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_EXPOSURE_PROGRAM , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_SPECTRAL_SENSITIVITY , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_GPS_INFO_IFD_POINTER , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_ISO_SPEED_RATINGS , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_OECF , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_EXIF_VERSION , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_DATE_TIME_ORIGINAL , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_DATE_TIME_DIGITIZED , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_COMPONENTS_CONFIGURATION , EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_COMPRESSED_BITS_PER_PIXEL, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_SHUTTER_SPEED_VALUE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_APERTURE_VALUE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_BRIGHTNESS_VALUE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_EXPOSURE_BIAS_VALUE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_MAX_APERTURE_VALUE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_SUBJECT_DISTANCE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_METERING_MODE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_LIGHT_SOURCE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_FLASH , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_FOCAL_LENGTH , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_SUBJECT_AREA , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_MAKER_NOTE , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_USER_COMMENT , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_SUBSEC_TIME , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_FLASH_PIX_VERSION , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_COLOR_SPACE , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_PIXEL_X_DIMENSION , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_PIXEL_Y_DIMENSION , EXIF_CATEGORY_IMAGE_DATA}, + { EXIF_TAG_RELATED_SOUND_FILE , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_INTEROPERABILITY_IFD_POINTER, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_FLASH_ENERGY ,EXIF_CATEGORY_OTHER }, + { EXIF_TAG_SPATIAL_FREQUENCY_RESPONSE, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_FOCAL_PLANE_X_RESOLUTION, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_FOCAL_PLANE_Y_RESOLUTION, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_FOCAL_PLANE_RESOLUTION_UNIT, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_SUBJECT_LOCATION, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_EXPOSURE_INDEX, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_SENSING_METHOD, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_FILE_SOURCE , EXIF_CATEGORY_OTHER}, + { EXIF_TAG_SCENE_TYPE, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_NEW_CFA_PATTERN, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_CUSTOM_RENDERED, EXIF_CATEGORY_OTHER}, + { EXIF_TAG_EXPOSURE_MODE, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_WHITE_BALANCE, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_DIGITAL_ZOOM_RATIO, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_SCENE_CAPTURE_TYPE , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_GAIN_CONTROL , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_CONTRAST , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_SATURATION , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_SHARPNESS , EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_DEVICE_SETTING_DESCRIPTION, EXIF_CATEGORY_CAMERA}, + { EXIF_TAG_SUBJECT_DISTANCE_RANGE, EXIF_CATEGORY_IMAGE_TAKING_CONDITIONS}, + { EXIF_TAG_IMAGE_UNIQUE_ID , EXIF_CATEGORY_IMAGE_DATA}, + { -1, -1 } +}; +#endif + +#define MODEL_COLUMN_ATTRIBUTE 0 +#define MODEL_COLUMN_VALUE 1 + +struct _EomExifDetailsPrivate { + GtkTreeModel *model; + + GHashTable *id_path_hash; + GHashTable *id_path_hash_mnote; +}; + +static char* set_row_data (GtkTreeStore *store, char *path, char *parent, const char *attribute, const char *value); + +static void eom_exif_details_reset (EomExifDetails *exif_details); + +static void +eom_exif_details_dispose (GObject *object) +{ + EomExifDetailsPrivate *priv; + + priv = EOM_EXIF_DETAILS (object)->priv; + + if (priv->model) { + g_object_unref (priv->model); + priv->model = NULL; + } + + if (priv->id_path_hash) { + g_hash_table_destroy (priv->id_path_hash); + priv->id_path_hash = NULL; + } + + if (priv->id_path_hash_mnote) { + g_hash_table_destroy (priv->id_path_hash_mnote); + priv->id_path_hash_mnote = NULL; + } + G_OBJECT_CLASS (eom_exif_details_parent_class)->dispose (object); +} + +static void +eom_exif_details_init (EomExifDetails *exif_details) +{ + EomExifDetailsPrivate *priv; + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + + exif_details->priv = EOM_EXIF_DETAILS_GET_PRIVATE (exif_details); + + priv = exif_details->priv; + + priv->model = GTK_TREE_MODEL (gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING)); + priv->id_path_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + priv->id_path_hash_mnote = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + + /* Tag name column */ + cell = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Tag"), cell, + "text", MODEL_COLUMN_ATTRIBUTE, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (exif_details), column); + + /* Value column */ + cell = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Value"), cell, + "text", MODEL_COLUMN_VALUE, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (exif_details), column); + + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (exif_details), TRUE); + + eom_exif_details_reset (exif_details); + + gtk_tree_view_set_model (GTK_TREE_VIEW (exif_details), + GTK_TREE_MODEL (priv->model)); +} + +static void +eom_exif_details_class_init (EomExifDetailsClass *klass) +{ + GObjectClass *object_class = (GObjectClass*) klass; + + object_class->dispose = eom_exif_details_dispose; + + g_type_class_add_private (object_class, sizeof (EomExifDetailsPrivate)); +} + +#ifdef HAVE_EXIF +static ExifCategory +get_exif_category (ExifEntry *entry) +{ + ExifCategory cat = EXIF_CATEGORY_OTHER; + int i; + + for (i = 0; exif_tag_category_map [i].id != -1; i++) { + if (exif_tag_category_map[i].id == (int) entry->tag) { + cat = exif_tag_category_map[i].category; + break; + } + } + + return cat; +} +#endif + +static char* +set_row_data (GtkTreeStore *store, char *path, char *parent, const char *attribute, const char *value) +{ + GtkTreeIter iter; + gchar *utf_attribute = NULL; + gchar *utf_value = NULL; + gboolean iter_valid = FALSE; + + if (!attribute) return NULL; + + if (path != NULL) { + iter_valid = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &iter, path); + } + + if (!iter_valid) { + GtkTreePath *tree_path; + GtkTreeIter parent_iter; + gboolean parent_valid = FALSE; + + if (parent != NULL) { + parent_valid = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), + &parent_iter, + parent); + } + + gtk_tree_store_append (store, &iter, parent_valid ? &parent_iter : NULL); + + if (path == NULL) { + tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); + + if (tree_path != NULL) { + path = gtk_tree_path_to_string (tree_path); + gtk_tree_path_free (tree_path); + } + } + } + + utf_attribute = eom_util_make_valid_utf8 (attribute); + + gtk_tree_store_set (store, &iter, MODEL_COLUMN_ATTRIBUTE, utf_attribute, -1); + g_free (utf_attribute); + + if (value != NULL) { + utf_value = eom_util_make_valid_utf8 (value); + gtk_tree_store_set (store, &iter, MODEL_COLUMN_VALUE, utf_value, -1); + g_free (utf_value); + } + + return path; +} + +#ifdef HAVE_EXIF +static void +exif_entry_cb (ExifEntry *entry, gpointer data) +{ + GtkTreeStore *store; + EomExifDetails *view; + EomExifDetailsPrivate *priv; + ExifCategory cat; + char *path; + char b[1024]; + + view = EOM_EXIF_DETAILS (data); + priv = view->priv; + + store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view))); + + path = g_hash_table_lookup (priv->id_path_hash, GINT_TO_POINTER (entry->tag)); + + if (path != NULL) { + set_row_data (store, + path, + NULL, + exif_tag_get_name (entry->tag), + exif_entry_get_value (entry, b, sizeof(b))); + } else { + + ExifMnoteData *mnote = (entry->tag == EXIF_TAG_MAKER_NOTE ? + exif_data_get_mnote_data (entry->parent->parent) : NULL); + + if (mnote) { + // Supported MakerNote Found + unsigned int i, c = exif_mnote_data_count (mnote); + + for (i = 0; i < c; i++) { + path = g_hash_table_lookup (priv->id_path_hash_mnote, GINT_TO_POINTER (i)); + if (path != NULL) { + set_row_data (store, path, NULL, + exif_mnote_data_get_title (mnote, i), + exif_mnote_data_get_value (mnote, i, b, sizeof(b))); + } else { + path = set_row_data (store, + NULL, + exif_categories[EXIF_CATEGORY_MAKER_NOTE].path, + exif_mnote_data_get_title (mnote, i), + exif_mnote_data_get_value (mnote, i, b, sizeof(b))); + g_hash_table_insert (priv->id_path_hash_mnote, GINT_TO_POINTER (i), path); + } + } + } else { + cat = get_exif_category (entry); + + path = set_row_data (store, + NULL, + exif_categories[cat].path, + exif_tag_get_name (entry->tag), + exif_entry_get_value (entry, b, + sizeof(b))); + + g_hash_table_insert (priv->id_path_hash, + GINT_TO_POINTER (entry->tag), + path); + } + } +} +#endif + +#ifdef HAVE_EXIF +static void +exif_content_cb (ExifContent *content, gpointer data) +{ + exif_content_foreach_entry (content, exif_entry_cb, data); +} +#endif + +GtkWidget * +eom_exif_details_new (void) +{ + GObject *object; + + object = g_object_new (EOM_TYPE_EXIF_DETAILS, NULL); + + return GTK_WIDGET (object); +} + +static void +eom_exif_details_reset (EomExifDetails *exif_details) +{ + int i; + EomExifDetailsPrivate *priv = exif_details->priv; + + gtk_tree_store_clear (GTK_TREE_STORE (priv->model)); + + g_hash_table_remove_all (priv->id_path_hash); + g_hash_table_remove_all (priv->id_path_hash_mnote); + + for (i = 0; exif_categories [i].label != NULL; i++) { + char *translated_string; + + translated_string = gettext (exif_categories[i].label); + + set_row_data (GTK_TREE_STORE (priv->model), + exif_categories[i].path, + NULL, + translated_string, + NULL); + } +} + +#ifdef HAVE_EXIF +void +eom_exif_details_update (EomExifDetails *exif_details, ExifData *data) +{ + EomExifDetailsPrivate *priv; + + g_return_if_fail (EOM_IS_EXIF_DETAILS (exif_details)); + + priv = exif_details->priv; + + eom_exif_details_reset (exif_details); + if (data) { + exif_data_foreach_content (data, exif_content_cb, exif_details); + } +} +#endif /* HAVE_EXIF */ + +#ifdef HAVE_EXEMPI +typedef struct { + const char *id; + ExifCategory category; +} XmpNsCategory; + +static XmpNsCategory xmp_ns_category_map[] = { + { NS_EXIF, XMP_CATEGORY_EXIF}, + { NS_TIFF, XMP_CATEGORY_EXIF}, + { NS_XAP, XMP_CATEGORY_EXIF}, + { NS_XAP_RIGHTS, XMP_CATEGORY_RIGHTS}, + { NS_EXIF_AUX, XMP_CATEGORY_EXIF}, + { NS_DC, XMP_CATEGORY_IPTC}, + { NS_IPTC4XMP, XMP_CATEGORY_IPTC}, + { NS_CC, XMP_CATEGORY_RIGHTS}, + { NULL, -1} +}; + +static ExifCategory +get_xmp_category (XmpStringPtr schema) +{ + ExifCategory cat = XMP_CATEGORY_OTHER; + const char *s = xmp_string_cstr(schema); + int i; + + for (i = 0; xmp_ns_category_map[i].id != NULL; i++) { + if (strcmp (xmp_ns_category_map[i].id, s) == 0) { + cat = xmp_ns_category_map[i].category; + break; + } + } + + return cat; +} + +static void +xmp_entry_insert (EomExifDetails *view, XmpStringPtr xmp_schema, + XmpStringPtr xmp_path, XmpStringPtr xmp_prop) +{ + GtkTreeStore *store; + EomExifDetailsPrivate *priv; + ExifCategory cat; + char *path; + gchar *key; + + priv = view->priv; + + key = g_strconcat (xmp_string_cstr (xmp_schema), ":", + xmp_string_cstr (xmp_path), NULL); + + store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view))); + + path = g_hash_table_lookup (priv->id_path_hash, key); + + if (path != NULL) { + set_row_data (store, path, NULL, + xmp_string_cstr (xmp_path), + xmp_string_cstr (xmp_prop)); + + g_free(key); + } + else { + cat = get_xmp_category (xmp_schema); + + path = set_row_data (store, NULL, exif_categories[cat].path, + xmp_string_cstr(xmp_path), + xmp_string_cstr(xmp_prop)); + + g_hash_table_insert (priv->id_path_hash, key, path); + } +} + +void +eom_exif_details_xmp_update (EomExifDetails *view, XmpPtr data) +{ + EomExifDetailsPrivate *priv; + + g_return_if_fail (EOM_IS_EXIF_DETAILS (view)); + + priv = view->priv; + + if (data) { + XmpIteratorPtr iter = xmp_iterator_new(data, NULL, NULL, XMP_ITER_JUSTLEAFNODES); + XmpStringPtr the_schema = xmp_string_new (); + XmpStringPtr the_path = xmp_string_new (); + XmpStringPtr the_prop = xmp_string_new (); + + while (xmp_iterator_next (iter, the_schema, the_path, the_prop, NULL)) { + xmp_entry_insert (view, the_schema, the_path, the_prop); + } + + xmp_string_free (the_prop); + xmp_string_free (the_path); + xmp_string_free (the_schema); + xmp_iterator_free (iter); + } +} +#endif diff --git a/src/eom-exif-details.h b/src/eom-exif-details.h new file mode 100644 index 0000000..5964133 --- /dev/null +++ b/src/eom-exif-details.h @@ -0,0 +1,72 @@ +/* Eye Of Mate - EOM Image Exif Details + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_EXIF_DETAILS__ +#define __EOM_EXIF_DETAILS__ + +#include <glib-object.h> +#include <gtk/gtk.h> +#if HAVE_EXIF +#include <libexif/exif-data.h> +#endif +#if HAVE_EXEMPI +#include <exempi/xmp.h> +#endif + +G_BEGIN_DECLS + +typedef struct _EomExifDetails EomExifDetails; +typedef struct _EomExifDetailsClass EomExifDetailsClass; +typedef struct _EomExifDetailsPrivate EomExifDetailsPrivate; + +#define EOM_TYPE_EXIF_DETAILS (eom_exif_details_get_type ()) +#define EOM_EXIF_DETAILS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_EXIF_DETAILS, EomExifDetails)) +#define EOM_EXIF_DETAILS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_EXIF_DETAILS, EomExifDetailsClass)) +#define EOM_IS_EXIF_DETAILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_EXIF_DETAILS)) +#define EOM_IS_EXIF_DETAILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_EXIF_DETAILS)) +#define EOM_EXIF_DETAILS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_EXIF_DETAILS, EomExifDetailsClass)) + +struct _EomExifDetails { + GtkTreeView parent; + + EomExifDetailsPrivate *priv; +}; + +struct _EomExifDetailsClass { + GtkTreeViewClass parent_class; +}; + +GType eom_exif_details_get_type (void) G_GNUC_CONST; + +GtkWidget *eom_exif_details_new (void); + +#if HAVE_EXIF +void eom_exif_details_update (EomExifDetails *view, + ExifData *data); +#endif +#if HAVE_EXEMPI +void eom_exif_details_xmp_update (EomExifDetails *view, + XmpPtr xmp_data); +#endif + +G_END_DECLS + +#endif /* __EOM_EXIF_DETAILS__ */ diff --git a/src/eom-exif-util.c b/src/eom-exif-util.c new file mode 100644 index 0000000..93d54e0 --- /dev/null +++ b/src/eom-exif-util.c @@ -0,0 +1,214 @@ +/* Eye Of Mate - EXIF Utilities + * + * Copyright (C) 2006-2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * Author: Claudio Saavedra <[email protected]> + * Author: Felix Riemann <[email protected]> + * + * Based on code by: + * - Jens Finke <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRPTIME +#define _XOPEN_SOURCE +#endif +#include <time.h> + +#include "eom-exif-util.h" + +#include <string.h> +#include <glib/gi18n.h> + +#define DATE_BUF_SIZE 200 + +/* gboolean <-> gpointer conversion macros taken from gedit */ +#ifndef GBOOLEAN_TO_POINTER +#define GBOOLEAN_TO_POINTER(i) (GINT_TO_POINTER ((i) ? 2 : 1)) +#endif +#ifndef GPOINTER_TO_BOOLEAN +#define GPOINTER_TO_BOOLEAN(i) ((gboolean) ((GPOINTER_TO_INT (i) == 2) ? TRUE : FALSE)) +#endif + +static gpointer +_check_strptime_updates_wday (gpointer data) +{ + struct tm tm; + + memset (&tm, '\0', sizeof (tm)); + strptime ("2008:12:24 20:30:45", "%Y:%m:%d %T", &tm); + /* Check if tm.tm_wday is set to Wednesday (3) now */ + return GBOOLEAN_TO_POINTER (tm.tm_wday == 3); +} + +/** + * _calculate_wday_yday: + * @tm: A struct tm that should be updated. + * + * Ensure tm_wday and tm_yday are set correctly in a struct tm. + * The other date (dmy) values must have been set already. + **/ +static void +_calculate_wday_yday (struct tm *tm) +{ + GDate *exif_date; + struct tm tmp_tm; + + exif_date = g_date_new_dmy (tm->tm_mday, + tm->tm_mon+1, + tm->tm_year+1900); + + g_return_if_fail (exif_date != NULL && g_date_valid (exif_date)); + + // Use this to get GLib <-> libc corrected values + g_date_to_struct_tm (exif_date, &tmp_tm); + g_date_free (exif_date); + + tm->tm_wday = tmp_tm.tm_wday; + tm->tm_yday = tmp_tm.tm_yday; +} + +#ifdef HAVE_STRPTIME +static gchar * +eom_exif_util_format_date_with_strptime (const gchar *date) +{ + static GOnce strptime_updates_wday = G_ONCE_INIT; + gchar *new_date = NULL; + gchar tmp_date[DATE_BUF_SIZE]; + gchar *p; + gsize dlen; + struct tm tm; + + memset (&tm, '\0', sizeof (tm)); + p = strptime (date, "%Y:%m:%d %T", &tm); + + if (p == date + strlen (date)) { + g_once (&strptime_updates_wday, + _check_strptime_updates_wday, + NULL); + + // Ensure tm.tm_wday and tm.tm_yday are set + if (!GPOINTER_TO_BOOLEAN (strptime_updates_wday.retval)) + _calculate_wday_yday (&tm); + + /* A strftime-formatted string, to display the date the image was taken. */ + dlen = strftime (tmp_date, DATE_BUF_SIZE * sizeof(gchar), _("%a, %d %B %Y %X"), &tm); + new_date = g_strndup (tmp_date, dlen); + } + + return new_date; +} +#else +static gchar * +eom_exif_util_format_date_by_hand (const gchar *date) +{ + int year, month, day, hour, minutes, seconds; + int result; + gchar *new_date = NULL; + + result = sscanf (date, "%d:%d:%d %d:%d:%d", + &year, &month, &day, &hour, &minutes, &seconds); + + if (result < 3 || !g_date_valid_dmy (day, month, year)) { + return NULL; + } else { + gchar tmp_date[DATE_BUF_SIZE]; + gsize dlen; + time_t secs; + struct tm tm; + + memset (&tm, '\0', sizeof (tm)); + tm.tm_mday = day; + tm.tm_mon = month-1; + tm.tm_year = year-1900; + // Calculate tm.tm_wday + _calculate_wday_yday (&tm); + + if (result < 5) { + /* A strftime-formatted string, to display the date the image was taken, for the case we don't have the time. */ + dlen = strftime (tmp_date, DATE_BUF_SIZE * sizeof(gchar), _("%a, %d %B %Y"), &tm); + } else { + tm.tm_sec = result < 6 ? 0 : seconds; + tm.tm_min = minutes; + tm.tm_hour = hour; + /* A strftime-formatted string, to display the date the image was taken. */ + dlen = strftime (tmp_date, DATE_BUF_SIZE * sizeof(gchar), _("%a, %d %B %Y %X"), &tm); + } + + if (dlen == 0) + return NULL; + else + new_date = g_strndup (tmp_date, dlen); + } + return new_date; +} +#endif /* HAVE_STRPTIME */ + +/** + * eom_exif_util_format_date: + * @date: a date string following Exif specifications + * + * Takes a date string formatted after Exif specifications and generates a + * more readable, possibly localized, string out of it. + * + * Returns: a newly allocated date string formatted according to the + * current locale. + */ +gchar * +eom_exif_util_format_date (const gchar *date) +{ + gchar *new_date; +#ifdef HAVE_STRPTIME + new_date = eom_exif_util_format_date_with_strptime (date); +#else + new_date = eom_exif_util_format_date_by_hand (date); +#endif /* HAVE_STRPTIME */ + return new_date; +} + +/** + * eom_exif_util_get_value: + * @exif_data: pointer to an <structname>ExifData</structname> struct + * @tag_id: the requested tag's id. See <filename>exif-tag.h</filename> + * from the libexif package for possible values (e.g. %EXIF_TAG_EXPOSURE_MODE). + * @buffer: a pre-allocated output buffer + * @buf_size: size of @buffer + * + * Convenience function to extract a string representation of an Exif tag + * directly from an <structname>ExifData</structname> struct. The string is + * written into @buffer as far as @buf_size permits. + * + * Returns: a pointer to @buffer. + */ +const gchar * +eom_exif_util_get_value (ExifData *exif_data, gint tag_id, gchar *buffer, guint buf_size) +{ + ExifEntry *exif_entry; + const gchar *exif_value; + + exif_entry = exif_data_get_entry (exif_data, tag_id); + + buffer[0] = 0; + + exif_value = exif_entry_get_value (exif_entry, buffer, buf_size); + + return exif_value; +} diff --git a/src/eom-exif-util.h b/src/eom-exif-util.h new file mode 100644 index 0000000..9463524 --- /dev/null +++ b/src/eom-exif-util.h @@ -0,0 +1,41 @@ +/* Eye Of Mate - EXIF Utilities + * + * Copyright (C) 2006-2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * Author: Claudio Saavedra <[email protected]> + * Author: Felix Riemann <[email protected]> + * + * Based on code by: + * - Jens Finke <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_EXIF_UTIL_H__ +#define __EOM_EXIF_UTIL_H__ + +#include <glib.h> +#include <libexif/exif-data.h> + +G_BEGIN_DECLS + +gchar* eom_exif_util_format_date (const gchar *date); + +const gchar *eom_exif_util_get_value (ExifData *exif_data, gint tag_id, gchar *buffer, guint buf_size); + +G_END_DECLS + +#endif /* __EOM_EXIF_UTIL_H__ */ diff --git a/src/eom-file-chooser.c b/src/eom-file-chooser.c new file mode 100644 index 0000000..83a6fe2 --- /dev/null +++ b/src/eom-file-chooser.c @@ -0,0 +1,497 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eom-file-chooser.h" +#include "eom-config-keys.h" +#include "eom-pixbuf-util.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +/* We must define MATE_DESKTOP_USE_UNSTABLE_API to be able + to use MateDesktopThumbnail */ +#ifndef MATE_DESKTOP_USE_UNSTABLE_API +#define MATE_DESKTOP_USE_UNSTABLE_API +#endif +#include <libmateui/mate-desktop-thumbnail.h> + +static char *last_dir[] = { NULL, NULL, NULL, NULL }; + +#define FILE_FORMAT_KEY "file-format" + +#define EOM_FILE_CHOOSER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ + EOM_TYPE_FILE_CHOOSER, \ + EomFileChooserPrivate)) + +struct _EomFileChooserPrivate +{ + MateDesktopThumbnailFactory *thumb_factory; + + GtkWidget *image; + GtkWidget *size_label; + GtkWidget *dim_label; + GtkWidget *creator_label; +}; + +G_DEFINE_TYPE(EomFileChooser, eom_file_chooser, GTK_TYPE_FILE_CHOOSER_DIALOG) + +static void +eom_file_chooser_finalize (GObject *object) +{ + EomFileChooserPrivate *priv; + + priv = EOM_FILE_CHOOSER (object)->priv; + + if (priv->thumb_factory != NULL) + g_object_unref (priv->thumb_factory); + + (* G_OBJECT_CLASS (eom_file_chooser_parent_class)->finalize) (object); +} + +static void +eom_file_chooser_class_init (EomFileChooserClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->finalize = eom_file_chooser_finalize; + + g_type_class_add_private (object_class, sizeof (EomFileChooserPrivate)); +} + +static void +eom_file_chooser_init (EomFileChooser *chooser) +{ + chooser->priv = EOM_FILE_CHOOSER_GET_PRIVATE (chooser); +} + +static void +response_cb (GtkDialog *dlg, gint id, gpointer data) +{ + char *dir; + GtkFileChooserAction action; + + if (id == GTK_RESPONSE_OK) { + dir = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg)); + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dlg)); + + if (last_dir [action] != NULL) + g_free (last_dir [action]); + + last_dir [action] = dir; + } +} + +static void +save_response_cb (GtkDialog *dlg, gint id, gpointer data) +{ + GFile *file; + GdkPixbufFormat *format; + + if (id != GTK_RESPONSE_OK) + return; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dlg)); + format = eom_pixbuf_get_format (file); + g_object_unref (file); + + if (!format || !gdk_pixbuf_format_is_writable (format)) { + GtkWidget *msg_dialog; + + msg_dialog = gtk_message_dialog_new ( + GTK_WINDOW (dlg), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("File format is unknown or unsupported")); + + gtk_message_dialog_format_secondary_text ( + GTK_MESSAGE_DIALOG (msg_dialog), + "%s\n%s", + _("Eye of MATE could not determine a supported writable file format based on the filename."), + _("Please try a different file extension like .png or .jpg.")); + + gtk_dialog_run (GTK_DIALOG (msg_dialog)); + gtk_widget_destroy (msg_dialog); + + g_signal_stop_emission_by_name (dlg, "response"); + } else { + response_cb (dlg, id, data); + } +} + +static void +eom_file_chooser_add_filter (EomFileChooser *chooser) +{ + GSList *it; + GSList *formats; + GtkFileFilter *all_file_filter; + GtkFileFilter *all_img_filter; + GtkFileFilter *filter; + GSList *filters = NULL; + gchar **mime_types, **pattern, *tmp; + int i; + GtkFileChooserAction action; + + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (chooser)); + + if (action != GTK_FILE_CHOOSER_ACTION_SAVE && action != GTK_FILE_CHOOSER_ACTION_OPEN) { + return; + } + + /* All Files Filter */ + all_file_filter = gtk_file_filter_new (); + gtk_file_filter_set_name (all_file_filter, _("All Files")); + gtk_file_filter_add_pattern (all_file_filter, "*"); + + /* All Image Filter */ + all_img_filter = gtk_file_filter_new (); + gtk_file_filter_set_name (all_img_filter, _("All Images")); + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) { + formats = eom_pixbuf_get_savable_formats (); + } + else { + formats = gdk_pixbuf_get_formats (); + } + + /* Image filters */ + for (it = formats; it != NULL; it = it->next) { + char *filter_name; + char *description, *extension; + GdkPixbufFormat *format; + filter = gtk_file_filter_new (); + + format = (GdkPixbufFormat*) it->data; + description = gdk_pixbuf_format_get_description (format); + extension = gdk_pixbuf_format_get_name (format); + + /* Filter name: First description then file extension, eg. "The PNG-Format (*.png)".*/ + filter_name = g_strdup_printf (_("%s (*.%s)"), description, extension); + g_free (description); + g_free (extension); + + gtk_file_filter_set_name (filter, filter_name); + g_free (filter_name); + + mime_types = gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) it->data); + for (i = 0; mime_types[i] != NULL; i++) { + gtk_file_filter_add_mime_type (filter, mime_types[i]); + gtk_file_filter_add_mime_type (all_img_filter, mime_types[i]); + } + g_strfreev (mime_types); + + pattern = gdk_pixbuf_format_get_extensions ((GdkPixbufFormat *) it->data); + for (i = 0; pattern[i] != NULL; i++) { + tmp = g_strconcat ("*.", pattern[i], NULL); + gtk_file_filter_add_pattern (filter, tmp); + gtk_file_filter_add_pattern (all_img_filter, tmp); + g_free (tmp); + } + g_strfreev (pattern); + + /* attach GdkPixbufFormat to filter, see also + * eom_file_chooser_get_format. */ + g_object_set_data (G_OBJECT (filter), + FILE_FORMAT_KEY, + format); + + filters = g_slist_prepend (filters, filter); + } + g_slist_free (formats); + + /* Add filter to filechooser */ + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_file_filter); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_img_filter); + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), all_img_filter); + + for (it = filters; it != NULL; it = it->next) { + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), GTK_FILE_FILTER (it->data)); + } + g_slist_free (filters); +} + +static void +set_preview_label (GtkWidget *label, const char *str) +{ + if (str == NULL) { + gtk_widget_hide (GTK_WIDGET (label)); + } + else { + gtk_label_set_text (GTK_LABEL (label), str); + gtk_widget_show (GTK_WIDGET (label)); + } +} + +/* Sets the pixbuf as preview thumbnail and tries to read and display + * further information according to the thumbnail spec. + */ +static void +set_preview_pixbuf (EomFileChooser *chooser, GdkPixbuf *pixbuf, goffset size) +{ + EomFileChooserPrivate *priv; + int bytes; + int pixels; + const char *bytes_str; + const char *width; + const char *height; + const char *creator = NULL; + char *size_str = NULL; + char *dim_str = NULL; + + g_return_if_fail (EOM_IS_FILE_CHOOSER (chooser)); + + priv = chooser->priv; + + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); + + if (pixbuf != NULL) { + /* try to read file size */ + bytes_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Size"); + if (bytes_str != NULL) { + bytes = atoi (bytes_str); + size_str = g_format_size_for_display (bytes); + } + else { + size_str = g_format_size_for_display (size); + } + + /* try to read image dimensions */ + width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width"); + height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height"); + + if ((width != NULL) && (height != NULL)) { + pixels = atoi (height); + /* Pixel size of image: width x height in pixel */ + dim_str = g_strdup_printf ("%s x %s %s", width, height, ngettext ("pixel", "pixels", pixels)); + } + +#if 0 + /* Not sure, if this is really useful, therefore its commented out for now. */ + + /* try to read creator of the thumbnail */ + creator = gdk_pixbuf_get_option (pixbuf, "tEXt::Software"); + + /* stupid workaround to display nicer string if the + * thumbnail is created through the mate libraries. + */ + if (g_ascii_strcasecmp (creator, "Mate::ThumbnailFactory") == 0) { + creator = "MATE Libs"; + } +#endif + } + + set_preview_label (priv->size_label, size_str); + set_preview_label (priv->dim_label, dim_str); + set_preview_label (priv->creator_label, creator); + + if (size_str != NULL) { + g_free (size_str); + } + + if (dim_str != NULL) { + g_free (dim_str); + } +} + +static void +update_preview_cb (GtkFileChooser *file_chooser, gpointer data) +{ + EomFileChooserPrivate *priv; + char *uri; + char *thumb_path = NULL; + GFile *file; + GFileInfo *file_info; + GdkPixbuf *pixbuf = NULL; + gboolean have_preview = FALSE; + + priv = EOM_FILE_CHOOSER (file_chooser)->priv; + + uri = gtk_file_chooser_get_preview_uri (file_chooser); + if (uri == NULL) { + gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE); + return; + } + + file = g_file_new_for_uri (uri); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED "," + G_FILE_ATTRIBUTE_STANDARD_SIZE, + 0, NULL, NULL); + g_object_unref (file); + + if ((file_info != NULL) && (priv->thumb_factory != NULL)) { + guint64 mtime; + + mtime = g_file_info_get_attribute_uint64 (file_info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + thumb_path = mate_desktop_thumbnail_factory_lookup (priv->thumb_factory, uri, mtime); + if (thumb_path == NULL) { + /* read files smaller than 100kb directly */ + if (g_file_info_get_size (file_info) <= 100000) { + /* FIXME: we should then output also the image dimensions */ + thumb_path = gtk_file_chooser_get_preview_filename (file_chooser); + } + } + + if (thumb_path != NULL && g_file_test (thumb_path, G_FILE_TEST_EXISTS)) { + /* try to load and display preview thumbnail */ + pixbuf = gdk_pixbuf_new_from_file (thumb_path, NULL); + + have_preview = (pixbuf != NULL); + + set_preview_pixbuf (EOM_FILE_CHOOSER (file_chooser), pixbuf, + g_file_info_get_size (file_info)); + + if (pixbuf != NULL) { + g_object_unref (pixbuf); + } + } + } + + if (thumb_path != NULL) { + g_free (thumb_path); + } + + g_free (uri); + g_object_unref (file_info); + + gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview); +} + +static void +eom_file_chooser_add_preview (GtkWidget *widget) +{ + EomFileChooserPrivate *priv; + GtkWidget *vbox; + + priv = EOM_FILE_CHOOSER (widget)->priv; + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + + priv->image = gtk_image_new (); + /* 128x128 is maximum size of thumbnails */ + gtk_widget_set_size_request (priv->image, 128,128); + + priv->dim_label = gtk_label_new (NULL); + priv->size_label = gtk_label_new (NULL); + priv->creator_label = gtk_label_new (NULL); + + gtk_box_pack_start (GTK_BOX (vbox), priv->image, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->dim_label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->size_label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), priv->creator_label, FALSE, TRUE, 0); + + gtk_widget_show_all (vbox); + + gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (widget), vbox); + gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (widget), FALSE); + + priv->thumb_factory = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + + g_signal_connect (widget, "update-preview", + G_CALLBACK (update_preview_cb), NULL); +} + +GtkWidget * +eom_file_chooser_new (GtkFileChooserAction action) +{ + GtkWidget *chooser; + gchar *title = NULL; + + chooser = g_object_new (EOM_TYPE_FILE_CHOOSER, + "action", action, + "select-multiple", (action == GTK_FILE_CHOOSER_ACTION_OPEN), + "local-only", FALSE, + NULL); + + switch (action) { + case GTK_FILE_CHOOSER_ACTION_OPEN: + gtk_dialog_add_buttons (GTK_DIALOG (chooser), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + title = _("Open Image"); + break; + + case GTK_FILE_CHOOSER_ACTION_SAVE: + gtk_dialog_add_buttons (GTK_DIALOG (chooser), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + title = _("Save Image"); + break; + + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + gtk_dialog_add_buttons (GTK_DIALOG (chooser), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + title = _("Open Folder"); + break; + + default: + g_assert_not_reached (); + } + + if (action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { + eom_file_chooser_add_filter (EOM_FILE_CHOOSER (chooser)); + eom_file_chooser_add_preview (chooser); + } + + if (last_dir[action] != NULL) { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), last_dir [action]); + } + + g_signal_connect (chooser, "response", + G_CALLBACK ((action == GTK_FILE_CHOOSER_ACTION_SAVE) ? + save_response_cb : response_cb), + NULL); + + gtk_window_set_title (GTK_WINDOW (chooser), title); + gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (chooser), TRUE); + + return chooser; +} + +GdkPixbufFormat * +eom_file_chooser_get_format (EomFileChooser *chooser) +{ + GtkFileFilter *filter; + GdkPixbufFormat* format; + + g_return_val_if_fail (EOM_IS_FILE_CHOOSER (chooser), NULL); + + filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (chooser)); + if (filter == NULL) + return NULL; + + format = g_object_get_data (G_OBJECT (filter), FILE_FORMAT_KEY); + + return format; +} diff --git a/src/eom-file-chooser.h b/src/eom-file-chooser.h new file mode 100644 index 0000000..dc31fcb --- /dev/null +++ b/src/eom-file-chooser.h @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _EOM_FILE_CHOOSER_H_ +#define _EOM_FILE_CHOOSER_H_ + +#include <gtk/gtk.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +#define EOM_TYPE_FILE_CHOOSER (eom_file_chooser_get_type ()) +#define EOM_FILE_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EOM_TYPE_FILE_CHOOSER, EomFileChooser)) +#define EOM_FILE_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EOM_TYPE_FILE_CHOOSER, EomFileChooserClass)) + +#define EOM_IS_FILE_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_FILE_CHOOSER)) +#define EOM_IS_FILE_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EOM_TYPE_FILE_CHOOSER)) +#define EOM_FILE_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EOM_TYPE_FILE_CHOOSER, EomFileChooserClass)) + +typedef struct _EomFileChooser EomFileChooser; +typedef struct _EomFileChooserClass EomFileChooserClass; +typedef struct _EomFileChooserPrivate EomFileChooserPrivate; + +struct _EomFileChooser +{ + GtkFileChooserDialog parent; + + EomFileChooserPrivate *priv; +}; + +struct _EomFileChooserClass +{ + GtkFileChooserDialogClass parent_class; +}; + + +GType eom_file_chooser_get_type (void) G_GNUC_CONST; + +GtkWidget *eom_file_chooser_new (GtkFileChooserAction action); + +GdkPixbufFormat *eom_file_chooser_get_format (EomFileChooser *chooser); + + +G_END_DECLS + +#endif /* _EOM_FILE_CHOOSER_H_ */ diff --git a/src/eom-image-jpeg.c b/src/eom-image-jpeg.c new file mode 100644 index 0000000..ef7a2cc --- /dev/null +++ b/src/eom-image-jpeg.c @@ -0,0 +1,518 @@ +/* This code is based on the jpeg saving code from gdk-pixbuf. Full copyright + * notice is given in the following: + */ +/* GdkPixbuf library - JPEG image loader + * + * Copyright (C) 1999 Michael Zucchi + * Copyright (C) 1999 The Free Software Foundation + * + * Progressive loading code Copyright (C) 1999 Red Hat, Inc. + * + * Authors: Michael Zucchi <[email protected]> + * Federico Mena-Quintero <[email protected]> + * Michael Fulbright <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eom-image-jpeg.h" +#include "eom-image-private.h" + +#if HAVE_JPEG + +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> +#include <jpeglib.h> +#include <jerror.h> +#include "transupp.h" +#include <glib.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <glib/gi18n.h> +#if HAVE_EXIF +#include <libexif/exif-data.h> +#endif + +#ifdef G_OS_WIN32 +#define sigjmp_buf jmp_buf +#define sigsetjmp(env, savesigs) setjmp (env) +#define siglongjmp longjmp +#endif + +typedef enum { + EOM_SAVE_NONE, + EOM_SAVE_JPEG_AS_JPEG, + EOM_SAVE_ANY_AS_JPEG +} EomJpegSaveMethod; + +/* error handler data */ +struct error_handler_data { + struct jpeg_error_mgr pub; + sigjmp_buf setjmp_buffer; + GError **error; + char *filename; +}; + + +static void +fatal_error_handler (j_common_ptr cinfo) +{ + struct error_handler_data *errmgr; + char buffer[JMSG_LENGTH_MAX]; + + errmgr = (struct error_handler_data *) cinfo->err; + + /* Create the message */ + (* cinfo->err->format_message) (cinfo, buffer); + + /* broken check for *error == NULL for robustness against + * crappy JPEG library + */ + if (errmgr->error && *errmgr->error == NULL) { + g_set_error (errmgr->error, + 0, + 0, + "Error interpreting JPEG image file: %s\n\n%s", + g_path_get_basename (errmgr->filename), + buffer); + } + + siglongjmp (errmgr->setjmp_buffer, 1); + + g_assert_not_reached (); +} + + +static void +output_message_handler (j_common_ptr cinfo) +{ + /* This method keeps libjpeg from dumping crap to stderr */ + /* do nothing */ +} + +static void +init_transform_info (EomImage *image, jpeg_transform_info *info) +{ + EomImagePrivate *priv; + EomTransform *composition = NULL; + EomTransformType transformation; + JXFORM_CODE trans_code = JXFORM_NONE; + + g_return_if_fail (EOM_IS_IMAGE (image)); + + priv = image->priv; + + if (priv->trans != NULL && priv->trans_autorotate != NULL) { + composition = eom_transform_compose (priv->trans, + priv->trans_autorotate); + } else if (priv->trans != NULL) { + composition = g_object_ref (priv->trans); + } else if (priv->trans_autorotate != NULL) { + composition = g_object_ref (priv->trans_autorotate); + } + + if (composition != NULL) { + transformation = eom_transform_get_transform_type (composition); + + switch (transformation) { + case EOM_TRANSFORM_ROT_90: + trans_code = JXFORM_ROT_90; + break; + case EOM_TRANSFORM_ROT_270: + trans_code = JXFORM_ROT_270; + break; + case EOM_TRANSFORM_ROT_180: + trans_code = JXFORM_ROT_180; + break; + case EOM_TRANSFORM_FLIP_HORIZONTAL: + trans_code = JXFORM_FLIP_H; + break; + case EOM_TRANSFORM_FLIP_VERTICAL: + trans_code = JXFORM_FLIP_V; + break; + default: + trans_code = JXFORM_NONE; + break; + } + } + + info->transform = trans_code; + info->trim = FALSE; +#if JPEG_LIB_VERSION >= 80 + info->crop = FALSE; +#endif + info->force_grayscale = FALSE; + + g_object_unref (composition); +} + +static gboolean +_save_jpeg_as_jpeg (EomImage *image, const char *file, EomImageSaveInfo *source, + EomImageSaveInfo *target, GError **error) +{ + struct jpeg_decompress_struct srcinfo; + struct jpeg_compress_struct dstinfo; + struct error_handler_data jsrcerr, jdsterr; + jpeg_transform_info transformoption; + jvirt_barray_ptr *src_coef_arrays; + jvirt_barray_ptr *dst_coef_arrays; + FILE *output_file; + FILE *input_file; + EomImagePrivate *priv; + gchar *infile_uri; + + g_return_val_if_fail (EOM_IS_IMAGE (image), FALSE); + g_return_val_if_fail (EOM_IMAGE (image)->priv->file != NULL, FALSE); + + priv = image->priv; + + init_transform_info (image, &transformoption); + + /* Initialize the JPEG decompression object with default error + * handling. */ + jsrcerr.filename = g_file_get_path (priv->file); + srcinfo.err = jpeg_std_error (&(jsrcerr.pub)); + jsrcerr.pub.error_exit = fatal_error_handler; + jsrcerr.pub.output_message = output_message_handler; + jsrcerr.error = error; + + jpeg_create_decompress (&srcinfo); + + /* Initialize the JPEG compression object with default error + * handling. */ + jdsterr.filename = (char *) file; + dstinfo.err = jpeg_std_error (&(jdsterr.pub)); + jdsterr.pub.error_exit = fatal_error_handler; + jdsterr.pub.output_message = output_message_handler; + jdsterr.error = error; + + jpeg_create_compress (&dstinfo); + + dstinfo.err->trace_level = 0; + dstinfo.arith_code = FALSE; + dstinfo.optimize_coding = FALSE; + + jsrcerr.pub.trace_level = jdsterr.pub.trace_level; + srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use; + + /* Open the output file. */ + /* FIXME: Make this a GIO aware input manager */ + infile_uri = g_file_get_path (priv->file); + input_file = fopen (infile_uri, "rb"); + if (input_file == NULL) { + g_warning ("Input file not openable: %s\n", infile_uri); + g_free (jsrcerr.filename); + g_free (infile_uri); + return FALSE; + } + g_free (infile_uri); + + output_file = fopen (file, "wb"); + if (output_file == NULL) { + g_warning ("Output file not openable: %s\n", file); + fclose (input_file); + g_free (jsrcerr.filename); + return FALSE; + } + + if (sigsetjmp (jsrcerr.setjmp_buffer, 1)) { + fclose (output_file); + fclose (input_file); + jpeg_destroy_compress (&dstinfo); + jpeg_destroy_decompress (&srcinfo); + g_free (jsrcerr.filename); + return FALSE; + } + + if (sigsetjmp (jdsterr.setjmp_buffer, 1)) { + fclose (output_file); + fclose (input_file); + jpeg_destroy_compress (&dstinfo); + jpeg_destroy_decompress (&srcinfo); + g_free (jsrcerr.filename); + return FALSE; + } + + /* Specify data source for decompression */ + jpeg_stdio_src (&srcinfo, input_file); + + /* Enable saving of extra markers that we want to copy */ + jcopy_markers_setup (&srcinfo, JCOPYOPT_DEFAULT); + + /* Read file header */ + (void) jpeg_read_header (&srcinfo, TRUE); + + /* Any space needed by a transform option must be requested before + * jpeg_read_coefficients so that memory allocation will be done right. + */ + jtransform_request_workspace (&srcinfo, &transformoption); + + /* Read source file as DCT coefficients */ + src_coef_arrays = jpeg_read_coefficients (&srcinfo); + + /* Initialize destination compression parameters from source values */ + jpeg_copy_critical_parameters (&srcinfo, &dstinfo); + + /* Adjust destination parameters if required by transform options; + * also find out which set of coefficient arrays will hold the output. + */ + dst_coef_arrays = jtransform_adjust_parameters (&srcinfo, + &dstinfo, + src_coef_arrays, + &transformoption); + + /* Specify data destination for compression */ + jpeg_stdio_dest (&dstinfo, output_file); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients (&dstinfo, dst_coef_arrays); + + /* handle EXIF/IPTC data explicitly */ +#if HAVE_EXIF + /* exif_chunk and exif are mutally exclusvie, this is what we assure here */ + g_assert (priv->exif_chunk == NULL); + if (priv->exif != NULL) + { + unsigned char *exif_buf; + unsigned int exif_buf_len; + + exif_data_save_data (priv->exif, &exif_buf, &exif_buf_len); + jpeg_write_marker (&dstinfo, JPEG_APP0+1, exif_buf, exif_buf_len); + g_free (exif_buf); + } +#else + if (priv->exif_chunk != NULL) { + jpeg_write_marker (&dstinfo, JPEG_APP0+1, priv->exif_chunk, priv->exif_chunk_len); + } +#endif + /* FIXME: Consider IPTC data too */ + + /* Copy to the output file any extra markers that we want to + * preserve */ + jcopy_markers_execute (&srcinfo, &dstinfo, JCOPYOPT_DEFAULT); + + /* Execute image transformation, if any */ + jtransform_execute_transformation (&srcinfo, + &dstinfo, + src_coef_arrays, + &transformoption); + + /* Finish compression and release memory */ + jpeg_finish_compress (&dstinfo); + jpeg_destroy_compress (&dstinfo); + (void) jpeg_finish_decompress (&srcinfo); + jpeg_destroy_decompress (&srcinfo); + g_free (jsrcerr.filename); + + /* Close files */ + fclose (input_file); + fclose (output_file); + + return TRUE; +} + +static gboolean +_save_any_as_jpeg (EomImage *image, const char *file, EomImageSaveInfo *source, + EomImageSaveInfo *target, GError **error) +{ + EomImagePrivate *priv; + GdkPixbuf *pixbuf; + struct jpeg_compress_struct cinfo; + guchar *buf = NULL; + guchar *ptr; + guchar *pixels = NULL; + JSAMPROW *jbuf; + int y = 0; + volatile int quality = 75; /* default; must be between 0 and 100 */ + int i, j; + int w, h = 0; + int rowstride = 0; + FILE *outfile; + struct error_handler_data jerr; + + g_return_val_if_fail (EOM_IS_IMAGE (image), FALSE); + g_return_val_if_fail (EOM_IMAGE (image)->priv->image != NULL, FALSE); + + priv = image->priv; + pixbuf = priv->image; + + outfile = fopen (file, "wb"); + if (outfile == NULL) { + g_set_error (error, /* FIXME: Better error message */ + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + _("Couldn't create temporary file for saving: %s"), + file); + return FALSE; + } + + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + + /* no image data? abort */ + pixels = gdk_pixbuf_get_pixels (pixbuf); + g_return_val_if_fail (pixels != NULL, FALSE); + + /* allocate a small buffer to convert image data */ + buf = g_try_malloc (w * 3 * sizeof (guchar)); + if (!buf) { + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + _("Couldn't allocate memory for loading JPEG file")); + fclose (outfile); + return FALSE; + } + + /* set up error handling */ + jerr.filename = (char *) file; + cinfo.err = jpeg_std_error (&(jerr.pub)); + jerr.pub.error_exit = fatal_error_handler; + jerr.pub.output_message = output_message_handler; + jerr.error = error; + + /* setup compress params */ + jpeg_create_compress (&cinfo); + jpeg_stdio_dest (&cinfo, outfile); + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + /* error exit routine */ + if (sigsetjmp (jerr.setjmp_buffer, 1)) { + g_free (buf); + fclose (outfile); + jpeg_destroy_compress (&cinfo); + return FALSE; + } + + /* set desired jpeg quality if available */ + if (target != NULL && target->jpeg_quality >= 0.0) { + quality = (int) MIN (target->jpeg_quality, 1.0) * 100; + } + + /* set up jepg compression parameters */ + jpeg_set_defaults (&cinfo); + jpeg_set_quality (&cinfo, quality, TRUE); + jpeg_start_compress (&cinfo, TRUE); + + /* write EXIF/IPTC data explicitly */ +#if HAVE_EXIF + /* exif_chunk and exif are mutally exclusvie, this is what we assure here */ + g_assert (priv->exif_chunk == NULL); + if (priv->exif != NULL) + { + unsigned char *exif_buf; + unsigned int exif_buf_len; + + exif_data_save_data (priv->exif, &exif_buf, &exif_buf_len); + jpeg_write_marker (&cinfo, 0xe1, exif_buf, exif_buf_len); + g_free (exif_buf); + } +#else + if (priv->exif_chunk != NULL) { + jpeg_write_marker (&cinfo, JPEG_APP0+1, priv->exif_chunk, priv->exif_chunk_len); + } +#endif + /* FIXME: Consider IPTC data too */ + + /* get the start pointer */ + ptr = pixels; + /* go one scanline at a time... and save */ + i = 0; + while (cinfo.next_scanline < cinfo.image_height) { + /* convert scanline from ARGB to RGB packed */ + for (j = 0; j < w; j++) + memcpy (&(buf[j*3]), &(ptr[i*rowstride + j*(rowstride/w)]), 3); + + /* write scanline */ + jbuf = (JSAMPROW *)(&buf); + jpeg_write_scanlines (&cinfo, jbuf, 1); + i++; + y++; + + } + + /* finish off */ + jpeg_finish_compress (&cinfo); + jpeg_destroy_compress(&cinfo); + g_free (buf); + + fclose (outfile); + + return TRUE; +} + +gboolean +eom_image_jpeg_save_file (EomImage *image, const char *file, + EomImageSaveInfo *source, EomImageSaveInfo *target, + GError **error) +{ + EomJpegSaveMethod method = EOM_SAVE_NONE; + gboolean source_is_jpeg = FALSE; + gboolean target_is_jpeg = FALSE; + gboolean result; + + g_return_val_if_fail (source != NULL, FALSE); + + source_is_jpeg = !g_ascii_strcasecmp (source->format, EOM_FILE_FORMAT_JPEG); + + /* determine which method should be used for saving */ + if (target == NULL) { + if (source_is_jpeg) { + method = EOM_SAVE_JPEG_AS_JPEG; + } + } + else { + target_is_jpeg = !g_ascii_strcasecmp (target->format, EOM_FILE_FORMAT_JPEG); + + if (source_is_jpeg && target_is_jpeg) { + if (target->jpeg_quality < 0.0) { + method = EOM_SAVE_JPEG_AS_JPEG; + } + else { + /* reencoding is required, cause quality is set */ + method = EOM_SAVE_ANY_AS_JPEG; + } + } + else if (!source_is_jpeg && target_is_jpeg) { + method = EOM_SAVE_ANY_AS_JPEG; + } + } + + switch (method) { + case EOM_SAVE_JPEG_AS_JPEG: + result = _save_jpeg_as_jpeg (image, file, source, target, error); + break; + case EOM_SAVE_ANY_AS_JPEG: + result = _save_any_as_jpeg (image, file, source, target, error); + break; + default: + result = FALSE; + } + + return result; +} +#endif diff --git a/src/eom-image-jpeg.h b/src/eom-image-jpeg.h new file mode 100644 index 0000000..4e43d0d --- /dev/null +++ b/src/eom-image-jpeg.h @@ -0,0 +1,22 @@ +#ifndef _EOM_IMAGE_JPEG_H_ +#define _EOM_IMAGE_JPEG_H_ + +#if HAVE_JPEG + +#include <glib.h> +#include "eom-image.h" +#include "eom-image-save-info.h" + +/* Saves a source jpeg file in an arbitrary format (as specified by + * target). The target pointer may be NULL, in which case the output + * file is saved as jpeg too. This method tries to be as smart as + * possible. It will save the image as lossless as possible (if the + * target is a jpeg image too). + */ +G_GNUC_INTERNAL +gboolean eom_image_jpeg_save_file (EomImage *image, const char *file, + EomImageSaveInfo *source, EomImageSaveInfo *target, + GError **error); +#endif + +#endif /* _EOM_IMAGE_JPEG_H_ */ diff --git a/src/eom-image-private.h b/src/eom-image-private.h new file mode 100644 index 0000000..794d4ad --- /dev/null +++ b/src/eom-image-private.h @@ -0,0 +1,96 @@ +/* Eye Of Mate - Image Private Data + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_IMAGE_PRIVATE_H__ +#define __EOM_IMAGE_PRIVATE_H__ + +#include "eom-image.h" +#ifdef HAVE_RSVG +#include <librsvg/rsvg.h> +#endif + +G_BEGIN_DECLS + +struct _EomImagePrivate { + GFile *file; + + EomImageStatus status; + EomImageStatus prev_status; + gboolean is_monitored; + EomImageMetadataStatus metadata_status; + + GdkPixbuf *image; + GdkPixbufAnimation *anim; + GdkPixbufAnimationIter *anim_iter; + gboolean is_playing; + GdkPixbuf *thumbnail; +#ifdef HAVE_RSVG + RsvgHandle *svg; +#endif + + gint width; + gint height; + + goffset bytes; + gchar *file_type; + gboolean threadsafe_format; + + /* Holds EXIF raw data */ + guint exif_chunk_len; + guchar *exif_chunk; + + /* Holds IPTC raw data */ + guchar *iptc_chunk; + guint iptc_chunk_len; + + gboolean modified; + +#ifdef HAVE_EXIF + gboolean autorotate; + gint orientation; + ExifData *exif; +#endif +#ifdef HAVE_EXEMPI + XmpPtr xmp; +#endif + +#ifdef HAVE_LCMS + cmsHPROFILE profile; +#endif + + gchar *caption; + + gchar *collate_key; + + GMutex *status_mutex; + + gboolean cancel_loading; + guint data_ref_count; + + GSList *undo_stack; + + EomTransform *trans; + EomTransform *trans_autorotate; +}; + +G_END_DECLS + +#endif /* __EOM_IMAGE_PRIVATE_H__ */ diff --git a/src/eom-image-save-info.c b/src/eom-image-save-info.c new file mode 100644 index 0000000..d368a61 --- /dev/null +++ b/src/eom-image-save-info.c @@ -0,0 +1,150 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include "eom-image-save-info.h" +#include "eom-image-private.h" +#include "eom-pixbuf-util.h" +#include "eom-image.h" + +G_DEFINE_TYPE (EomImageSaveInfo, eom_image_save_info, G_TYPE_OBJECT) + +static void +eom_image_save_info_dispose (GObject *object) +{ + EomImageSaveInfo *info = EOM_IMAGE_SAVE_INFO (object); + + if (info->file != NULL) { + g_object_unref (info->file); + info->file = NULL; + } + + if (info->format != NULL) { + g_free (info->format); + info->format = NULL; + } + + G_OBJECT_CLASS (eom_image_save_info_parent_class)->dispose (object); +} + +static void +eom_image_save_info_init (EomImageSaveInfo *obj) +{ + +} + +static void +eom_image_save_info_class_init (EomImageSaveInfoClass *klass) +{ + GObjectClass *object_class = (GObjectClass*) klass; + + object_class->dispose = eom_image_save_info_dispose; +} + +/* is_local_uri: + * + * Checks if the URI points to a local file system. This tests simply + * if the URI scheme is 'file'. This function is used to ensure that + * we can write to the path-part of the URI with non-VFS aware + * filesystem calls. + */ +static gboolean +is_local_file (GFile *file) +{ + char *scheme; + gboolean ret; + + g_return_val_if_fail (file != NULL, FALSE); + + scheme = g_file_get_uri_scheme (file); + + ret = (g_ascii_strcasecmp (scheme, "file") == 0); + g_free (scheme); + return ret; +} + +static char* +get_save_file_type_by_file (GFile *file) +{ + GdkPixbufFormat *format; + char *type = NULL; + + format = eom_pixbuf_get_format (file); + if (format != NULL) { + type = gdk_pixbuf_format_get_name (format); + } + + return type; +} + +EomImageSaveInfo* +eom_image_save_info_from_image (gpointer data) +{ + EomImageSaveInfo *info = NULL; + EomImage *image; + + image = EOM_IMAGE (data); + + g_return_val_if_fail (EOM_IS_IMAGE (image), NULL); + + info = g_object_new (EOM_TYPE_IMAGE_SAVE_INFO, NULL); + + info->file = eom_image_get_file (image); + info->format = g_strdup (image->priv->file_type); + info->exists = g_file_query_exists (info->file, NULL); + info->local = is_local_file (info->file); + info->has_metadata = eom_image_has_data (image, EOM_IMAGE_DATA_EXIF); + info->modified = eom_image_is_modified (image); + info->overwrite = FALSE; + + info->jpeg_quality = -1.0; + + return info; +} + +EomImageSaveInfo* +eom_image_save_info_from_uri (const char *txt_uri, GdkPixbufFormat *format) +{ + GFile *file; + EomImageSaveInfo *info; + + g_return_val_if_fail (txt_uri != NULL, NULL); + + file = g_file_new_for_uri (txt_uri); + + info = eom_image_save_info_from_file (file, format); + + g_object_unref (file); + + return info; +} + +EomImageSaveInfo* +eom_image_save_info_from_file (GFile *file, GdkPixbufFormat *format) +{ + EomImageSaveInfo *info; + + g_return_val_if_fail (file != NULL, NULL); + + info = g_object_new (EOM_TYPE_IMAGE_SAVE_INFO, NULL); + + info->file = g_object_ref (file); + if (format == NULL) { + info->format = get_save_file_type_by_file (info->file); + } + else { + info->format = gdk_pixbuf_format_get_name (format); + } + info->exists = g_file_query_exists (file, NULL); + info->local = is_local_file (file); + info->has_metadata = FALSE; + info->modified = FALSE; + info->overwrite = FALSE; + + info->jpeg_quality = -1.0; + + g_assert (info->format != NULL); + + return info; +} diff --git a/src/eom-image-save-info.h b/src/eom-image-save-info.h new file mode 100644 index 0000000..8a3c36f --- /dev/null +++ b/src/eom-image-save-info.h @@ -0,0 +1,54 @@ +#ifndef _EOM_IMAGE_SAVE_INFO_H_ +#define _EOM_IMAGE_SAVE_INFO_H_ + +#include <glib-object.h> +#include <gio/gio.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +struct EomImage; + +#define EOM_TYPE_IMAGE_SAVE_INFO (eom_image_save_info_get_type ()) +#define EOM_IMAGE_SAVE_INFO(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EOM_TYPE_IMAGE_SAVE_INFO, EomImageSaveInfo)) +#define EOM_IMAGE_SAVE_INFO_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EOM_TYPE_IMAGE_SAVE_INFO, EomImageSaveInfoClass)) +#define EOM_IS_IMAGE_SAVE_INFO(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_IMAGE_SAVE_INFO)) +#define EOM_IS_IMAGE_SAVE_INFO_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EOM_TYPE_IMAGE_SAVE_INFO)) +#define EOM_IMAGE_SAVE_INFO_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EOM_TYPE_IMAGE_SAVE_INFO, EomImageSaveInfoClass)) + +typedef struct _EomImageSaveInfo EomImageSaveInfo; +typedef struct _EomImageSaveInfoClass EomImageSaveInfoClass; + +struct _EomImageSaveInfo { + GObject parent; + + GFile *file; + char *format; + gboolean exists; + gboolean local; + gboolean has_metadata; + gboolean modified; + gboolean overwrite; + + float jpeg_quality; /* valid range: [0.0 ... 1.0] */ +}; + +struct _EomImageSaveInfoClass { + GObjectClass parent_klass; +}; + +#define EOM_FILE_FORMAT_JPEG "jpeg" + +GType eom_image_save_info_get_type (void) G_GNUC_CONST; + +EomImageSaveInfo *eom_image_save_info_from_image (gpointer data); + +EomImageSaveInfo *eom_image_save_info_from_uri (const char *uri, + GdkPixbufFormat *format); + +EomImageSaveInfo *eom_image_save_info_from_file (GFile *file, + GdkPixbufFormat *format); + +G_END_DECLS + +#endif /* _EOM_IMAGE_SAVE_INFO_H_ */ diff --git a/src/eom-image.c b/src/eom-image.c new file mode 100644 index 0000000..e57e7cb --- /dev/null +++ b/src/eom-image.c @@ -0,0 +1,2237 @@ +/* Eye Of Mate - Image + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GDK_PIXBUF_ENABLE_BACKEND + +#include "eom-image.h" +#include "eom-image-private.h" +#include "eom-debug.h" + +#ifdef HAVE_JPEG +#include "eom-image-jpeg.h" +#endif + +#include "eom-marshal.h" +#include "eom-pixbuf-util.h" +#include "eom-metadata-reader.h" +#include "eom-image-save-info.h" +#include "eom-transform.h" +#include "eom-util.h" +#include "eom-jobs.h" +#include "eom-thumbnail.h" + +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#ifdef HAVE_EXIF +#include <libexif/exif-data.h> +#include <libexif/exif-utils.h> +#include <libexif/exif-loader.h> +#endif + +#ifdef HAVE_LCMS +#include <lcms.h> +#ifndef EXIF_TAG_GAMMA +#define EXIF_TAG_GAMMA 0xa500 +#endif +#endif + +#define EOM_IMAGE_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_IMAGE, EomImagePrivate)) + +G_DEFINE_TYPE (EomImage, eom_image, G_TYPE_OBJECT) + +enum { + SIGNAL_CHANGED, + SIGNAL_SIZE_PREPARED, + SIGNAL_THUMBNAIL_CHANGED, + SIGNAL_SAVE_PROGRESS, + SIGNAL_NEXT_FRAME, + SIGNAL_FILE_CHANGED, + SIGNAL_LAST +}; + +static gint signals[SIGNAL_LAST]; + +static GList *supported_mime_types = NULL; + +#define EOM_IMAGE_READ_BUFFER_SIZE 65535 + +static void +eom_image_free_mem_private (EomImage *image) +{ + EomImagePrivate *priv; + + priv = image->priv; + + if (priv->status == EOM_IMAGE_STATUS_LOADING) { + eom_image_cancel_load (image); + } else { + if (priv->anim_iter != NULL) { + g_object_unref (priv->anim_iter); + priv->anim_iter = NULL; + } + + if (priv->anim != NULL) { + g_object_unref (priv->anim); + priv->anim = NULL; + } + + priv->is_playing = FALSE; + + if (priv->image != NULL) { + g_object_unref (priv->image); + priv->image = NULL; + } + +#ifdef HAVE_RSVG + if (priv->svg != NULL) { + g_object_unref (priv->svg); + priv->svg = NULL; + } +#endif + +#ifdef HAVE_EXIF + if (priv->exif != NULL) { + exif_data_unref (priv->exif); + priv->exif = NULL; + } +#endif + + if (priv->exif_chunk != NULL) { + g_free (priv->exif_chunk); + priv->exif_chunk = NULL; + } + + priv->exif_chunk_len = 0; + +#ifdef HAVE_EXEMPI + if (priv->xmp != NULL) { + xmp_free (priv->xmp); + priv->xmp = NULL; + } +#endif + +#ifdef HAVE_LCMS + if (priv->profile != NULL) { + cmsCloseProfile (priv->profile); + priv->profile = NULL; + } +#endif + + priv->status = EOM_IMAGE_STATUS_UNKNOWN; + } +} + +static void +eom_image_dispose (GObject *object) +{ + EomImagePrivate *priv; + + priv = EOM_IMAGE (object)->priv; + + eom_image_free_mem_private (EOM_IMAGE (object)); + + if (priv->file) { + g_object_unref (priv->file); + priv->file = NULL; + } + + if (priv->caption) { + g_free (priv->caption); + priv->caption = NULL; + } + + if (priv->collate_key) { + g_free (priv->collate_key); + priv->collate_key = NULL; + } + + if (priv->file_type) { + g_free (priv->file_type); + priv->file_type = NULL; + } + + if (priv->status_mutex) { + g_mutex_free (priv->status_mutex); + priv->status_mutex = NULL; + } + + if (priv->trans) { + g_object_unref (priv->trans); + priv->trans = NULL; + } + + if (priv->trans_autorotate) { + g_object_unref (priv->trans_autorotate); + priv->trans_autorotate = NULL; + } + + if (priv->undo_stack) { + g_slist_foreach (priv->undo_stack, (GFunc) g_object_unref, NULL); + g_slist_free (priv->undo_stack); + priv->undo_stack = NULL; + } + + G_OBJECT_CLASS (eom_image_parent_class)->dispose (object); +} + +static void +eom_image_class_init (EomImageClass *klass) +{ + GObjectClass *object_class = (GObjectClass*) klass; + + object_class->dispose = eom_image_dispose; + + signals[SIGNAL_SIZE_PREPARED] = + g_signal_new ("size-prepared", + EOM_TYPE_IMAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomImageClass, size_prepared), + NULL, NULL, + eom_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); + + signals[SIGNAL_CHANGED] = + g_signal_new ("changed", + EOM_TYPE_IMAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomImageClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[SIGNAL_THUMBNAIL_CHANGED] = + g_signal_new ("thumbnail-changed", + EOM_TYPE_IMAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomImageClass, thumbnail_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[SIGNAL_SAVE_PROGRESS] = + g_signal_new ("save-progress", + EOM_TYPE_IMAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomImageClass, save_progress), + NULL, NULL, + g_cclosure_marshal_VOID__FLOAT, + G_TYPE_NONE, 1, + G_TYPE_FLOAT); + /** + * EomImage::next-frame: + * @img: the object which received the signal. + * @delay: number of milliseconds the current frame will be displayed. + * + * The ::next-frame signal will be emitted each time an animated image + * advances to the next frame. + */ + signals[SIGNAL_NEXT_FRAME] = + g_signal_new ("next-frame", + EOM_TYPE_IMAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomImageClass, next_frame), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + signals[SIGNAL_FILE_CHANGED] = g_signal_new ("file-changed", + EOM_TYPE_IMAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomImageClass, file_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (object_class, sizeof (EomImagePrivate)); +} + +static void +eom_image_init (EomImage *img) +{ + img->priv = EOM_IMAGE_GET_PRIVATE (img); + + img->priv->file = NULL; + img->priv->image = NULL; + img->priv->anim = NULL; + img->priv->anim_iter = NULL; + img->priv->is_playing = FALSE; + img->priv->thumbnail = NULL; + img->priv->width = -1; + img->priv->height = -1; + img->priv->modified = FALSE; + img->priv->status_mutex = g_mutex_new (); + img->priv->status = EOM_IMAGE_STATUS_UNKNOWN; + img->priv->metadata_status = EOM_IMAGE_METADATA_NOT_READ; + img->priv->is_monitored = FALSE; + img->priv->undo_stack = NULL; + img->priv->trans = NULL; + img->priv->trans_autorotate = NULL; + img->priv->data_ref_count = 0; +#ifdef HAVE_EXIF + img->priv->orientation = 0; + img->priv->autorotate = FALSE; + img->priv->exif = NULL; +#endif +#ifdef HAVE_EXEMPI + img->priv->xmp = NULL; +#endif +#ifdef HAVE_LCMS + img->priv->profile = NULL; +#endif +#ifdef HAVE_RSVG + img->priv->svg = NULL; +#endif +} + +EomImage * +eom_image_new (const char *txt_uri) +{ + EomImage *img; + + img = EOM_IMAGE (g_object_new (EOM_TYPE_IMAGE, NULL)); + + img->priv->file = g_file_new_for_uri (txt_uri); + + return img; +} + +EomImage * +eom_image_new_file (GFile *file) +{ + EomImage *img; + + img = EOM_IMAGE (g_object_new (EOM_TYPE_IMAGE, NULL)); + + img->priv->file = g_object_ref (file); + + return img; +} + +GQuark +eom_image_error_quark (void) +{ + static GQuark q = 0; + + if (q == 0) { + q = g_quark_from_static_string ("eom-image-error-quark"); + } + + return q; +} + +static void +eom_image_update_exif_data (EomImage *image) +{ +#ifdef HAVE_EXIF + EomImagePrivate *priv; + ExifEntry *entry; + ExifByteOrder bo; + + eom_debug (DEBUG_IMAGE_DATA); + + g_return_if_fail (EOM_IS_IMAGE (image)); + + priv = image->priv; + + if (priv->exif == NULL) return; + + bo = exif_data_get_byte_order (priv->exif); + + /* Update image width */ + entry = exif_data_get_entry (priv->exif, EXIF_TAG_PIXEL_X_DIMENSION); + if (entry != NULL && (priv->width >= 0)) { + if (entry->format == EXIF_FORMAT_LONG) + exif_set_long (entry->data, bo, priv->width); + else if (entry->format == EXIF_FORMAT_SHORT) + exif_set_short (entry->data, bo, priv->width); + else + g_warning ("Exif entry has unsupported size"); + } + + /* Update image height */ + entry = exif_data_get_entry (priv->exif, EXIF_TAG_PIXEL_Y_DIMENSION); + if (entry != NULL && (priv->height >= 0)) { + if (entry->format == EXIF_FORMAT_LONG) + exif_set_long (entry->data, bo, priv->height); + else if (entry->format == EXIF_FORMAT_SHORT) + exif_set_short (entry->data, bo, priv->height); + else + g_warning ("Exif entry has unsupported size"); + } + + /* Update image orientation */ + entry = exif_data_get_entry (priv->exif, EXIF_TAG_ORIENTATION); + if (entry != NULL) { + if (entry->format == EXIF_FORMAT_LONG) + exif_set_long (entry->data, bo, 1); + else if (entry->format == EXIF_FORMAT_SHORT) + exif_set_short (entry->data, bo, 1); + else + g_warning ("Exif entry has unsupported size"); + + priv->orientation = 1; + } +#endif +} + +static void +eom_image_real_transform (EomImage *img, + EomTransform *trans, + gboolean is_undo, + EomJob *job) +{ + EomImagePrivate *priv; + GdkPixbuf *transformed; + gboolean modified = FALSE; + + g_return_if_fail (EOM_IS_IMAGE (img)); + g_return_if_fail (EOM_IS_TRANSFORM (trans)); + + priv = img->priv; + + if (priv->image != NULL) { + transformed = eom_transform_apply (trans, priv->image, job); + + g_object_unref (priv->image); + priv->image = transformed; + + priv->width = gdk_pixbuf_get_width (transformed); + priv->height = gdk_pixbuf_get_height (transformed); + + modified = TRUE; + } + + if (priv->thumbnail != NULL) { + transformed = eom_transform_apply (trans, priv->thumbnail, NULL); + + g_object_unref (priv->thumbnail); + priv->thumbnail = transformed; + + modified = TRUE; + } + + if (modified) { + priv->modified = TRUE; + eom_image_update_exif_data (img); + } + + if (priv->trans == NULL) { + g_object_ref (trans); + priv->trans = trans; + } else { + EomTransform *composition; + + composition = eom_transform_compose (priv->trans, trans); + + g_object_unref (priv->trans); + + priv->trans = composition; + } + + if (!is_undo) { + g_object_ref (trans); + priv->undo_stack = g_slist_prepend (priv->undo_stack, trans); + } +} + +static gboolean +do_emit_size_prepared_signal (EomImage *img) +{ + g_signal_emit (img, signals[SIGNAL_SIZE_PREPARED], 0, + img->priv->width, img->priv->height); + return FALSE; +} + +static void +eom_image_emit_size_prepared (EomImage *img) +{ + gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) do_emit_size_prepared_signal, + g_object_ref (img), g_object_unref); +} + +static gboolean +check_loader_threadsafety (GdkPixbufLoader *loader, gboolean *result) +{ + GdkPixbufFormat *format; + gboolean ret_val = FALSE; + + format = gdk_pixbuf_loader_get_format (loader); + if (format) { + ret_val = TRUE; + if (result) + /* FIXME: We should not be accessing this struct internals + * directly. Keep track of bug #469209 to fix that. */ + *result = format->flags & GDK_PIXBUF_FORMAT_THREADSAFE; + } + + return ret_val; +} + +static void +eom_image_pre_size_prepared (GdkPixbufLoader *loader, + gint width, + gint height, + gpointer data) +{ + EomImage *img; + + eom_debug (DEBUG_IMAGE_LOAD); + + g_return_if_fail (EOM_IS_IMAGE (data)); + + img = EOM_IMAGE (data); + check_loader_threadsafety (loader, &img->priv->threadsafe_format); +} + +static void +eom_image_size_prepared (GdkPixbufLoader *loader, + gint width, + gint height, + gpointer data) +{ + EomImage *img; + + eom_debug (DEBUG_IMAGE_LOAD); + + g_return_if_fail (EOM_IS_IMAGE (data)); + + img = EOM_IMAGE (data); + + g_mutex_lock (img->priv->status_mutex); + + img->priv->width = width; + img->priv->height = height; + + g_mutex_unlock (img->priv->status_mutex); + +#ifdef HAVE_EXIF + if (img->priv->threadsafe_format && (!img->priv->autorotate || img->priv->exif)) +#else + if (img->priv->threadsafe_format) +#endif + eom_image_emit_size_prepared (img); +} + +static EomMetadataReader* +check_for_metadata_img_format (EomImage *img, guchar *buffer, guint bytes_read) +{ + EomMetadataReader *md_reader = NULL; + + eom_debug_message (DEBUG_IMAGE_DATA, "Check image format for jpeg: %x%x - length: %i", + buffer[0], buffer[1], bytes_read); + + if (bytes_read >= 2) { + /* SOI (start of image) marker for JPEGs is 0xFFD8 */ + if ((buffer[0] == 0xFF) && (buffer[1] == 0xD8)) { + md_reader = eom_metadata_reader_new (EOM_METADATA_JPEG); + } + if (bytes_read >= 8 && + memcmp (buffer, "\x89PNG\x0D\x0A\x1a\x0A", 8) == 0) { + md_reader = eom_metadata_reader_new (EOM_METADATA_PNG); + } + } + + return md_reader; +} + +static gboolean +eom_image_needs_transformation (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + + return (img->priv->trans != NULL || img->priv->trans_autorotate != NULL); +} + +static gboolean +eom_image_apply_transformations (EomImage *img, GError **error) +{ + GdkPixbuf *transformed = NULL; + EomTransform *composition = NULL; + EomImagePrivate *priv; + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + + priv = img->priv; + + if (priv->trans == NULL && priv->trans_autorotate == NULL) { + return TRUE; + } + + if (priv->image == NULL) { + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_NOT_LOADED, + _("Transformation on unloaded image.")); + + return FALSE; + } + + if (priv->trans != NULL && priv->trans_autorotate != NULL) { + composition = eom_transform_compose (priv->trans, + priv->trans_autorotate); + } else if (priv->trans != NULL) { + composition = g_object_ref (priv->trans); + } else if (priv->trans_autorotate != NULL) { + composition = g_object_ref (priv->trans_autorotate); + } + + if (composition != NULL) { + transformed = eom_transform_apply (composition, priv->image, NULL); + } + + g_object_unref (priv->image); + priv->image = transformed; + + if (transformed != NULL) { + priv->width = gdk_pixbuf_get_width (priv->image); + priv->height = gdk_pixbuf_get_height (priv->image); + } else { + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_GENERIC, + _("Transformation failed.")); + } + + g_object_unref (composition); + + return (transformed != NULL); +} + +static void +eom_image_get_file_info (EomImage *img, + goffset *bytes, + gchar **mime_type, + GError **error) +{ + GFileInfo *file_info; + + file_info = g_file_query_info (img->priv->file, + G_FILE_ATTRIBUTE_STANDARD_SIZE "," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, error); + + if (file_info == NULL) { + if (bytes) + *bytes = 0; + + if (mime_type) + *mime_type = NULL; + + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_VFS, + "Error in getting image file info"); + } else { + if (bytes) + *bytes = g_file_info_get_size (file_info); + + if (mime_type) + *mime_type = g_strdup (g_file_info_get_content_type (file_info)); + g_object_unref (file_info); + } +} + +#ifdef HAVE_LCMS +void +eom_image_apply_display_profile (EomImage *img, cmsHPROFILE screen) +{ + EomImagePrivate *priv; + cmsHTRANSFORM transform; + gint row, width, rows, stride; + guchar *p; + + g_return_if_fail (img != NULL); + + priv = img->priv; + + if (screen == NULL || priv->profile == NULL) return; + + /* TODO: support other colorspaces than RGB */ + if (cmsGetColorSpace (priv->profile) != icSigRgbData || + cmsGetColorSpace (screen) != icSigRgbData) { + eom_debug_message (DEBUG_LCMS, "One or both ICC profiles not in RGB colorspace; not correcting"); + return; + } + + /* TODO: find the right way to colorcorrect RGBA images */ + if (gdk_pixbuf_get_has_alpha (priv->image)) { + eom_debug_message (DEBUG_LCMS, "Colorcorrecting RGBA images is unsupported."); + return; + } + + transform = cmsCreateTransform (priv->profile, + TYPE_RGB_8, + screen, + TYPE_RGB_8, + INTENT_PERCEPTUAL, + 0); + + if (G_LIKELY (transform != NULL)) { + rows = gdk_pixbuf_get_height (priv->image); + width = gdk_pixbuf_get_width (priv->image); + stride = gdk_pixbuf_get_rowstride (priv->image); + p = gdk_pixbuf_get_pixels (priv->image); + + for (row = 0; row < rows; ++row) { + cmsDoTransform (transform, p, p, width); + p += stride; + } + cmsDeleteTransform (transform); + } +} + +static void +eom_image_set_icc_data (EomImage *img, EomMetadataReader *md_reader) +{ + EomImagePrivate *priv = img->priv; + + priv->profile = eom_metadata_reader_get_icc_profile (md_reader); + + +} +#endif + +#ifdef HAVE_EXIF +static void +eom_image_set_orientation (EomImage *img) +{ + EomImagePrivate *priv; + ExifData* exif; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + + exif = (ExifData*) eom_image_get_exif_info (img); + + if (exif != NULL) { + ExifByteOrder o = exif_data_get_byte_order (exif); + + ExifEntry *entry = exif_data_get_entry (exif, + EXIF_TAG_ORIENTATION); + + if (entry && entry->data != NULL) { + priv->orientation = exif_get_short (entry->data, o); + } + } + + /* exif_data_unref handles NULL values like g_free */ + exif_data_unref (exif); + + if (priv->orientation > 4 && + priv->orientation < 9) { + gint tmp; + + tmp = priv->width; + priv->width = priv->height; + priv->height = tmp; + } +} + +static void +eom_image_real_autorotate (EomImage *img) +{ + static const EomTransformType lookup[8] = {EOM_TRANSFORM_NONE, + EOM_TRANSFORM_FLIP_HORIZONTAL, + EOM_TRANSFORM_ROT_180, + EOM_TRANSFORM_FLIP_VERTICAL, + EOM_TRANSFORM_TRANSPOSE, + EOM_TRANSFORM_ROT_90, + EOM_TRANSFORM_TRANSVERSE, + EOM_TRANSFORM_ROT_270}; + EomImagePrivate *priv; + EomTransformType type; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + + type = (priv->orientation >= 1 && priv->orientation <= 8 ? + lookup[priv->orientation - 1] : EOM_TRANSFORM_NONE); + + if (type != EOM_TRANSFORM_NONE) { + img->priv->trans_autorotate = eom_transform_new (type); + } + + /* Disable auto orientation for next loads */ + priv->autorotate = FALSE; +} + +void +eom_image_autorotate (EomImage *img) +{ + g_return_if_fail (EOM_IS_IMAGE (img)); + + /* Schedule auto orientation */ + img->priv->autorotate = TRUE; +} +#endif + +#ifdef HAVE_EXEMPI +static void +eom_image_set_xmp_data (EomImage *img, EomMetadataReader *md_reader) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + + if (priv->xmp) { + xmp_free (priv->xmp); + } + priv->xmp = eom_metadata_reader_get_xmp_data (md_reader); +} +#endif + +static void +eom_image_set_exif_data (EomImage *img, EomMetadataReader *md_reader) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + +#ifdef HAVE_EXIF + g_mutex_lock (priv->status_mutex); + if (priv->exif) { + exif_data_unref (priv->exif); + } + priv->exif = eom_metadata_reader_get_exif_data (md_reader); + g_mutex_unlock (priv->status_mutex); + + priv->exif_chunk = NULL; + priv->exif_chunk_len = 0; + + /* EXIF data is already available, set the image orientation */ + if (priv->autorotate) { + eom_image_set_orientation (img); + + /* Emit size prepared signal if we have the size */ + if (priv->width > 0 && + priv->height > 0) { + eom_image_emit_size_prepared (img); + } + } +#else + if (priv->exif_chunk) { + g_free (priv->exif_chunk); + } + eom_metadata_reader_get_exif_chunk (md_reader, + &priv->exif_chunk, + &priv->exif_chunk_len); +#endif +} + +/* + * Attempts to get the image dimensions from the thumbnail. + * Returns FALSE if this information is not found. + **/ +static gboolean +eom_image_get_dimension_from_thumbnail (EomImage *image, + gint *width, + gint *height) +{ + if (image->priv->thumbnail == NULL) + return FALSE; + + *width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (image->priv->thumbnail), + EOM_THUMBNAIL_ORIGINAL_WIDTH)); + *height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (image->priv->thumbnail), + EOM_THUMBNAIL_ORIGINAL_HEIGHT)); + + return (*width || *height); +} + +static gboolean +eom_image_real_load (EomImage *img, + guint data2read, + EomJob *job, + GError **error) +{ + EomImagePrivate *priv; + GFileInputStream *input_stream; + EomMetadataReader *md_reader = NULL; + GdkPixbufFormat *format; + gchar *mime_type; + GdkPixbufLoader *loader = NULL; + guchar *buffer; + goffset bytes_read, bytes_read_total = 0; + gboolean failed = FALSE; + gboolean first_run = TRUE; + gboolean set_metadata = TRUE; + gboolean read_image_data = (data2read & EOM_IMAGE_DATA_IMAGE); + gboolean read_only_dimension = (data2read & EOM_IMAGE_DATA_DIMENSION) && + ((data2read ^ EOM_IMAGE_DATA_DIMENSION) == 0); + + + priv = img->priv; + + g_assert (!read_image_data || priv->image == NULL); + + if (read_image_data && priv->file_type != NULL) { + g_free (priv->file_type); + priv->file_type = NULL; + } + + priv->threadsafe_format = FALSE; + + eom_image_get_file_info (img, &priv->bytes, &mime_type, error); + + if (error && *error) { + g_free (mime_type); + return FALSE; + } + + if (read_only_dimension) { + gint width, height; + gboolean done; + + done = eom_image_get_dimension_from_thumbnail (img, + &width, + &height); + + if (done) { + priv->width = width; + priv->height = height; + + g_free (mime_type); + return TRUE; + } + } + + input_stream = g_file_read (priv->file, NULL, error); + + if (input_stream == NULL) { + g_free (mime_type); + + if (error != NULL) { + g_clear_error (error); + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_VFS, + "Failed to open input stream for file"); + } + return FALSE; + } + + buffer = g_new0 (guchar, EOM_IMAGE_READ_BUFFER_SIZE); + + if (read_image_data || read_only_dimension) { + gboolean checked_threadsafety = FALSE; + +#ifdef HAVE_RSVG + if (priv->svg != NULL) { + g_object_unref (priv->svg); + priv->svg = NULL; + } + + if (!strcmp (mime_type, "image/svg+xml")) { + gchar *file_path; + /* Keep the object for rendering */ + priv->svg = rsvg_handle_new (); + file_path = g_file_get_path (priv->file); + rsvg_handle_set_base_uri (priv->svg, file_path); + g_free (file_path); + } +#endif + loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, error); + + if (error && *error) { + g_error_free (*error); + *error = NULL; + + loader = gdk_pixbuf_loader_new (); + } else { + /* The mimetype-based loader should know the + * format here already. */ + checked_threadsafety = check_loader_threadsafety (loader, &priv->threadsafe_format); + } + + /* This is used to detect non-threadsafe loaders and disable + * any possible asyncronous task that could bring deadlocks + * to image loading process. */ + if (!checked_threadsafety) + g_signal_connect (loader, + "size-prepared", + G_CALLBACK (eom_image_pre_size_prepared), + img); + + g_signal_connect_object (G_OBJECT (loader), + "size-prepared", + G_CALLBACK (eom_image_size_prepared), + img, + 0); + } + g_free (mime_type); + + while (!priv->cancel_loading) { + /* FIXME: make this async */ + bytes_read = g_input_stream_read (G_INPUT_STREAM (input_stream), + buffer, + EOM_IMAGE_READ_BUFFER_SIZE, + NULL, error); + + if (bytes_read == 0) { + /* End of the file */ + break; + } else if (bytes_read == -1) { + failed = TRUE; + + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_VFS, + "Failed to read from input stream"); + + break; + } + + if ((read_image_data || read_only_dimension)) { + if (!gdk_pixbuf_loader_write (loader, buffer, bytes_read, error)) { + failed = TRUE; + break; + } +#ifdef HAVE_RSVG + if (eom_image_is_svg (img) && + !rsvg_handle_write (priv->svg, buffer, bytes_read, error)) { + failed = TRUE; + break; + } +#endif + } + + bytes_read_total += bytes_read; + + if (job != NULL) { + float progress = (float) bytes_read_total / (float) priv->bytes; + eom_job_set_progress (job, progress); + } + + if (first_run) { + md_reader = check_for_metadata_img_format (img, buffer, bytes_read); + + if (md_reader == NULL) { + if (data2read == EOM_IMAGE_DATA_EXIF) { + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_GENERIC, + _("EXIF not supported for this file format.")); + break; + } + + if (priv->threadsafe_format) + eom_image_emit_size_prepared (img); + + priv->metadata_status = EOM_IMAGE_METADATA_NOT_AVAILABLE; + } + + first_run = FALSE; + } + + if (md_reader != NULL) { + eom_metadata_reader_consume (md_reader, buffer, bytes_read); + + if (eom_metadata_reader_finished (md_reader)) { + if (set_metadata) { + eom_image_set_exif_data (img, md_reader); + +#ifdef HAVE_LCMS + eom_image_set_icc_data (img, md_reader); +#endif + +#ifdef HAVE_EXEMPI + eom_image_set_xmp_data (img, md_reader); +#endif + set_metadata = FALSE; + priv->metadata_status = EOM_IMAGE_METADATA_READY; + } + + if (data2read == EOM_IMAGE_DATA_EXIF) + break; + } + } + + if (read_only_dimension && + eom_image_has_data (img, EOM_IMAGE_DATA_DIMENSION)) { + break; + } + } + + if (read_image_data || read_only_dimension) { + if (failed) { + gdk_pixbuf_loader_close (loader, NULL); + } else if (!gdk_pixbuf_loader_close (loader, error)) { + if (gdk_pixbuf_loader_get_pixbuf (loader) != NULL) { + /* Clear error in order to support partial + * images as well. */ + g_clear_error (error); + } + } +#ifdef HAVE_RSVG + if (eom_image_is_svg (img)) + rsvg_handle_close (priv->svg, error); +#endif + } + + g_free (buffer); + + g_object_unref (G_OBJECT (input_stream)); + + failed = (failed || + priv->cancel_loading || + bytes_read_total == 0 || + (error && *error != NULL)); + + if (failed) { + if (priv->cancel_loading) { + priv->cancel_loading = FALSE; + priv->status = EOM_IMAGE_STATUS_UNKNOWN; + } else { + priv->status = EOM_IMAGE_STATUS_FAILED; + } + } else if (read_image_data) { + if (priv->image != NULL) { + g_object_unref (priv->image); + } + + priv->anim = gdk_pixbuf_loader_get_animation (loader); + + if (gdk_pixbuf_animation_is_static_image (priv->anim)) { + priv->image = gdk_pixbuf_animation_get_static_image (priv->anim); + priv->anim = NULL; + } else { + priv->anim_iter = gdk_pixbuf_animation_get_iter (priv->anim,NULL); + priv->image = gdk_pixbuf_animation_iter_get_pixbuf (priv->anim_iter); + } + + if (G_LIKELY (priv->image != NULL)) { + g_object_ref (priv->image); + + priv->width = gdk_pixbuf_get_width (priv->image); + priv->height = gdk_pixbuf_get_height (priv->image); + + format = gdk_pixbuf_loader_get_format (loader); + + if (format != NULL) { + priv->file_type = gdk_pixbuf_format_get_name (format); + } + + /* If it's non-threadsafe loader, then trigger window + * showing in the end of the process. */ + if (!priv->threadsafe_format) + eom_image_emit_size_prepared (img); + } else { + /* Some loaders don't report errors correctly. + * Error will be set below. */ + failed = TRUE; + priv->status = EOM_IMAGE_STATUS_FAILED; + } + } + + if (loader != NULL) { + g_object_unref (loader); + } + + if (md_reader != NULL) { + g_object_unref (md_reader); + md_reader = NULL; + } + + /* Catch-all in case of poor-error reporting */ + if (failed && error && *error == NULL) { + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_GENERIC, + _("Image loading failed.")); + } + + return !failed; +} + +gboolean +eom_image_has_data (EomImage *img, EomImageData req_data) +{ + EomImagePrivate *priv; + gboolean has_data = TRUE; + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + + priv = img->priv; + + if ((req_data & EOM_IMAGE_DATA_IMAGE) > 0) { + req_data = (req_data & !EOM_IMAGE_DATA_IMAGE); + has_data = has_data && (priv->image != NULL); + } + + if ((req_data & EOM_IMAGE_DATA_DIMENSION) > 0 ) { + req_data = (req_data & !EOM_IMAGE_DATA_DIMENSION); + has_data = has_data && (priv->width >= 0) && (priv->height >= 0); + } + + if ((req_data & EOM_IMAGE_DATA_EXIF) > 0) { + req_data = (req_data & !EOM_IMAGE_DATA_EXIF); +#ifdef HAVE_EXIF + has_data = has_data && (priv->exif != NULL); +#else + has_data = has_data && (priv->exif_chunk != NULL); +#endif + } + + if ((req_data & EOM_IMAGE_DATA_XMP) > 0) { + req_data = (req_data & !EOM_IMAGE_DATA_XMP); +#ifdef HAVE_EXEMPI + has_data = has_data && (priv->xmp != NULL); +#endif + } + + if (req_data != 0) { + g_warning ("Asking for unknown data, remaining: %i\n", req_data); + has_data = FALSE; + } + + return has_data; +} + +gboolean +eom_image_load (EomImage *img, EomImageData data2read, EomJob *job, GError **error) +{ + EomImagePrivate *priv; + gboolean success = FALSE; + + eom_debug (DEBUG_IMAGE_LOAD); + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + + priv = EOM_IMAGE (img)->priv; + + if (data2read == 0) { + return TRUE; + } + + if (eom_image_has_data (img, data2read)) { + return TRUE; + } + + priv->status = EOM_IMAGE_STATUS_LOADING; + + success = eom_image_real_load (img, data2read, job, error); + +#ifdef HAVE_EXIF + /* Check that the metadata was loaded at least once before + * trying to autorotate. */ + if (priv->autorotate && + priv->metadata_status == EOM_IMAGE_METADATA_READY) { + eom_image_real_autorotate (img); + } +#endif + + if (success && eom_image_needs_transformation (img)) { + success = eom_image_apply_transformations (img, error); + } + + if (success) { + priv->status = EOM_IMAGE_STATUS_LOADED; + } else { + priv->status = EOM_IMAGE_STATUS_FAILED; + } + + return success; +} + +void +eom_image_set_thumbnail (EomImage *img, GdkPixbuf *thumbnail) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (img)); + g_return_if_fail (GDK_IS_PIXBUF (thumbnail) || thumbnail == NULL); + + priv = img->priv; + + if (priv->thumbnail != NULL) { + g_object_unref (priv->thumbnail); + priv->thumbnail = NULL; + } + + if (thumbnail != NULL && priv->trans != NULL) { + priv->thumbnail = eom_transform_apply (priv->trans, thumbnail, NULL); + } else { + priv->thumbnail = thumbnail; + + if (thumbnail != NULL) { + g_object_ref (priv->thumbnail); + } + } + + if (priv->thumbnail != NULL) { + g_signal_emit (img, signals[SIGNAL_THUMBNAIL_CHANGED], 0); + } +} + +GdkPixbuf * +eom_image_get_pixbuf (EomImage *img) +{ + GdkPixbuf *image = NULL; + + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + g_mutex_lock (img->priv->status_mutex); + image = img->priv->image; + g_mutex_unlock (img->priv->status_mutex); + + if (image != NULL) { + g_object_ref (image); + } + + return image; +} + +#ifdef HAVE_LCMS +cmsHPROFILE +eom_image_get_profile (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + return img->priv->profile; +} +#endif + +GdkPixbuf * +eom_image_get_thumbnail (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + if (img->priv->thumbnail != NULL) { + g_object_ref (img->priv->thumbnail); + + return img->priv->thumbnail; + } + + return NULL; +} + +void +eom_image_get_size (EomImage *img, int *width, int *height) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + + *width = priv->width; + *height = priv->height; +} + +void +eom_image_transform (EomImage *img, EomTransform *trans, EomJob *job) +{ + eom_image_real_transform (img, trans, FALSE, job); +} + +void +eom_image_undo (EomImage *img) +{ + EomImagePrivate *priv; + EomTransform *trans; + EomTransform *inverse; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + + if (priv->undo_stack != NULL) { + trans = EOM_TRANSFORM (priv->undo_stack->data); + + inverse = eom_transform_reverse (trans); + + eom_image_real_transform (img, inverse, TRUE, NULL); + + priv->undo_stack = g_slist_delete_link (priv->undo_stack, priv->undo_stack); + + g_object_unref (trans); + g_object_unref (inverse); + + if (eom_transform_is_identity (priv->trans)) { + g_object_unref (priv->trans); + priv->trans = NULL; + } + } + + priv->modified = (priv->undo_stack != NULL); +} + +static GFile * +tmp_file_get (void) +{ + GFile *tmp_file; + char *tmp_file_path; + gint fd; + + tmp_file_path = g_build_filename (g_get_tmp_dir (), "eom-save-XXXXXX", NULL); + fd = g_mkstemp (tmp_file_path); + if (fd == -1) { + g_free (tmp_file_path); + return NULL; + } + else { + tmp_file = g_file_new_for_path (tmp_file_path); + g_free (tmp_file_path); + return tmp_file; + } +} + +static void +transfer_progress_cb (goffset cur_bytes, + goffset total_bytes, + gpointer user_data) +{ + EomImage *image = EOM_IMAGE (user_data); + + if (cur_bytes > 0) { + g_signal_emit (G_OBJECT(image), + signals[SIGNAL_SAVE_PROGRESS], + 0, + (gfloat) cur_bytes / (gfloat) total_bytes); + } +} + +static gboolean +tmp_file_move_to_uri (EomImage *image, + GFile *tmpfile, + GFile *file, + gboolean overwrite, + GError **error) +{ + gboolean result; + GError *ioerror = NULL; + + result = g_file_move (tmpfile, + file, + (overwrite ? G_FILE_COPY_OVERWRITE : 0) | + G_FILE_COPY_ALL_METADATA, + NULL, + (GFileProgressCallback) transfer_progress_cb, + image, + &ioerror); + + if (result == FALSE) { + if (g_error_matches (ioerror, G_IO_ERROR, + G_IO_ERROR_EXISTS)) { + g_set_error (error, EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_FILE_EXISTS, + "File exists"); + } else { + g_set_error (error, EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_VFS, + "VFS error moving the temp file"); + } + g_clear_error (&ioerror); + } + + return result; +} + +static gboolean +tmp_file_delete (GFile *tmpfile) +{ + gboolean result; + GError *err = NULL; + + if (tmpfile == NULL) return FALSE; + + result = g_file_delete (tmpfile, NULL, &err); + if (result == FALSE) { + char *tmpfile_path; + if (err != NULL) { + if (err->code == G_IO_ERROR_NOT_FOUND) { + g_error_free (err); + return TRUE; + } + g_error_free (err); + } + tmpfile_path = g_file_get_path (tmpfile); + g_warning ("Couldn't delete temporary file: %s", tmpfile_path); + g_free (tmpfile_path); + } + + return result; +} + +static void +eom_image_reset_modifications (EomImage *image) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (image)); + + priv = image->priv; + + g_slist_foreach (priv->undo_stack, (GFunc) g_object_unref, NULL); + g_slist_free (priv->undo_stack); + priv->undo_stack = NULL; + + if (priv->trans != NULL) { + g_object_unref (priv->trans); + priv->trans = NULL; + } + + if (priv->trans_autorotate != NULL) { + g_object_unref (priv->trans_autorotate); + priv->trans_autorotate = NULL; + } + + priv->modified = FALSE; +} + +static void +eom_image_link_with_target (EomImage *image, EomImageSaveInfo *target) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (image)); + g_return_if_fail (EOM_IS_IMAGE_SAVE_INFO (target)); + + priv = image->priv; + + /* update file location */ + if (priv->file != NULL) { + g_object_unref (priv->file); + } + priv->file = g_object_ref (target->file); + + /* Clear caption and caption key, these will be + * updated on next eom_image_get_caption call. + */ + if (priv->caption != NULL) { + g_free (priv->caption); + priv->caption = NULL; + } + if (priv->collate_key != NULL) { + g_free (priv->collate_key); + priv->collate_key = NULL; + } + + /* update file format */ + if (priv->file_type != NULL) { + g_free (priv->file_type); + } + priv->file_type = g_strdup (target->format); +} + +gboolean +eom_image_save_by_info (EomImage *img, EomImageSaveInfo *source, GError **error) +{ + EomImagePrivate *priv; + EomImageStatus prev_status; + gboolean success = FALSE; + GFile *tmp_file; + char *tmp_file_path; + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + g_return_val_if_fail (EOM_IS_IMAGE_SAVE_INFO (source), FALSE); + + priv = img->priv; + + prev_status = priv->status; + + /* Image is now being saved */ + priv->status = EOM_IMAGE_STATUS_SAVING; + + /* see if we need any saving at all */ + if (source->exists && !source->modified) { + return TRUE; + } + + /* fail if there is no image to save */ + if (priv->image == NULL) { + g_set_error (error, EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_NOT_LOADED, + _("No image loaded.")); + return FALSE; + } + + /* generate temporary file */ + tmp_file = tmp_file_get (); + + if (tmp_file == NULL) { + g_set_error (error, EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_TMP_FILE_FAILED, + _("Temporary file creation failed.")); + return FALSE; + } + + tmp_file_path = g_file_get_path (tmp_file); + +#ifdef HAVE_JPEG + /* determine kind of saving */ + if ((g_ascii_strcasecmp (source->format, EOM_FILE_FORMAT_JPEG) == 0) && + source->exists && source->modified) + { + success = eom_image_jpeg_save_file (img, tmp_file_path, source, NULL, error); + } +#endif + + if (!success && (*error == NULL)) { + success = gdk_pixbuf_save (priv->image, tmp_file_path, source->format, error, NULL); + } + + if (success) { + /* try to move result file to target uri */ + success = tmp_file_move_to_uri (img, tmp_file, priv->file, TRUE /*overwrite*/, error); + } + + if (success) { + eom_image_reset_modifications (img); + } + + tmp_file_delete (tmp_file); + + g_free (tmp_file_path); + g_object_unref (tmp_file); + + priv->status = prev_status; + + return success; +} + +static gboolean +eom_image_copy_file (EomImage *image, EomImageSaveInfo *source, EomImageSaveInfo *target, GError **error) +{ + gboolean result; + GError *ioerror = NULL; + + g_return_val_if_fail (EOM_IS_IMAGE_SAVE_INFO (source), FALSE); + g_return_val_if_fail (EOM_IS_IMAGE_SAVE_INFO (target), FALSE); + + result = g_file_copy (source->file, + target->file, + (target->overwrite ? G_FILE_COPY_OVERWRITE : 0) | + G_FILE_COPY_ALL_METADATA, + NULL, + EOM_IS_IMAGE (image) ? transfer_progress_cb :NULL, + image, + &ioerror); + + if (result == FALSE) { + if (ioerror->code == G_IO_ERROR_EXISTS) { + g_set_error (error, EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_FILE_EXISTS, + "%s", ioerror->message); + } else { + g_set_error (error, EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_VFS, + "%s", ioerror->message); + } + g_error_free (ioerror); + } + + return result; +} + +gboolean +eom_image_save_as_by_info (EomImage *img, EomImageSaveInfo *source, EomImageSaveInfo *target, GError **error) +{ + EomImagePrivate *priv; + gboolean success = FALSE; + char *tmp_file_path; + GFile *tmp_file; + gboolean direct_copy = FALSE; + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + g_return_val_if_fail (EOM_IS_IMAGE_SAVE_INFO (source), FALSE); + g_return_val_if_fail (EOM_IS_IMAGE_SAVE_INFO (target), FALSE); + + priv = img->priv; + + /* fail if there is no image to save */ + if (priv->image == NULL) { + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_NOT_LOADED, + _("No image loaded.")); + + return FALSE; + } + + /* generate temporary file name */ + tmp_file = tmp_file_get (); + + if (tmp_file == NULL) { + g_set_error (error, + EOM_IMAGE_ERROR, + EOM_IMAGE_ERROR_TMP_FILE_FAILED, + _("Temporary file creation failed.")); + + return FALSE; + } + tmp_file_path = g_file_get_path (tmp_file); + + /* determine kind of saving */ + if (g_ascii_strcasecmp (source->format, target->format) == 0 && !source->modified) { + success = eom_image_copy_file (img, source, target, error); + direct_copy = success; + } + +#ifdef HAVE_JPEG + else if ((g_ascii_strcasecmp (source->format, EOM_FILE_FORMAT_JPEG) == 0 && source->exists) || + (g_ascii_strcasecmp (target->format, EOM_FILE_FORMAT_JPEG) == 0)) + { + success = eom_image_jpeg_save_file (img, tmp_file_path, source, target, error); + } +#endif + + if (!success && (*error == NULL)) { + success = gdk_pixbuf_save (priv->image, tmp_file_path, target->format, error, NULL); + } + + if (success && !direct_copy) { /* not required if we alredy copied the file directly */ + /* try to move result file to target uri */ + success = tmp_file_move_to_uri (img, tmp_file, target->file, target->overwrite, error); + } + + if (success) { + /* update image information to new uri */ + eom_image_reset_modifications (img); + eom_image_link_with_target (img, target); + } + + tmp_file_delete (tmp_file); + g_object_unref (tmp_file); + g_free (tmp_file_path); + + priv->status = EOM_IMAGE_STATUS_UNKNOWN; + + return success; +} + + +/* + * This function is extracted from + * File: caja/libcaja-private/caja-file.c + * Revision: 1.309 + * Author: Darin Adler <[email protected]> + */ +static gboolean +have_broken_filenames (void) +{ + static gboolean initialized = FALSE; + static gboolean broken; + + if (initialized) { + return broken; + } + + broken = g_getenv ("G_BROKEN_FILENAMES") != NULL; + + initialized = TRUE; + + return broken; +} + +/* + * This function is inspired by + * caja/libcaja-private/caja-file.c:caja_file_get_display_name_nocopy + * Revision: 1.309 + * Author: Darin Adler <[email protected]> + */ +const gchar* +eom_image_get_caption (EomImage *img) +{ + EomImagePrivate *priv; + char *name; + char *utf8_name; + char *scheme; + gboolean validated = FALSE; + gboolean broken_filenames; + + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + priv = img->priv; + + if (priv->file == NULL) return NULL; + + if (priv->caption != NULL) + /* Use cached caption string */ + return priv->caption; + + name = g_file_get_basename (priv->file); + scheme = g_file_get_uri_scheme (priv->file); + + if (name != NULL && g_ascii_strcasecmp (scheme, "file") == 0) { + /* Support the G_BROKEN_FILENAMES feature of + * glib by using g_filename_to_utf8 to convert + * local filenames to UTF-8. Also do the same + * thing with any local filename that does not + * validate as good UTF-8. + */ + broken_filenames = have_broken_filenames (); + + if (broken_filenames || !g_utf8_validate (name, -1, NULL)) { + utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL); + if (utf8_name != NULL) { + g_free (name); + name = utf8_name; + /* Guaranteed to be correct utf8 here */ + validated = TRUE; + } + } else if (!broken_filenames) { + /* name was valid, no need to re-validate */ + validated = TRUE; + } + } + + if (!validated && !g_utf8_validate (name, -1, NULL)) { + if (name == NULL) { + name = g_strdup ("[Invalid Unicode]"); + } else { + utf8_name = eom_util_make_valid_utf8 (name); + g_free (name); + name = utf8_name; + } + } + + priv->caption = name; + + if (priv->caption == NULL) { + char *short_str; + + short_str = g_file_get_basename (priv->file); + if (g_utf8_validate (short_str, -1, NULL)) { + priv->caption = g_strdup (short_str); + } else { + priv->caption = g_filename_to_utf8 (short_str, -1, NULL, NULL, NULL); + } + g_free (short_str); + } + g_free (scheme); + + return priv->caption; +} + +const gchar* +eom_image_get_collate_key (EomImage *img) +{ + EomImagePrivate *priv; + + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + priv = img->priv; + + if (priv->collate_key == NULL) { + const char *caption; + + caption = eom_image_get_caption (img); + + priv->collate_key = g_utf8_collate_key_for_filename (caption, -1); + } + + return priv->collate_key; +} + +void +eom_image_cancel_load (EomImage *img) +{ + EomImagePrivate *priv; + + g_return_if_fail (EOM_IS_IMAGE (img)); + + priv = img->priv; + + g_mutex_lock (priv->status_mutex); + + if (priv->status == EOM_IMAGE_STATUS_LOADING) { + priv->cancel_loading = TRUE; + } + + g_mutex_unlock (priv->status_mutex); +} + +gpointer +eom_image_get_exif_info (EomImage *img) +{ + EomImagePrivate *priv; + gpointer data = NULL; + + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + priv = img->priv; + +#ifdef HAVE_EXIF + g_mutex_lock (priv->status_mutex); + + exif_data_ref (priv->exif); + data = priv->exif; + + g_mutex_unlock (priv->status_mutex); +#endif + + return data; +} + + +gpointer +eom_image_get_xmp_info (EomImage *img) +{ + EomImagePrivate *priv; + gpointer data = NULL; + + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + priv = img->priv; + +#ifdef HAVE_EXEMPI + g_mutex_lock (priv->status_mutex); + data = (gpointer) xmp_copy (priv->xmp); + g_mutex_unlock (priv->status_mutex); +#endif + + return data; +} + + +GFile * +eom_image_get_file (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + return g_object_ref (img->priv->file); +} + +gboolean +eom_image_is_modified (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + + return img->priv->modified; +} + +goffset +eom_image_get_bytes (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), 0); + + return img->priv->bytes; +} + +void +eom_image_modified (EomImage *img) +{ + g_return_if_fail (EOM_IS_IMAGE (img)); + + g_signal_emit (G_OBJECT (img), signals[SIGNAL_CHANGED], 0); +} + +gchar* +eom_image_get_uri_for_display (EomImage *img) +{ + EomImagePrivate *priv; + gchar *uri_str = NULL; + gchar *str = NULL; + + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + priv = img->priv; + + if (priv->file != NULL) { + uri_str = g_file_get_uri (priv->file); + + if (uri_str != NULL) { + str = g_uri_unescape_string (uri_str, NULL); + g_free (uri_str); + } + } + + return str; +} + +EomImageStatus +eom_image_get_status (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), EOM_IMAGE_STATUS_UNKNOWN); + + return img->priv->status; +} + +/** + * eom_image_get_metadata_status: + * @img: a #EomImage + * + * Returns the current status of the image metadata, that is, + * whether the metadata has not been read yet, is ready, or not available at all. + * + * Returns: one of #EomImageMetadataStatus + **/ +EomImageMetadataStatus +eom_image_get_metadata_status (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), EOM_IMAGE_METADATA_NOT_AVAILABLE); + + return img->priv->metadata_status; +} + +void +eom_image_data_ref (EomImage *img) +{ + g_return_if_fail (EOM_IS_IMAGE (img)); + + g_object_ref (G_OBJECT (img)); + img->priv->data_ref_count++; + + g_assert (img->priv->data_ref_count <= G_OBJECT (img)->ref_count); +} + +void +eom_image_data_unref (EomImage *img) +{ + g_return_if_fail (EOM_IS_IMAGE (img)); + + if (img->priv->data_ref_count > 0) { + img->priv->data_ref_count--; + } else { + g_warning ("More image data unrefs than refs."); + } + + if (img->priv->data_ref_count == 0) { + eom_image_free_mem_private (img); + } + + g_object_unref (G_OBJECT (img)); + + g_assert (img->priv->data_ref_count <= G_OBJECT (img)->ref_count); +} + +static gint +compare_quarks (gconstpointer a, gconstpointer b) +{ + GQuark quark; + + quark = g_quark_from_string ((const gchar *) a); + + return quark - GPOINTER_TO_INT (b); +} + +GList * +eom_image_get_supported_mime_types (void) +{ + GSList *format_list, *it; + gchar **mime_types; + int i; + + if (!supported_mime_types) { + format_list = gdk_pixbuf_get_formats (); + + for (it = format_list; it != NULL; it = it->next) { + mime_types = + gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) it->data); + + for (i = 0; mime_types[i] != NULL; i++) { + supported_mime_types = + g_list_prepend (supported_mime_types, + g_strdup (mime_types[i])); + } + + g_strfreev (mime_types); + } + + supported_mime_types = g_list_sort (supported_mime_types, + (GCompareFunc) compare_quarks); + + g_slist_free (format_list); + } + + return supported_mime_types; +} + +gboolean +eom_image_is_supported_mime_type (const char *mime_type) +{ + GList *supported_mime_types, *result; + GQuark quark; + + if (mime_type == NULL) { + return FALSE; + } + + supported_mime_types = eom_image_get_supported_mime_types (); + + quark = g_quark_from_string (mime_type); + + result = g_list_find_custom (supported_mime_types, + GINT_TO_POINTER (quark), + (GCompareFunc) compare_quarks); + + return (result != NULL); +} + +static gboolean +eom_image_iter_advance (EomImage *img) +{ + EomImagePrivate *priv; + gboolean new_frame; + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (img->priv->anim_iter), FALSE); + + priv = img->priv; + + if ((new_frame = gdk_pixbuf_animation_iter_advance (img->priv->anim_iter, NULL)) == TRUE) + { + g_mutex_lock (priv->status_mutex); + g_object_unref (priv->image); + priv->image = gdk_pixbuf_animation_iter_get_pixbuf (priv->anim_iter); + g_object_ref (priv->image); + /* keep the transformation over time */ + if (EOM_IS_TRANSFORM (priv->trans)) { + GdkPixbuf* transformed = eom_transform_apply (priv->trans, priv->image, NULL); + g_object_unref (priv->image); + priv->image = transformed; + priv->width = gdk_pixbuf_get_width (transformed); + priv->height = gdk_pixbuf_get_height (transformed); + } + g_mutex_unlock (priv->status_mutex); + /* Emit next frame signal so we can update the display */ + g_signal_emit (img, signals[SIGNAL_NEXT_FRAME], 0, + gdk_pixbuf_animation_iter_get_delay_time (priv->anim_iter)); + } + + return new_frame; +} + +/** + * eom_image_is_animation: + * @img: a #EomImage + * + * Checks whether a given image is animated. + * + * Returns: #TRUE if it is an animated image, #FALSE otherwise. + * + **/ +gboolean +eom_image_is_animation (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + return img->priv->anim != NULL; +} + +static gboolean +private_timeout (gpointer data) +{ + EomImage *img = EOM_IMAGE (data); + EomImagePrivate *priv = img->priv; + + if (eom_image_is_animation (img) && + !g_source_is_destroyed (g_main_current_source ()) && + priv->is_playing) { + while (eom_image_iter_advance (img) != TRUE) {}; /* cpu-sucking ? */ + g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (priv->anim_iter), private_timeout, img); + return FALSE; + } + priv->is_playing = FALSE; + return FALSE; /* stop playing */ +} + +/** + * eom_image_start_animation: + * @img: a #EomImage + * + * Starts playing an animated image. + * + * Returns: %TRUE on success, %FALSE if @img is already playing or isn't an animated image. + **/ +gboolean +eom_image_start_animation (EomImage *img) +{ + EomImagePrivate *priv; + + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + priv = img->priv; + + if (!eom_image_is_animation (img) || priv->is_playing) + return FALSE; + + g_mutex_lock (priv->status_mutex); + g_object_ref (priv->anim_iter); + priv->is_playing = TRUE; + g_mutex_unlock (priv->status_mutex); + + g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (priv->anim_iter), private_timeout, img); + + return TRUE; +} + +#ifdef HAVE_RSVG +gboolean +eom_image_is_svg (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), FALSE); + + return (img->priv->svg != NULL); +} + +RsvgHandle * +eom_image_get_svg (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + return img->priv->svg; +} + +EomTransform * +eom_image_get_transform (EomImage *img) +{ + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + return img->priv->trans; +} + +#endif + +/** + * eom_image_file_changed: + * @img: a #EomImage + * + * Emits EomImage::file-changed signal + **/ +void +eom_image_file_changed (EomImage *img) +{ + g_return_if_fail (EOM_IS_IMAGE (img)); + + g_signal_emit (img, signals[SIGNAL_FILE_CHANGED], 0); +} diff --git a/src/eom-image.h b/src/eom-image.h new file mode 100644 index 0000000..791eb8a --- /dev/null +++ b/src/eom-image.h @@ -0,0 +1,218 @@ +/* Eye Of Mate - Image + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_IMAGE_H__ +#define __EOM_IMAGE_H__ + +#include "eom-jobs.h" +#include "eom-window.h" +#include "eom-transform.h" +#include "eom-image-save-info.h" +#include "eom-enums.h" + +#include <glib.h> +#include <glib-object.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#ifdef HAVE_EXIF +#include <libexif/exif-data.h> +#endif + +#ifdef HAVE_LCMS +#include <lcms.h> +#endif + +#ifdef HAVE_EXEMPI +#include <exempi/xmp.h> +#endif + +#ifdef HAVE_RSVG +#include <librsvg/rsvg.h> +#endif + +G_BEGIN_DECLS + +#ifndef __EOM_IMAGE_DECLR__ +#define __EOM_IMAGE_DECLR__ +typedef struct _EomImage EomImage; +#endif +typedef struct _EomImageClass EomImageClass; +typedef struct _EomImagePrivate EomImagePrivate; + +#define EOM_TYPE_IMAGE (eom_image_get_type ()) +#define EOM_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_IMAGE, EomImage)) +#define EOM_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_IMAGE, EomImageClass)) +#define EOM_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_IMAGE)) +#define EOM_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_IMAGE)) +#define EOM_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_IMAGE, EomImageClass)) + +typedef enum { + EOM_IMAGE_ERROR_SAVE_NOT_LOCAL, + EOM_IMAGE_ERROR_NOT_LOADED, + EOM_IMAGE_ERROR_VFS, + EOM_IMAGE_ERROR_FILE_EXISTS, + EOM_IMAGE_ERROR_TMP_FILE_FAILED, + EOM_IMAGE_ERROR_GENERIC, + EOM_IMAGE_ERROR_UNKNOWN +} EomImageError; + +#define EOM_IMAGE_ERROR eom_image_error_quark () + +typedef enum { + EOM_IMAGE_STATUS_UNKNOWN, + EOM_IMAGE_STATUS_LOADING, + EOM_IMAGE_STATUS_LOADED, + EOM_IMAGE_STATUS_SAVING, + EOM_IMAGE_STATUS_FAILED +} EomImageStatus; + +typedef enum { + EOM_IMAGE_METADATA_NOT_READ, + EOM_IMAGE_METADATA_NOT_AVAILABLE, + EOM_IMAGE_METADATA_READY +} EomImageMetadataStatus; + +struct _EomImage { + GObject parent; + + EomImagePrivate *priv; +}; + +struct _EomImageClass { + GObjectClass parent_class; + + void (* changed) (EomImage *img); + + void (* size_prepared) (EomImage *img, + int width, + int height); + + void (* thumbnail_changed) (EomImage *img); + + void (* save_progress) (EomImage *img, + gfloat progress); + + void (* next_frame) (EomImage *img, + gint delay); + + void (* file_changed) (EomImage *img); +}; + +GType eom_image_get_type (void) G_GNUC_CONST; + +GQuark eom_image_error_quark (void); + +EomImage *eom_image_new (const char *txt_uri); + +EomImage *eom_image_new_file (GFile *file); + +gboolean eom_image_load (EomImage *img, + EomImageData data2read, + EomJob *job, + GError **error); + +void eom_image_cancel_load (EomImage *img); + +gboolean eom_image_has_data (EomImage *img, + EomImageData data); + +void eom_image_data_ref (EomImage *img); + +void eom_image_data_unref (EomImage *img); + +void eom_image_set_thumbnail (EomImage *img, + GdkPixbuf *pixbuf); + +gboolean eom_image_save_as_by_info (EomImage *img, + EomImageSaveInfo *source, + EomImageSaveInfo *target, + GError **error); + +gboolean eom_image_save_by_info (EomImage *img, + EomImageSaveInfo *source, + GError **error); + +GdkPixbuf* eom_image_get_pixbuf (EomImage *img); + +GdkPixbuf* eom_image_get_thumbnail (EomImage *img); + +void eom_image_get_size (EomImage *img, + gint *width, + gint *height); + +goffset eom_image_get_bytes (EomImage *img); + +gboolean eom_image_is_modified (EomImage *img); + +void eom_image_modified (EomImage *img); + +const gchar* eom_image_get_caption (EomImage *img); + +const gchar *eom_image_get_collate_key (EomImage *img); + +gpointer eom_image_get_exif_info (EomImage *img); + +gpointer eom_image_get_xmp_info (EomImage *img); + +GFile* eom_image_get_file (EomImage *img); + +gchar* eom_image_get_uri_for_display (EomImage *img); + +EomImageStatus eom_image_get_status (EomImage *img); + +EomImageMetadataStatus eom_image_get_metadata_status (EomImage *img); + +void eom_image_transform (EomImage *img, + EomTransform *trans, + EomJob *job); + +#ifdef HAVE_EXIF +void eom_image_autorotate (EomImage *img); +#endif + +#ifdef HAVE_LCMS +cmsHPROFILE eom_image_get_profile (EomImage *img); + +void eom_image_apply_display_profile (EomImage *img, + cmsHPROFILE display_profile); +#endif + +void eom_image_undo (EomImage *img); + +GList *eom_image_get_supported_mime_types (void); + +gboolean eom_image_is_supported_mime_type (const char *mime_type); + +gboolean eom_image_is_animation (EomImage *img); + +gboolean eom_image_start_animation (EomImage *img); + +#ifdef HAVE_RSVG +gboolean eom_image_is_svg (EomImage *img); +RsvgHandle *eom_image_get_svg (EomImage *img); +EomTransform *eom_image_get_transform (EomImage *img); +#endif + +void eom_image_file_changed (EomImage *img); + +G_END_DECLS + +#endif /* __EOM_IMAGE_H__ */ diff --git a/src/eom-job-queue.c b/src/eom-job-queue.c new file mode 100644 index 0000000..93ed903 --- /dev/null +++ b/src/eom-job-queue.c @@ -0,0 +1,238 @@ +/* Eye Of Mate - Jobs Queue + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on evince code (shell/ev-job-queue.c) by: + * - Martin Kretzschmar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "eom-jobs.h" +#include "eom-job-queue.h" + +static GCond *render_cond = NULL; +static GMutex *eom_queue_mutex = NULL; + +static GQueue *thumbnail_queue = NULL; +static GQueue *load_queue = NULL; +static GQueue *model_queue = NULL; +static GQueue *transform_queue = NULL; +static GQueue *save_queue = NULL; +static GQueue *copy_queue = NULL; + +static gboolean +remove_job_from_queue (GQueue *queue, EomJob *job) +{ + GList *list; + + list = g_queue_find (queue, job); + + if (list) { + g_object_unref (G_OBJECT (job)); + g_queue_delete_link (queue, list); + + return TRUE; + } + + return FALSE; +} + +static void +add_job_to_queue_locked (GQueue *queue, EomJob *job) +{ + g_object_ref (job); + g_queue_push_tail (queue, job); + g_cond_broadcast (render_cond); +} + +static gboolean +notify_finished (GObject *job) +{ + eom_job_finished (EOM_JOB (job)); + + return FALSE; +} + +static void +handle_job (EomJob *job) +{ + g_object_ref (G_OBJECT (job)); + + // Do the EOM_JOB cast for safety + eom_job_run (EOM_JOB (job)); + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) notify_finished, + job, + g_object_unref); +} + +static gboolean +no_jobs_available_unlocked (void) +{ + return g_queue_is_empty (load_queue) && + g_queue_is_empty (transform_queue) && + g_queue_is_empty (thumbnail_queue) && + g_queue_is_empty (model_queue) && + g_queue_is_empty (save_queue) && + g_queue_is_empty (copy_queue); +} + +static EomJob * +search_for_jobs_unlocked (void) +{ + EomJob *job; + + job = (EomJob *) g_queue_pop_head (load_queue); + if (job) + return job; + + job = (EomJob *) g_queue_pop_head (transform_queue); + if (job) + return job; + + job = (EomJob *) g_queue_pop_head (thumbnail_queue); + if (job) + return job; + + job = (EomJob *) g_queue_pop_head (model_queue); + if (job) + return job; + + job = (EomJob *) g_queue_pop_head (save_queue); + if (job) + return job; + + job = (EomJob *) g_queue_pop_head (copy_queue); + if (job) + return job; + + return NULL; +} + +static gpointer +eom_render_thread (gpointer data) +{ + while (TRUE) { + EomJob *job; + + g_mutex_lock (eom_queue_mutex); + + if (no_jobs_available_unlocked ()) { + g_cond_wait (render_cond, eom_queue_mutex); + } + + job = search_for_jobs_unlocked (); + + g_mutex_unlock (eom_queue_mutex); + + /* Now that we have our job, we handle it */ + if (job) { + handle_job (job); + g_object_unref (G_OBJECT (job)); + } + } + return NULL; + +} + +void +eom_job_queue_init (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); + + render_cond = g_cond_new (); + eom_queue_mutex = g_mutex_new (); + + thumbnail_queue = g_queue_new (); + load_queue = g_queue_new (); + model_queue = g_queue_new (); + transform_queue = g_queue_new (); + save_queue = g_queue_new (); + copy_queue = g_queue_new (); + + g_thread_create (eom_render_thread, NULL, FALSE, NULL); +} + +static GQueue * +find_queue (EomJob *job) +{ + if (EOM_IS_JOB_THUMBNAIL (job)) { + return thumbnail_queue; + } else if (EOM_IS_JOB_LOAD (job)) { + return load_queue; + } else if (EOM_IS_JOB_MODEL (job)) { + return model_queue; + } else if (EOM_IS_JOB_TRANSFORM (job)) { + return transform_queue; + } else if (EOM_IS_JOB_SAVE (job)) { + return save_queue; + } else if (EOM_IS_JOB_COPY (job)) { + return copy_queue; + } + + g_assert_not_reached (); + + return NULL; +} + +void +eom_job_queue_add_job (EomJob *job) +{ + GQueue *queue; + + g_return_if_fail (EOM_IS_JOB (job)); + + queue = find_queue (job); + + g_mutex_lock (eom_queue_mutex); + + add_job_to_queue_locked (queue, job); + + g_mutex_unlock (eom_queue_mutex); +} + +gboolean +eom_job_queue_remove_job (EomJob *job) +{ + gboolean retval = FALSE; + + g_return_val_if_fail (EOM_IS_JOB (job), FALSE); + + g_mutex_lock (eom_queue_mutex); + + if (EOM_IS_JOB_THUMBNAIL (job)) { + retval = remove_job_from_queue (thumbnail_queue, job); + } else if (EOM_IS_JOB_LOAD (job)) { + retval = remove_job_from_queue (load_queue, job); + } else if (EOM_IS_JOB_MODEL (job)) { + retval = remove_job_from_queue (model_queue, job); + } else if (EOM_IS_JOB_TRANSFORM (job)) { + retval = remove_job_from_queue (transform_queue, job); + } else if (EOM_IS_JOB_SAVE (job)) { + retval = remove_job_from_queue (save_queue, job); + } else if (EOM_IS_JOB_COPY (job)) { + retval = remove_job_from_queue (copy_queue, job); + } else { + g_assert_not_reached (); + } + + g_mutex_unlock (eom_queue_mutex); + + return retval; +} diff --git a/src/eom-job-queue.h b/src/eom-job-queue.h new file mode 100644 index 0000000..6a71e7b --- /dev/null +++ b/src/eom-job-queue.h @@ -0,0 +1,42 @@ +/* Eye Of Mate - Jobs Queue + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on evince code (shell/ev-job-queue.h) by: + * - Martin Kretzschmar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_JOB_QUEUE_H__ +#define __EOM_JOB_QUEUE_H__ + +#include "eom-jobs.h" + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +void eom_job_queue_init (void); + +void eom_job_queue_add_job (EomJob *job); + +gboolean eom_job_queue_remove_job (EomJob *job); + +G_END_DECLS + +#endif /* __EOM_JOB_QUEUE_H__ */ diff --git a/src/eom-jobs.c b/src/eom-jobs.c new file mode 100644 index 0000000..c3bac3a --- /dev/null +++ b/src/eom-jobs.c @@ -0,0 +1,885 @@ +/* Eye Of Mate - Jobs + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on evince code (shell/ev-jobs.c) by: + * - Martin Kretzschmar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "eom-uri-converter.h" +#include "eom-jobs.h" +#include "eom-job-queue.h" +#include "eom-image.h" +#include "eom-transform.h" +#include "eom-list-store.h" +#include "eom-thumbnail.h" +#include "eom-pixbuf-util.h" + +#include <gdk-pixbuf/gdk-pixbuf.h> + +#define EOM_JOB_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_JOB, EomJobPrivate)) + +G_DEFINE_TYPE (EomJob, eom_job, G_TYPE_OBJECT); +G_DEFINE_TYPE (EomJobThumbnail, eom_job_thumbnail, EOM_TYPE_JOB); +G_DEFINE_TYPE (EomJobLoad, eom_job_load, EOM_TYPE_JOB); +G_DEFINE_TYPE (EomJobModel, eom_job_model, EOM_TYPE_JOB); +G_DEFINE_TYPE (EomJobTransform, eom_job_transform, EOM_TYPE_JOB); +G_DEFINE_TYPE (EomJobSave, eom_job_save, EOM_TYPE_JOB); +G_DEFINE_TYPE (EomJobSaveAs, eom_job_save_as, EOM_TYPE_JOB_SAVE); +G_DEFINE_TYPE (EomJobCopy, eom_job_copy, EOM_TYPE_JOB); + +enum +{ + SIGNAL_FINISHED, + SIGNAL_PROGRESS, + SIGNAL_LAST_SIGNAL +}; + +static guint job_signals[SIGNAL_LAST_SIGNAL]; + +static void eom_job_copy_run (EomJob *ejob); +static void eom_job_load_run (EomJob *ejob); +static void eom_job_model_run (EomJob *ejob); +static void eom_job_save_run (EomJob *job); +static void eom_job_save_as_run (EomJob *job); +static void eom_job_thumbnail_run (EomJob *ejob); +static void eom_job_transform_run (EomJob *ejob); + +static void eom_job_init (EomJob *job) +{ + job->mutex = g_mutex_new(); + job->progress = 0.0; +} + +static void +eom_job_dispose (GObject *object) +{ + EomJob *job; + + job = EOM_JOB (object); + + if (job->error) { + g_error_free (job->error); + job->error = NULL; + } + + if (job->mutex) { + g_mutex_free (job->mutex); + job->mutex = NULL; + } + + (* G_OBJECT_CLASS (eom_job_parent_class)->dispose) (object); +} + +static void +eom_job_run_default (EomJob *job) +{ + g_critical ("Class \"%s\" does not implement the required run action", + G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (job))); +} + +static void +eom_job_class_init (EomJobClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = eom_job_dispose; + + class->run = eom_job_run_default; + + job_signals [SIGNAL_FINISHED] = + g_signal_new ("finished", + EOM_TYPE_JOB, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomJobClass, finished), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + job_signals [SIGNAL_PROGRESS] = + g_signal_new ("progress", + EOM_TYPE_JOB, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EomJobClass, progress), + NULL, NULL, + g_cclosure_marshal_VOID__FLOAT, + G_TYPE_NONE, 1, + G_TYPE_FLOAT); +} + +void +eom_job_finished (EomJob *job) +{ + g_return_if_fail (EOM_IS_JOB (job)); + + g_signal_emit (job, job_signals[SIGNAL_FINISHED], 0); +} + +/** + * eom_job_run: + * @job: the job to execute. + * + * Executes the job passed as @job. Usually there is no need to call this + * on your own. Jobs should be executed by using the EomJobQueue. + **/ +void +eom_job_run (EomJob *job) +{ + EomJobClass *class; + + g_return_if_fail (EOM_IS_JOB (job)); + + class = EOM_JOB_GET_CLASS (job); + if (class->run) + class->run (job); + else + eom_job_run_default (job); +} +static gboolean +notify_progress (gpointer data) +{ + EomJob *job = EOM_JOB (data); + + g_signal_emit (job, job_signals[SIGNAL_PROGRESS], 0, job->progress); + + return FALSE; +} + +void +eom_job_set_progress (EomJob *job, float progress) +{ + g_return_if_fail (EOM_IS_JOB (job)); + g_return_if_fail (progress >= 0.0 && progress <= 1.0); + + g_mutex_lock (job->mutex); + job->progress = progress; + g_mutex_unlock (job->mutex); + + g_idle_add (notify_progress, job); +} + +static void eom_job_thumbnail_init (EomJobThumbnail *job) { /* Do Nothing */ } + +static void +eom_job_thumbnail_dispose (GObject *object) +{ + EomJobThumbnail *job; + + job = EOM_JOB_THUMBNAIL (object); + + if (job->image) { + g_object_unref (job->image); + job->image = NULL; + } + + if (job->thumbnail) { + g_object_unref (job->thumbnail); + job->thumbnail = NULL; + } + + (* G_OBJECT_CLASS (eom_job_thumbnail_parent_class)->dispose) (object); +} + +static void +eom_job_thumbnail_class_init (EomJobThumbnailClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = eom_job_thumbnail_dispose; + + EOM_JOB_CLASS (class)->run = eom_job_thumbnail_run; +} + +EomJob * +eom_job_thumbnail_new (EomImage *image) +{ + EomJobThumbnail *job; + + job = g_object_new (EOM_TYPE_JOB_THUMBNAIL, NULL); + + if (image) { + job->image = g_object_ref (image); + } + + return EOM_JOB (job); +} + +static void +eom_job_thumbnail_run (EomJob *ejob) +{ + gchar *orig_width, *orig_height; + gint width, height; + GdkPixbuf *pixbuf; + EomJobThumbnail *job; + + g_return_if_fail (EOM_IS_JOB_THUMBNAIL (ejob)); + + job = EOM_JOB_THUMBNAIL (ejob); + + if (ejob->error) { + g_error_free (ejob->error); + ejob->error = NULL; + } + + job->thumbnail = eom_thumbnail_load (job->image, + &ejob->error); + + if (!job->thumbnail) { + ejob->finished = TRUE; + return; + } + + orig_width = g_strdup (gdk_pixbuf_get_option (job->thumbnail, "tEXt::Thumb::Image::Width")); + orig_height = g_strdup (gdk_pixbuf_get_option (job->thumbnail, "tEXt::Thumb::Image::Height")); + + pixbuf = eom_thumbnail_fit_to_size (job->thumbnail, EOM_LIST_STORE_THUMB_SIZE); + g_object_unref (job->thumbnail); + job->thumbnail = eom_thumbnail_add_frame (pixbuf); + g_object_unref (pixbuf); + + if (orig_width) { + sscanf (orig_width, "%i", &width); + g_object_set_data (G_OBJECT (job->thumbnail), + EOM_THUMBNAIL_ORIGINAL_WIDTH, + GINT_TO_POINTER (width)); + g_free (orig_width); + } + if (orig_height) { + sscanf (orig_height, "%i", &height); + g_object_set_data (G_OBJECT (job->thumbnail), + EOM_THUMBNAIL_ORIGINAL_HEIGHT, + GINT_TO_POINTER (height)); + g_free (orig_height); + } + + if (ejob->error) { + g_warning ("%s", ejob->error->message); + } + + ejob->finished = TRUE; +} + +static void eom_job_load_init (EomJobLoad *job) { /* Do Nothing */ } + +static void +eom_job_load_dispose (GObject *object) +{ + EomJobLoad *job; + + job = EOM_JOB_LOAD (object); + + if (job->image) { + g_object_unref (job->image); + job->image = NULL; + } + + (* G_OBJECT_CLASS (eom_job_load_parent_class)->dispose) (object); +} + +static void +eom_job_load_class_init (EomJobLoadClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = eom_job_load_dispose; + EOM_JOB_CLASS (class)->run = eom_job_load_run; +} + +EomJob * +eom_job_load_new (EomImage *image, EomImageData data) +{ + EomJobLoad *job; + + job = g_object_new (EOM_TYPE_JOB_LOAD, NULL); + + if (image) { + job->image = g_object_ref (image); + } + job->data = data; + + return EOM_JOB (job); +} + +static void +eom_job_load_run (EomJob *job) +{ + g_return_if_fail (EOM_IS_JOB_LOAD (job)); + + if (job->error) { + g_error_free (job->error); + job->error = NULL; + } + + eom_image_load (EOM_IMAGE (EOM_JOB_LOAD (job)->image), + EOM_JOB_LOAD (job)->data, + job, + &job->error); + + job->finished = TRUE; +} + +static void eom_job_model_init (EomJobModel *job) { /* Do Nothing */ } + +static void +eom_job_model_dispose (GObject *object) +{ + EomJobModel *job; + + job = EOM_JOB_MODEL (object); + + (* G_OBJECT_CLASS (eom_job_model_parent_class)->dispose) (object); +} + +static void +eom_job_model_class_init (EomJobModelClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = eom_job_model_dispose; + EOM_JOB_CLASS (class)->run = eom_job_model_run; +} + +EomJob * +eom_job_model_new (GSList *file_list) +{ + EomJobModel *job; + + job = g_object_new (EOM_TYPE_JOB_MODEL, NULL); + + job->file_list = file_list; + + return EOM_JOB (job); +} + +static void +filter_files (GSList *files, GList **file_list, GList **error_list) +{ + GSList *it; + GFileInfo *file_info; + + for (it = files; it != NULL; it = it->next) { + GFile *file; + GFileType type = G_FILE_TYPE_UNKNOWN; + + file = (GFile *) it->data; + + if (file != NULL) { + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + if (file_info == NULL) { + type = G_FILE_TYPE_UNKNOWN; + } else { + type = g_file_info_get_file_type (file_info); + + /* Workaround for gvfs backends that + don't set the GFileType. */ + if (G_UNLIKELY (type == G_FILE_TYPE_UNKNOWN)) { + const gchar *ctype; + + ctype = g_file_info_get_content_type (file_info); + + /* If the content type is supported + adjust the file_type */ + if (eom_image_is_supported_mime_type (ctype)) + type = G_FILE_TYPE_REGULAR; + } + + g_object_unref (file_info); + } + } + + switch (type) { + case G_FILE_TYPE_REGULAR: + case G_FILE_TYPE_DIRECTORY: + *file_list = g_list_prepend (*file_list, g_object_ref (file)); + break; + default: + *error_list = g_list_prepend (*error_list, + g_file_get_uri (file)); + break; + } + + g_object_unref (file); + } + + *file_list = g_list_reverse (*file_list); + *error_list = g_list_reverse (*error_list); +} + +static void +eom_job_model_run (EomJob *ejob) +{ + GList *filtered_list = NULL; + GList *error_list = NULL; + EomJobModel *job; + + g_return_if_fail (EOM_IS_JOB_MODEL (ejob)); + + job = EOM_JOB_MODEL (ejob); + + filter_files (job->file_list, &filtered_list, &error_list); + + job->store = EOM_LIST_STORE (eom_list_store_new ()); + + eom_list_store_add_files (job->store, filtered_list); + + g_list_foreach (filtered_list, (GFunc) g_object_unref, NULL); + g_list_free (filtered_list); + + g_list_foreach (error_list, (GFunc) g_free, NULL); + g_list_free (error_list); + + ejob->finished = TRUE; +} + +static void eom_job_transform_init (EomJobTransform *job) { /* Do Nothing */ } + +static void +eom_job_transform_dispose (GObject *object) +{ + EomJobTransform *job; + + job = EOM_JOB_TRANSFORM (object); + + if (job->trans) { + g_object_unref (job->trans); + job->trans = NULL; + } + + g_list_foreach (job->images, (GFunc) g_object_unref, NULL); + g_list_free (job->images); + + (* G_OBJECT_CLASS (eom_job_transform_parent_class)->dispose) (object); +} + +static void +eom_job_transform_class_init (EomJobTransformClass *class) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (class); + + oclass->dispose = eom_job_transform_dispose; + + EOM_JOB_CLASS (class)->run = eom_job_transform_run; +} + +EomJob * +eom_job_transform_new (GList *images, EomTransform *trans) +{ + EomJobTransform *job; + + job = g_object_new (EOM_TYPE_JOB_TRANSFORM, NULL); + + if (trans) { + job->trans = g_object_ref (trans); + } else { + job->trans = NULL; + } + + job->images = images; + + return EOM_JOB (job); +} + +static gboolean +eom_job_transform_image_modified (gpointer data) +{ + g_return_val_if_fail (EOM_IS_IMAGE (data), FALSE); + + eom_image_modified (EOM_IMAGE (data)); + g_object_unref (G_OBJECT (data)); + + return FALSE; +} + +void +eom_job_transform_run (EomJob *ejob) +{ + EomJobTransform *job; + GList *it; + + g_return_if_fail (EOM_IS_JOB_TRANSFORM (ejob)); + + job = EOM_JOB_TRANSFORM (ejob); + + if (ejob->error) { + g_error_free (ejob->error); + ejob->error = NULL; + } + + for (it = job->images; it != NULL; it = it->next) { + EomImage *image = EOM_IMAGE (it->data); + + if (job->trans == NULL) { + eom_image_undo (image); + } else { + eom_image_transform (image, job->trans, ejob); + } + + if (eom_image_is_modified (image) || job->trans == NULL) { + g_object_ref (image); + g_idle_add (eom_job_transform_image_modified, image); + } + } + + ejob->finished = TRUE; +} + +static void eom_job_save_init (EomJobSave *job) { /* do nothing */ } + +static void +eom_job_save_dispose (GObject *object) +{ + EomJobSave *job; + + job = EOM_JOB_SAVE (object); + + if (job->images) { + g_list_foreach (job->images, (GFunc) g_object_unref, NULL); + g_list_free (job->images); + job->images = NULL; + } + + (* G_OBJECT_CLASS (eom_job_save_parent_class)->dispose) (object); +} + +static void +eom_job_save_class_init (EomJobSaveClass *class) +{ + G_OBJECT_CLASS (class)->dispose = eom_job_save_dispose; + EOM_JOB_CLASS (class)->run = eom_job_save_run; +} + +EomJob * +eom_job_save_new (GList *images) +{ + EomJobSave *job; + + job = g_object_new (EOM_TYPE_JOB_SAVE, NULL); + + job->images = images; + job->current_image = NULL; + + return EOM_JOB (job); +} + +static void +save_progress_handler (EomImage *image, gfloat progress, gpointer data) +{ + EomJobSave *job = EOM_JOB_SAVE (data); + guint n_images = g_list_length (job->images); + gfloat job_progress; + + job_progress = (job->current_pos / (gfloat) n_images) + (progress / n_images); + + eom_job_set_progress (EOM_JOB (job), job_progress); +} + +static void +eom_job_save_run (EomJob *ejob) +{ + EomJobSave *job; + GList *it; + + g_return_if_fail (EOM_IS_JOB_SAVE (ejob)); + + job = EOM_JOB_SAVE (ejob); + + job->current_pos = 0; + + for (it = job->images; it != NULL; it = it->next, job->current_pos++) { + EomImage *image = EOM_IMAGE (it->data); + EomImageSaveInfo *save_info = NULL; + gulong handler_id = 0; + gboolean success = FALSE; + + job->current_image = image; + + /* Make sure the image doesn't go away while saving */ + eom_image_data_ref (image); + + if (!eom_image_has_data (image, EOM_IMAGE_DATA_ALL)) { + eom_image_load (image, + EOM_IMAGE_DATA_ALL, + NULL, + &ejob->error); + } + + handler_id = g_signal_connect (G_OBJECT (image), + "save-progress", + G_CALLBACK (save_progress_handler), + job); + + save_info = eom_image_save_info_from_image (image); + + success = eom_image_save_by_info (image, + save_info, + &ejob->error); + + if (save_info) + g_object_unref (save_info); + + if (handler_id != 0) + g_signal_handler_disconnect (G_OBJECT (image), handler_id); + + eom_image_data_unref (image); + + if (!success) break; + } + + ejob->finished = TRUE; +} + +static void eom_job_save_as_init (EomJobSaveAs *job) { /* do nothing */ } + +static void eom_job_save_as_dispose (GObject *object) +{ + EomJobSaveAs *job = EOM_JOB_SAVE_AS (object); + + if (job->converter != NULL) { + g_object_unref (job->converter); + job->converter = NULL; + } + + if (job->file != NULL) { + g_object_unref (job->file); + job->file = NULL; + } + + (* G_OBJECT_CLASS (eom_job_save_as_parent_class)->dispose) (object); +} + +static void +eom_job_save_as_class_init (EomJobSaveAsClass *class) +{ + G_OBJECT_CLASS (class)->dispose = eom_job_save_as_dispose; + EOM_JOB_CLASS (class)->run = eom_job_save_as_run; +} + +EomJob * +eom_job_save_as_new (GList *images, EomURIConverter *converter, GFile *file) +{ + EomJobSaveAs *job; + + g_assert (converter != NULL || g_list_length (images) == 1); + + job = g_object_new (EOM_TYPE_JOB_SAVE_AS, NULL); + + EOM_JOB_SAVE(job)->images = images; + + job->converter = converter ? g_object_ref (converter) : NULL; + job->file = file ? g_object_ref (file) : NULL; + + return EOM_JOB (job); +} + +static void +eom_job_save_as_run (EomJob *ejob) +{ + EomJobSave *job; + EomJobSaveAs *saveas_job; + GList *it; + guint n_images; + + g_return_if_fail (EOM_IS_JOB_SAVE_AS (ejob)); + + job = EOM_JOB_SAVE (ejob); + + n_images = g_list_length (job->images); + + saveas_job = EOM_JOB_SAVE_AS (job); + + job->current_pos = 0; + + for (it = job->images; it != NULL; it = it->next, job->current_pos++) { + GdkPixbufFormat *format; + EomImageSaveInfo *src_info, *dest_info; + EomImage *image = EOM_IMAGE (it->data); + gboolean success = FALSE; + gulong handler_id = 0; + + job->current_image = image; + + eom_image_data_ref (image); + + if (!eom_image_has_data (image, EOM_IMAGE_DATA_ALL)) { + eom_image_load (image, + EOM_IMAGE_DATA_ALL, + NULL, + &ejob->error); + } + + g_assert (ejob->error == NULL); + + handler_id = g_signal_connect (G_OBJECT (image), + "save-progress", + G_CALLBACK (save_progress_handler), + job); + + src_info = eom_image_save_info_from_image (image); + + if (n_images == 1) { + g_assert (saveas_job->file != NULL); + + format = eom_pixbuf_get_format (saveas_job->file); + + dest_info = eom_image_save_info_from_file (saveas_job->file, + format); + + /* SaveAsDialog has already secured permission to overwrite */ + if (dest_info->exists) { + dest_info->overwrite = TRUE; + } + } else { + GFile *dest_file; + gboolean result; + + result = eom_uri_converter_do (saveas_job->converter, + image, + &dest_file, + &format, + NULL); + + g_assert (result); + + dest_info = eom_image_save_info_from_file (dest_file, + format); + } + + success = eom_image_save_as_by_info (image, + src_info, + dest_info, + &ejob->error); + + if (src_info) + g_object_unref (src_info); + + if (dest_info) + g_object_unref (dest_info); + + if (handler_id != 0) + g_signal_handler_disconnect (G_OBJECT (image), handler_id); + + eom_image_data_unref (image); + + if (!success) + break; + } + + ejob->finished = TRUE; +} + +static void eom_job_copy_init (EomJobCopy *job) { /* do nothing */}; + +static void +eom_job_copy_dispose (GObject *object) +{ + EomJobCopy *job = EOM_JOB_COPY (object); + + if (job->dest) { + g_free (job->dest); + job->dest = NULL; + } + + (* G_OBJECT_CLASS (eom_job_copy_parent_class)->dispose) (object); +} + +static void +eom_job_copy_class_init (EomJobCopyClass *class) +{ + G_OBJECT_CLASS (class)->dispose = eom_job_copy_dispose; + EOM_JOB_CLASS (class)->run = eom_job_copy_run; +} + +EomJob * +eom_job_copy_new (GList *images, const gchar *dest) +{ + EomJobCopy *job; + + g_assert (images != NULL && dest != NULL); + + job = g_object_new (EOM_TYPE_JOB_COPY, NULL); + + job->images = images; + job->dest = g_strdup (dest); + + return EOM_JOB (job); +} + +static void +eom_job_copy_progress_callback (goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + gfloat job_progress; + guint n_images; + EomJobCopy *job; + + job = EOM_JOB_COPY (user_data); + n_images = g_list_length (job->images); + + job_progress = ((current_num_bytes / (gfloat) total_num_bytes) + job->current_pos)/n_images; + + eom_job_set_progress (EOM_JOB (job), job_progress); +} + +void +eom_job_copy_run (EomJob *ejob) +{ + EomJobCopy *job; + GList *it; + guint n_images; + GFile *src, *dest; + gchar *filename, *dest_filename; + + g_return_if_fail (EOM_IS_JOB_COPY (ejob)); + + job = EOM_JOB_COPY (ejob); + + n_images = g_list_length (job->images); + + job->current_pos = 0; + + for (it = job->images; it != NULL; it = g_list_next (it), job->current_pos++) { + src = (GFile *) it->data; + filename = g_file_get_basename (src); + dest_filename = g_build_filename (job->dest, filename, NULL); + dest = g_file_new_for_path (dest_filename); + + g_file_copy (src, dest, + G_FILE_COPY_OVERWRITE, NULL, + eom_job_copy_progress_callback, job, + &ejob->error); + g_free (filename); + g_free (dest_filename); + } + + ejob->finished = TRUE; +} diff --git a/src/eom-jobs.h b/src/eom-jobs.h new file mode 100644 index 0000000..7fd4965 --- /dev/null +++ b/src/eom-jobs.h @@ -0,0 +1,275 @@ +/* Eye Of Mate - Jobs + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on evince code (shell/ev-jobs.h) by: + * - Martin Kretzschmar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_JOBS_H__ +#define __EOM_JOBS_H__ + +#include "eom-list-store.h" +#include "eom-transform.h" +#include "eom-enums.h" + +#include <glib.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +#ifndef __EOM_IMAGE_DECLR__ +#define __EOM_IMAGE_DECLR__ + typedef struct _EomImage EomImage; +#endif + +#ifndef __EOM_URI_CONVERTER_DECLR__ +#define __EOM_URI_CONVERTER_DECLR__ +typedef struct _EomURIConverter EomURIConverter; +#endif + +#ifndef __EOM_JOB_DECLR__ +#define __EOM_JOB_DECLR__ +typedef struct _EomJob EomJob; +#endif +typedef struct _EomJobClass EomJobClass; + +typedef struct _EomJobThumbnail EomJobThumbnail; +typedef struct _EomJobThumbnailClass EomJobThumbnailClass; + +typedef struct _EomJobLoad EomJobLoad; +typedef struct _EomJobLoadClass EomJobLoadClass; + +typedef struct _EomJobModel EomJobModel; +typedef struct _EomJobModelClass EomJobModelClass; + +typedef struct _EomJobTransform EomJobTransform; +typedef struct _EomJobTransformClass EomJobTransformClass; + +typedef struct _EomJobSave EomJobSave; +typedef struct _EomJobSaveClass EomJobSaveClass; + +typedef struct _EomJobSaveAs EomJobSaveAs; +typedef struct _EomJobSaveAsClass EomJobSaveAsClass; + +typedef struct _EomJobCopy EomJobCopy; +typedef struct _EomJobCopyClass EomJobCopyClass; + +#define EOM_TYPE_JOB (eom_job_get_type()) +#define EOM_JOB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB, EomJob)) +#define EOM_JOB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB, EomJobClass)) +#define EOM_IS_JOB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB)) +#define EOM_JOB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_JOB, EomJobClass)) + +#define EOM_TYPE_JOB_THUMBNAIL (eom_job_thumbnail_get_type()) +#define EOM_JOB_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_THUMBNAIL, EomJobThumbnail)) +#define EOM_JOB_THUMBNAIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_THUMBNAIL, EomJobThumbnailClass)) +#define EOM_IS_JOB_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_THUMBNAIL)) + +#define EOM_TYPE_JOB_LOAD (eom_job_load_get_type()) +#define EOM_JOB_LOAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_LOAD, EomJobLoad)) +#define EOM_JOB_LOAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_LOAD, EomJobLoadClass)) +#define EOM_IS_JOB_LOAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_LOAD)) + +#define EOM_TYPE_JOB_MODEL (eom_job_model_get_type()) +#define EOM_JOB_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_MODEL, EomJobModel)) +#define EOM_JOB_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_MODEL, EomJobModelClass)) +#define EOM_IS_JOB_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_MODEL)) + +#define EOM_TYPE_JOB_TRANSFORM (eom_job_transform_get_type()) +#define EOM_JOB_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_TRANSFORM, EomJobTransform)) +#define EOM_JOB_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_TRANSFORM, EomJobTransformClass)) +#define EOM_IS_JOB_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_TRANSFORM)) + +#define EOM_TYPE_JOB_SAVE (eom_job_save_get_type()) +#define EOM_JOB_SAVE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_SAVE, EomJobSave)) +#define EOM_JOB_SAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_SAVE, EomJobSaveClass)) +#define EOM_IS_JOB_SAVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_SAVE)) +#define EOM_JOB_SAVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_JOB_SAVE, EomJobSaveClass)) + +#define EOM_TYPE_JOB_SAVE_AS (eom_job_save_as_get_type()) +#define EOM_JOB_SAVE_AS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_SAVE_AS, EomJobSaveAs)) +#define EOM_JOB_SAVE_AS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_SAVE_AS, EomJobSaveAsClass)) +#define EOM_IS_JOB_SAVE_AS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_SAVE_AS)) + +#define EOM_TYPE_JOB_COPY (eom_job_copy_get_type()) +#define EOM_JOB_COPY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_JOB_COPY, EomJobCopy)) +#define EOM_JOB_COPY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_JOB_COPY, EomJobCopyClass)) +#define EOM_IS_JOB_COPY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_JOB_COPY)) + + +struct _EomJob +{ + GObject parent; + + GError *error; + GMutex *mutex; + float progress; + gboolean finished; +}; + +struct _EomJobClass +{ + GObjectClass parent_class; + + void (* finished) (EomJob *job); + void (* progress) (EomJob *job, float progress); + void (*run) (EomJob *job); +}; + +struct _EomJobThumbnail +{ + EomJob parent; + EomImage *image; + GdkPixbuf *thumbnail; +}; + +struct _EomJobThumbnailClass +{ + EomJobClass parent_class; +}; + +struct _EomJobLoad +{ + EomJob parent; + EomImage *image; + EomImageData data; +}; + +struct _EomJobLoadClass +{ + EomJobClass parent_class; +}; + +struct _EomJobModel +{ + EomJob parent; + EomListStore *store; + GSList *file_list; +}; + +struct _EomJobModelClass +{ + EomJobClass parent_class; +}; + +struct _EomJobTransform +{ + EomJob parent; + GList *images; + EomTransform *trans; +}; + +struct _EomJobTransformClass +{ + EomJobClass parent_class; +}; + +typedef enum { + EOM_SAVE_RESPONSE_NONE, + EOM_SAVE_RESPONSE_RETRY, + EOM_SAVE_RESPONSE_SKIP, + EOM_SAVE_RESPONSE_OVERWRITE, + EOM_SAVE_RESPONSE_CANCEL, + EOM_SAVE_RESPONSE_LAST +} EomJobSaveResponse; + +struct _EomJobSave +{ + EomJob parent; + GList *images; + guint current_pos; + EomImage *current_image; +}; + +struct _EomJobSaveClass +{ + EomJobClass parent_class; +}; + +struct _EomJobSaveAs +{ + EomJobSave parent; + EomURIConverter *converter; + GFile *file; +}; + +struct _EomJobSaveAsClass +{ + EomJobSaveClass parent; +}; + +struct _EomJobCopy +{ + EomJob parent; + GList *images; + guint current_pos; + gchar *dest; +}; + +struct _EomJobCopyClass +{ + EomJobClass parent_class; +}; + +/* base job class */ +GType eom_job_get_type (void) G_GNUC_CONST; +void eom_job_finished (EomJob *job); +void eom_job_run (EomJob *job); +void eom_job_set_progress (EomJob *job, + float progress); + +/* EomJobThumbnail */ +GType eom_job_thumbnail_get_type (void) G_GNUC_CONST; +EomJob *eom_job_thumbnail_new (EomImage *image); + +/* EomJobLoad */ +GType eom_job_load_get_type (void) G_GNUC_CONST; +EomJob *eom_job_load_new (EomImage *image, + EomImageData data); + +/* EomJobModel */ +GType eom_job_model_get_type (void) G_GNUC_CONST; +EomJob *eom_job_model_new (GSList *file_list); + +/* EomJobTransform */ +GType eom_job_transform_get_type (void) G_GNUC_CONST; +EomJob *eom_job_transform_new (GList *images, + EomTransform *trans); + +/* EomJobSave */ +GType eom_job_save_get_type (void) G_GNUC_CONST; +EomJob *eom_job_save_new (GList *images); + +/* EomJobSaveAs */ +GType eom_job_save_as_get_type (void) G_GNUC_CONST; +EomJob *eom_job_save_as_new (GList *images, + EomURIConverter *converter, + GFile *file); + +/*EomJobCopy */ +GType eom_job_copy_get_type (void) G_GNUC_CONST; +EomJob *eom_job_copy_new (GList *images, + const gchar *dest); + +G_END_DECLS + +#endif /* __EOM_JOBS_H__ */ diff --git a/src/eom-list-store.c b/src/eom-list-store.c new file mode 100644 index 0000000..562019e --- /dev/null +++ b/src/eom-list-store.c @@ -0,0 +1,931 @@ +/* Eye Of Mate - Image Store + * + * Copyright (C) 2006-2008 The Free Software Foundation + * + * Author: Claudio Saavedra <[email protected]> + * + * Based on code by: Jens Finke <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "eom-list-store.h" +#include "eom-thumbnail.h" +#include "eom-image.h" +#include "eom-job-queue.h" +#include "eom-jobs.h" + +#include <string.h> + +#define EOM_LIST_STORE_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_LIST_STORE, EomListStorePrivate)) + +G_DEFINE_TYPE (EomListStore, eom_list_store, GTK_TYPE_LIST_STORE); + +struct _EomListStorePrivate { + GList *monitors; /* Monitors for the directories */ + gint initial_image; /* The image that should be selected firstly by the view. */ + GdkPixbuf *busy_image; /* Loading image icon */ + GdkPixbuf *missing_image; /* Missing image icon */ + GMutex *mutex; /* Mutex for saving the jobs in the model */ +}; + +static void +eom_list_store_finalize (GObject *object) +{ + EomListStore *store = EOM_LIST_STORE (object); + + if (store->priv != NULL) { + g_free (store->priv); + store->priv = NULL; + } + + G_OBJECT_CLASS (eom_list_store_parent_class)->finalize (object); +} + +static void +foreach_monitors_free (gpointer data, gpointer user_data) +{ + g_file_monitor_cancel (G_FILE_MONITOR (data)); +} + +static void +eom_list_store_dispose (GObject *object) +{ + EomListStore *store = EOM_LIST_STORE (object); + + g_list_foreach (store->priv->monitors, + foreach_monitors_free, NULL); + + g_list_free (store->priv->monitors); + + store->priv->monitors = NULL; + + if(store->priv->busy_image != NULL) { + g_object_unref (store->priv->busy_image); + store->priv->busy_image = NULL; + } + + if(store->priv->missing_image != NULL) { + g_object_unref (store->priv->missing_image); + store->priv->missing_image = NULL; + } + + g_mutex_free (store->priv->mutex); + + G_OBJECT_CLASS (eom_list_store_parent_class)->dispose (object); +} + +static void +eom_list_store_class_init (EomListStoreClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = eom_list_store_finalize; + object_class->dispose = eom_list_store_dispose; + + g_type_class_add_private (object_class, sizeof (EomListStorePrivate)); +} + +/* + Sorting functions +*/ + +static gint +eom_list_store_compare_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gint r_value; + + EomImage *image_a, *image_b; + + gtk_tree_model_get (model, a, + EOM_LIST_STORE_EOM_IMAGE, &image_a, + -1); + + gtk_tree_model_get (model, b, + EOM_LIST_STORE_EOM_IMAGE, &image_b, + -1); + + r_value = strcmp (eom_image_get_collate_key (image_a), + eom_image_get_collate_key (image_b)); + + g_object_unref (G_OBJECT (image_a)); + g_object_unref (G_OBJECT (image_b)); + + return r_value; +} + +static GdkPixbuf * +eom_list_store_get_icon (const gchar *icon_name) +{ + GError *error = NULL; + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf; + + icon_theme = gtk_icon_theme_get_default (); + + pixbuf = gtk_icon_theme_load_icon (icon_theme, + icon_name, + EOM_LIST_STORE_THUMB_SIZE, + 0, + &error); + + if (!pixbuf) { + g_warning ("Couldn't load icon: %s", error->message); + g_error_free (error); + } + + return pixbuf; +} + +static void +eom_list_store_init (EomListStore *self) +{ + GType types[EOM_LIST_STORE_NUM_COLUMNS]; + + types[EOM_LIST_STORE_THUMBNAIL] = GDK_TYPE_PIXBUF; + types[EOM_LIST_STORE_EOM_IMAGE] = G_TYPE_OBJECT; + types[EOM_LIST_STORE_THUMB_SET] = G_TYPE_BOOLEAN; + types[EOM_LIST_STORE_EOM_JOB] = G_TYPE_POINTER; + + gtk_list_store_set_column_types (GTK_LIST_STORE (self), + EOM_LIST_STORE_NUM_COLUMNS, types); + + self->priv = EOM_LIST_STORE_GET_PRIVATE (self); + + self->priv->monitors = NULL; + self->priv->initial_image = -1; + + self->priv->busy_image = eom_list_store_get_icon ("image-loading"); + self->priv->missing_image = eom_list_store_get_icon ("image-missing"); + + self->priv->mutex = g_mutex_new (); + + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (self), + eom_list_store_compare_func, + NULL, NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); +} + +/** + * eom_list_store_new: + * + * Creates a new and empty #EomListStore. + * + * Returns: a newly created #EomListStore. + **/ +GtkListStore* +eom_list_store_new (void) +{ + return g_object_new (EOM_TYPE_LIST_STORE, NULL); +} + +/* + Searchs for a file in the store. If found and @iter_found is not NULL, + then sets @iter_found to a #GtkTreeIter pointing to the file. + */ +static gboolean +is_file_in_list_store (EomListStore *store, + const gchar *info_uri, + GtkTreeIter *iter_found) +{ + gboolean found = FALSE; + EomImage *image; + GFile *file; + gchar *str; + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { + return FALSE; + } + + do { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + if (!image) + continue; + + file = eom_image_get_file (image); + str = g_file_get_uri (file); + + found = (strcmp (str, info_uri) == 0)? TRUE : FALSE; + + g_object_unref (file); + g_free (str); + g_object_unref (G_OBJECT (image)); + + } while (!found && + gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + + if (found && iter_found != NULL) { + *iter_found = iter; + } + + return found; +} + +static gboolean +is_file_in_list_store_file (EomListStore *store, + GFile *file, + GtkTreeIter *iter_found) +{ + gchar *uri_str; + gboolean result; + + uri_str = g_file_get_uri (file); + + result = is_file_in_list_store (store, uri_str, iter_found); + + g_free (uri_str); + + return result; +} + +static void +eom_job_thumbnail_cb (EomJobThumbnail *job, gpointer data) +{ + EomListStore *store; + GtkTreeIter iter; + EomImage *image; + GdkPixbuf *thumbnail; + GFile *file; + + g_return_if_fail (EOM_IS_LIST_STORE (data)); + + store = EOM_LIST_STORE (data); + + file = eom_image_get_file (job->image); + + if (is_file_in_list_store_file (store, file, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + + if (job->thumbnail) { + eom_image_set_thumbnail (image, job->thumbnail); + + /* Getting the thumbnail, in case it needed + * transformations */ + thumbnail = eom_image_get_thumbnail (image); + } else { + thumbnail = g_object_ref (store->priv->missing_image); + } + + gtk_list_store_set (GTK_LIST_STORE (store), &iter, + EOM_LIST_STORE_THUMBNAIL, thumbnail, + EOM_LIST_STORE_THUMB_SET, TRUE, + EOM_LIST_STORE_EOM_JOB, NULL, + -1); + + g_object_unref (thumbnail); + } + + g_object_unref (file); +} + +static void +on_image_changed (EomImage *image, EomListStore *store) +{ + GtkTreePath *path; + GtkTreeIter iter; + gint pos; + + pos = eom_list_store_get_pos_by_image (store, image); + path = gtk_tree_path_new_from_indices (pos, -1); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + eom_list_store_thumbnail_refresh (store, &iter); + gtk_tree_path_free (path); +} + +/** + * eom_list_store_remove: + * @store: An #EomListStore. + * @iter: A #GtkTreeIter. + * + * Removes the image pointed by @iter from @store. + **/ +static void +eom_list_store_remove (EomListStore *store, GtkTreeIter *iter) +{ + EomImage *image; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + + g_signal_handlers_disconnect_by_func (image, on_image_changed, store); + g_object_unref (image); + + gtk_list_store_remove (GTK_LIST_STORE (store), iter); +} + +/** + * eom_list_store_append_image: + * @store: An #EomListStore. + * @image: An #EomImage. + * + * Adds an #EomImage to @store. The thumbnail of the image is not + * loaded and will only be loaded if the thumbnail is made visible + * or eom_list_store_set_thumbnail() is called. + * + **/ +void +eom_list_store_append_image (EomListStore *store, EomImage *image) +{ + GtkTreeIter iter; + + g_signal_connect (image, "changed", + G_CALLBACK (on_image_changed), + store); + + gtk_list_store_append (GTK_LIST_STORE (store), &iter); + gtk_list_store_set (GTK_LIST_STORE (store), &iter, + EOM_LIST_STORE_EOM_IMAGE, image, + EOM_LIST_STORE_THUMBNAIL, store->priv->busy_image, + EOM_LIST_STORE_THUMB_SET, FALSE, + -1); +} + +static void +eom_list_store_append_image_from_file (EomListStore *store, + GFile *file) +{ + EomImage *image; + + g_return_if_fail (EOM_IS_LIST_STORE (store)); + + image = eom_image_new_file (file); + + eom_list_store_append_image (store, image); +} + +static void +file_monitor_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + EomListStore *store) +{ + const char *mimetype; + GFileInfo *file_info; + GtkTreeIter iter; + EomImage *image; + + switch (event) { + case G_FILE_MONITOR_EVENT_CHANGED: + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + if (file_info == NULL) { + break; + } + mimetype = g_file_info_get_content_type (file_info); + + if (is_file_in_list_store_file (store, file, &iter)) { + if (eom_image_is_supported_mime_type (mimetype)) { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + eom_image_file_changed (image); + g_object_unref (image); + eom_list_store_thumbnail_refresh (store, &iter); + } else { + eom_list_store_remove (store, &iter); + } + } else { + if (eom_image_is_supported_mime_type (mimetype)) { + eom_list_store_append_image_from_file (store, file); + } + } + g_object_unref (file_info); + break; + case G_FILE_MONITOR_EVENT_DELETED: + if (is_file_in_list_store_file (store, file, &iter)) { + EomImage *image; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + + eom_list_store_remove (store, &iter); + } + break; + case G_FILE_MONITOR_EVENT_CREATED: + if (!is_file_in_list_store_file (store, file, NULL)) { + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + if (file_info == NULL) { + break; + } + mimetype = g_file_info_get_content_type (file_info); + + if (eom_image_is_supported_mime_type (mimetype)) { + eom_list_store_append_image_from_file (store, file); + } + g_object_unref (file_info); + } + break; + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + if (file_info == NULL) { + break; + } + mimetype = g_file_info_get_content_type (file_info); + if (is_file_in_list_store_file (store, file, &iter) && + eom_image_is_supported_mime_type (mimetype)) { + eom_list_store_thumbnail_refresh (store, &iter); + } + g_object_unref (file_info); + break; + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: + case G_FILE_MONITOR_EVENT_UNMOUNTED: + break; + } +} + +/* + * Called for each file in a directory. Checks if the file is some + * sort of image. If so, it creates an image object and adds it to the + * list. + */ +static void +directory_visit (GFile *directory, + GFileInfo *children_info, + EomListStore *store) +{ + GFile *child; + gboolean load_uri = FALSE; + const char *mime_type, *name; + + mime_type = g_file_info_get_content_type (children_info); + name = g_file_info_get_name (children_info); + + if (!g_str_has_prefix (name, ".")) { + if (eom_image_is_supported_mime_type (mime_type)) { + load_uri = TRUE; + } + } + + if (load_uri) { + child = g_file_get_child (directory, name); + eom_list_store_append_image_from_file (store, child); + } +} + +static void +eom_list_store_append_directory (EomListStore *store, + GFile *file, + GFileType file_type) +{ + GFileMonitor *file_monitor; + GFileEnumerator *file_enumerator; + GFileInfo *file_info; + + g_return_if_fail (file_type == G_FILE_TYPE_DIRECTORY); + + file_monitor = g_file_monitor_directory (file, + 0, NULL, NULL); + + if (file_monitor != NULL) { + g_signal_connect (file_monitor, "changed", + G_CALLBACK (file_monitor_changed_cb), store); + + /* prepend seems more efficient to me, we don't need this list + to be sorted */ + store->priv->monitors = g_list_prepend (store->priv->monitors, file_monitor); + } + + file_enumerator = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," + G_FILE_ATTRIBUTE_STANDARD_NAME, + 0, NULL, NULL); + file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL); + + while (file_info != NULL) + { + directory_visit (file, file_info, store); + g_object_unref (file_info); + file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL); + } + g_object_unref (file_enumerator); +} + +/** + * eom_list_store_add_files: + * @store: An #EomListStore. + * @file_list: A %NULL-terminated list of #GFile's. + * + * Adds a list of #GFile's to @store. The given list + * must be %NULL-terminated. + * + * If any of the #GFile's in @file_list is a directory, all the images + * in that directory will be added to @store. If the list of files contains + * only one file and this is a regular file, then all the images in the same + * directory will be added as well to @store. + * + **/ +void +eom_list_store_add_files (EomListStore *store, GList *file_list) +{ + GList *it; + GFileInfo *file_info; + GFileType file_type; + GFile *initial_file = NULL; + GtkTreeIter iter; + + if (file_list == NULL) { + return; + } + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + for (it = file_list; it != NULL; it = it->next) { + GFile *file = (GFile *) it->data; + + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE"," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + if (file_info == NULL) { + continue; + } + file_type = g_file_info_get_file_type (file_info); + + /* Workaround for gvfs backends that don't set the GFileType. */ + if (G_UNLIKELY (file_type == G_FILE_TYPE_UNKNOWN)) { + const gchar *ctype; + + ctype = g_file_info_get_content_type (file_info); + + /* If the content type is supported adjust file_type */ + if (eom_image_is_supported_mime_type (ctype)) + file_type = G_FILE_TYPE_REGULAR; + } + + g_object_unref (file_info); + + if (file_type == G_FILE_TYPE_DIRECTORY) { + eom_list_store_append_directory (store, file, file_type); + } else if (file_type == G_FILE_TYPE_REGULAR && + g_list_length (file_list) == 1) { + + initial_file = g_file_dup (file); + + file = g_file_get_parent (file); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE, + 0, NULL, NULL); + + /* If we can't get a file_info, + file_type will stay as G_FILE_TYPE_REGULAR */ + if (file_info != NULL) { + file_type = g_file_info_get_file_type (file_info); + g_object_unref (file_info); + } + + if (file_type == G_FILE_TYPE_DIRECTORY) { + eom_list_store_append_directory (store, file, file_type); + + if (!is_file_in_list_store_file (store, + initial_file, + &iter)) { + eom_list_store_append_image_from_file (store, initial_file); + } + } else { + eom_list_store_append_image_from_file (store, initial_file); + } + g_object_unref (file); + } else if (file_type == G_FILE_TYPE_REGULAR && + g_list_length (file_list) > 1) { + eom_list_store_append_image_from_file (store, file); + } + } + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + if (initial_file && + is_file_in_list_store_file (store, initial_file, &iter)) { + store->priv->initial_image = eom_list_store_get_pos_by_iter (store, &iter); + g_object_unref (initial_file); + } else { + store->priv->initial_image = 0; + } +} + +/** + * eom_list_store_remove_image: + * @store: An #EomListStore. + * @image: An #EomImage. + * + * Removes @image from @store. + **/ +void +eom_list_store_remove_image (EomListStore *store, EomImage *image) +{ + GtkTreeIter iter; + GFile *file; + + g_return_if_fail (EOM_IS_LIST_STORE (store)); + g_return_if_fail (EOM_IS_IMAGE (image)); + + file = eom_image_get_file (image); + + if (is_file_in_list_store_file (store, file, &iter)) { + eom_list_store_remove (store, &iter); + } + g_object_unref (file); +} + +/** + * eom_list_store_new_from_glist: + * @list: a %NULL-terminated list of #EomImage's. + * + * Creates a new #EomListStore from a list of #EomImage's. + * The given list must be %NULL-terminated. + * + * Returns: a new #EomListStore. + **/ +GtkListStore * +eom_list_store_new_from_glist (GList *list) +{ + GList *it; + + GtkListStore *store = eom_list_store_new (); + + for (it = list; it != NULL; it = it->next) { + eom_list_store_append_image (EOM_LIST_STORE (store), + EOM_IMAGE (it->data)); + } + + return store; +} + +/** + * eom_list_store_get_pos_by_image: + * @store: An #EomListStore. + * @image: An #EomImage. + * + * Gets the position where @image is stored in @store. If @image + * is not stored in @store, -1 is returned. + * + * Returns: the position of @image in @store or -1 if not found. + **/ +gint +eom_list_store_get_pos_by_image (EomListStore *store, EomImage *image) +{ + GtkTreeIter iter; + gint pos = -1; + GFile *file; + + g_return_val_if_fail (EOM_IS_LIST_STORE (store), -1); + g_return_val_if_fail (EOM_IS_IMAGE (image), -1); + + file = eom_image_get_file (image); + + if (is_file_in_list_store_file (store, file, &iter)) { + pos = eom_list_store_get_pos_by_iter (store, &iter); + } + + g_object_unref (file); + return pos; +} + +/** + * eom_list_store_get_image_by_pos: + * @store: An #EomListStore. + * @pos: the position of the required #EomImage. + * + * Gets the #EomImage in the position @pos of @store. If there is + * no image at position @pos, %NULL is returned. + * + * Returns: the #EomImage in position @pos or %NULL. + * + **/ +EomImage * +eom_list_store_get_image_by_pos (EomListStore *store, gint pos) +{ + EomImage *image = NULL; + GtkTreeIter iter; + + g_return_val_if_fail (EOM_IS_LIST_STORE (store), NULL); + + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, pos)) { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + } + + return image; +} + +/** + * eom_list_store_get_pos_by_iter: + * @store: An #EomListStore. + * @iter: A #GtkTreeIter pointing to an image in @store. + * + * Gets the position of the image pointed by @iter. + * + * Returns: The position of the image pointed by @iter. + **/ +gint +eom_list_store_get_pos_by_iter (EomListStore *store, + GtkTreeIter *iter) +{ + gint *indices; + GtkTreePath *path; + gint pos; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter); + indices = gtk_tree_path_get_indices (path); + pos = indices [0]; + gtk_tree_path_free (path); + + return pos; +} + +/** + * eom_list_store_length: + * @store: An #EomListStore. + * + * Returns the number of images in the store. + * + * Returns: The number of images in @store. + **/ +gint +eom_list_store_length (EomListStore *store) +{ + g_return_val_if_fail (EOM_IS_LIST_STORE (store), -1); + + return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); +} + +/** + * eom_list_store_get_initial_pos: + * @store: An #EomListStore. + * + * Gets the position of the #EomImage that should be loaded first. + * If not set, it returns -1. + * + * Returns: the position of the image to be loaded first or -1. + * + **/ +gint +eom_list_store_get_initial_pos (EomListStore *store) +{ + g_return_val_if_fail (EOM_IS_LIST_STORE (store), -1); + + return store->priv->initial_image; +} + +static void +eom_list_store_remove_thumbnail_job (EomListStore *store, + GtkTreeIter *iter) +{ + EomJob *job; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + EOM_LIST_STORE_EOM_JOB, &job, + -1); + + if (job != NULL) { + g_mutex_lock (store->priv->mutex); + eom_job_queue_remove_job (job); + gtk_list_store_set (GTK_LIST_STORE (store), iter, + EOM_LIST_STORE_EOM_JOB, NULL, + -1); + g_mutex_unlock (store->priv->mutex); + } + + +} + +static void +eom_list_store_add_thumbnail_job (EomListStore *store, GtkTreeIter *iter) +{ + EomImage *image; + EomJob *job; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + EOM_LIST_STORE_EOM_JOB, &job, + -1); + + if (job != NULL) { + g_object_unref (image); + return; + } + + job = eom_job_thumbnail_new (image); + + g_signal_connect (job, + "finished", + G_CALLBACK (eom_job_thumbnail_cb), + store); + + g_mutex_lock (store->priv->mutex); + gtk_list_store_set (GTK_LIST_STORE (store), iter, + EOM_LIST_STORE_EOM_JOB, job, + -1); + eom_job_queue_add_job (job); + g_mutex_unlock (store->priv->mutex); + g_object_unref (job); + g_object_unref (image); +} + +/** + * eom_list_store_thumbnail_set: + * @store: An #EomListStore. + * @iter: A #GtkTreeIter pointing to an image in @store. + * + * Sets the thumbnail for the image pointed by @iter. + * + **/ +void +eom_list_store_thumbnail_set (EomListStore *store, + GtkTreeIter *iter) +{ + gboolean thumb_set = FALSE; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + EOM_LIST_STORE_THUMB_SET, &thumb_set, + -1); + + if (thumb_set) { + return; + } + + eom_list_store_add_thumbnail_job (store, iter); +} + +/** + * eom_list_store_thumbnail_unset: + * @store: An #EomListStore. + * @iter: A #GtkTreeIter pointing to an image in @store. + * + * Unsets the thumbnail for the image pointed by @iter, changing + * it to a "busy" icon. + * + **/ +void +eom_list_store_thumbnail_unset (EomListStore *store, + GtkTreeIter *iter) +{ + EomImage *image; + + eom_list_store_remove_thumbnail_job (store, iter); + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + EOM_LIST_STORE_EOM_IMAGE, &image, + -1); + eom_image_set_thumbnail (image, NULL); + g_object_unref (image); + + gtk_list_store_set (GTK_LIST_STORE (store), iter, + EOM_LIST_STORE_THUMBNAIL, store->priv->busy_image, + EOM_LIST_STORE_THUMB_SET, FALSE, + -1); +} + +/** + * eom_list_store_thumbnail_refresh: + * @store: An #EomListStore. + * @iter: A #GtkTreeIter pointing to an image in @store. + * + * Refreshes the thumbnail for the image pointed by @iter. + * + **/ +void +eom_list_store_thumbnail_refresh (EomListStore *store, + GtkTreeIter *iter) +{ + eom_list_store_remove_thumbnail_job (store, iter); + eom_list_store_add_thumbnail_job (store, iter); +} diff --git a/src/eom-list-store.h b/src/eom-list-store.h new file mode 100644 index 0000000..f603e53 --- /dev/null +++ b/src/eom-list-store.h @@ -0,0 +1,113 @@ +/* Eye Of Mate - Image Store + * + * Copyright (C) 2006-2007 The Free Software Foundation + * + * Author: Claudio Saavedra <[email protected]> + * + * Based on code by: Jens Finke <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EOM_LIST_STORE_H +#define EOM_LIST_STORE_H + +#include <gtk/gtk.h> +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#ifndef __EOM_IMAGE_DECLR__ +#define __EOM_IMAGE_DECLR__ + typedef struct _EomImage EomImage; +#endif + +typedef struct _EomListStore EomListStore; +typedef struct _EomListStoreClass EomListStoreClass; +typedef struct _EomListStorePrivate EomListStorePrivate; + +#define EOM_TYPE_LIST_STORE eom_list_store_get_type() +#define EOM_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_LIST_STORE, EomListStore)) +#define EOM_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_LIST_STORE, EomListStoreClass)) +#define EOM_IS_LIST_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_LIST_STORE)) +#define EOM_IS_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_LIST_STORE)) +#define EOM_LIST_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_LIST_STORE, EomListStoreClass)) + +#define EOM_LIST_STORE_THUMB_SIZE 90 + +typedef enum { + EOM_LIST_STORE_THUMBNAIL = 0, + EOM_LIST_STORE_THUMB_SET, + EOM_LIST_STORE_EOM_IMAGE, + EOM_LIST_STORE_EOM_JOB, + EOM_LIST_STORE_NUM_COLUMNS +} EomListStoreColumn; + +struct _EomListStore { + GtkListStore parent; + EomListStorePrivate *priv; +}; + +struct _EomListStoreClass { + GtkListStoreClass parent_class; + + /* Padding for future expansion */ + void (* _eom_reserved1) (void); + void (* _eom_reserved2) (void); + void (* _eom_reserved3) (void); + void (* _eom_reserved4) (void); +}; + +GType eom_list_store_get_type (void) G_GNUC_CONST; + +GtkListStore *eom_list_store_new (void); + +GtkListStore *eom_list_store_new_from_glist (GList *list); + +void eom_list_store_append_image (EomListStore *store, + EomImage *image); + +void eom_list_store_add_files (EomListStore *store, + GList *file_list); + +void eom_list_store_remove_image (EomListStore *store, + EomImage *image); + +gint eom_list_store_get_pos_by_image (EomListStore *store, + EomImage *image); + +EomImage *eom_list_store_get_image_by_pos (EomListStore *store, + gint pos); + +gint eom_list_store_get_pos_by_iter (EomListStore *store, + GtkTreeIter *iter); + +gint eom_list_store_length (EomListStore *store); + +gint eom_list_store_get_initial_pos (EomListStore *store); + +void eom_list_store_thumbnail_set (EomListStore *store, + GtkTreeIter *iter); + +void eom_list_store_thumbnail_unset (EomListStore *store, + GtkTreeIter *iter); + +void eom_list_store_thumbnail_refresh (EomListStore *store, + GtkTreeIter *iter); + +G_END_DECLS + +#endif diff --git a/src/eom-marshal.list b/src/eom-marshal.list new file mode 100644 index 0000000..1a117a6 --- /dev/null +++ b/src/eom-marshal.list @@ -0,0 +1,2 @@ +VOID:INT,INT +VOID:DOUBLE diff --git a/src/eom-metadata-reader-jpg.c b/src/eom-metadata-reader-jpg.c new file mode 100644 index 0000000..788d2d3 --- /dev/null +++ b/src/eom-metadata-reader-jpg.c @@ -0,0 +1,673 @@ +/* Eye Of MATE -- JPEG Metadata Reader + * + * Copyright (C) 2008 The Free Software Foundation + * + * Author: Felix Riemann <[email protected]> + * + * Based on the original EomMetadataReader code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "eom-metadata-reader.h" +#include "eom-metadata-reader-jpg.h" +#include "eom-debug.h" + +typedef enum { + EMR_READ = 0, + EMR_READ_SIZE_HIGH_BYTE, + EMR_READ_SIZE_LOW_BYTE, + EMR_READ_MARKER, + EMR_SKIP_BYTES, + EMR_READ_APP1, + EMR_READ_EXIF, + EMR_READ_XMP, + EMR_READ_ICC, + EMR_READ_IPTC, + EMR_FINISHED +} EomMetadataReaderState; + +typedef enum { + EJA_EXIF = 0, + EJA_XMP, + EJA_OTHER +} EomJpegApp1Type; + + +#define EOM_JPEG_MARKER_START 0xFF +#define EOM_JPEG_MARKER_SOI 0xD8 +#define EOM_JPEG_MARKER_APP1 0xE1 +#define EOM_JPEG_MARKER_APP2 0xE2 +#define EOM_JPEG_MARKER_APP14 0xED + +#define IS_FINISHED(priv) (priv->state == EMR_READ && \ + priv->exif_chunk != NULL && \ + priv->icc_chunk != NULL && \ + priv->iptc_chunk != NULL && \ + priv->xmp_chunk != NULL) + +struct _EomMetadataReaderJpgPrivate { + EomMetadataReaderState state; + + /* data fields */ + guint exif_len; + gpointer exif_chunk; + + gpointer iptc_chunk; + guint iptc_len; + + guint icc_len; + gpointer icc_chunk; + + gpointer xmp_chunk; + guint xmp_len; + + /* management fields */ + int size; + int last_marker; + int bytes_read; +}; + +#define EOM_METADATA_READER_JPG_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_METADATA_READER_JPG, EomMetadataReaderJpgPrivate)) + +static void +eom_metadata_reader_jpg_init_emr_iface (gpointer g_iface, gpointer iface_data); + + +G_DEFINE_TYPE_WITH_CODE (EomMetadataReaderJpg, eom_metadata_reader_jpg, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (EOM_TYPE_METADATA_READER, + eom_metadata_reader_jpg_init_emr_iface)) + + +static void +eom_metadata_reader_jpg_dispose (GObject *object) +{ + EomMetadataReaderJpg *emr = EOM_METADATA_READER_JPG (object); + + if (emr->priv->exif_chunk != NULL) { + g_free (emr->priv->exif_chunk); + emr->priv->exif_chunk = NULL; + } + + if (emr->priv->iptc_chunk != NULL) { + g_free (emr->priv->iptc_chunk); + emr->priv->iptc_chunk = NULL; + } + + if (emr->priv->xmp_chunk != NULL) { + g_free (emr->priv->xmp_chunk); + emr->priv->xmp_chunk = NULL; + } + + if (emr->priv->icc_chunk != NULL) { + g_free (emr->priv->icc_chunk); + emr->priv->icc_chunk = NULL; + } + + G_OBJECT_CLASS (eom_metadata_reader_jpg_parent_class)->dispose (object); +} + +static void +eom_metadata_reader_jpg_init (EomMetadataReaderJpg *obj) +{ + EomMetadataReaderJpgPrivate *priv; + + priv = obj->priv = EOM_METADATA_READER_JPG_GET_PRIVATE (obj); + priv->exif_chunk = NULL; + priv->exif_len = 0; + priv->iptc_chunk = NULL; + priv->iptc_len = 0; + priv->icc_chunk = NULL; + priv->icc_len = 0; +} + +static void +eom_metadata_reader_jpg_class_init (EomMetadataReaderJpgClass *klass) +{ + GObjectClass *object_class = (GObjectClass*) klass; + + object_class->dispose = eom_metadata_reader_jpg_dispose; + + g_type_class_add_private (klass, sizeof (EomMetadataReaderJpgPrivate)); +} + +static gboolean +eom_metadata_reader_jpg_finished (EomMetadataReaderJpg *emr) +{ + g_return_val_if_fail (EOM_IS_METADATA_READER_JPG (emr), TRUE); + + return (emr->priv->state == EMR_FINISHED); +} + + +static EomJpegApp1Type +eom_metadata_identify_app1 (gchar *buf, guint len) +{ + if (len < 5) { + return EJA_OTHER; + } + + if (len < 29) { + return (strncmp ("Exif", buf, 5) == 0 ? EJA_EXIF : EJA_OTHER); + } + + if (strncmp ("Exif", buf, 5) == 0) { + return EJA_EXIF; + } else if (strncmp ("http://ns.adobe.com/xap/1.0/", buf, 29) == 0) { + return EJA_XMP; + } + + return EJA_OTHER; +} + +static void +eom_metadata_reader_get_next_block (EomMetadataReaderJpgPrivate* priv, + guchar *chunk, + int* i, + const guchar *buf, + int len, + EomMetadataReaderState state) +{ + if (*i + priv->size < len) { + /* read data in one block */ + memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[*i], priv->size); + priv->state = EMR_READ; + *i = *i + priv->size - 1; /* the for-loop consumes the other byte */ + } else { + int chunk_len = len - *i; + memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[*i], chunk_len); + priv->bytes_read += chunk_len; /* bytes already read */ + priv->size = (*i + priv->size) - len; /* remaining data to read */ + *i = len - 1; + priv->state = state; + } +} + +static void +eom_metadata_reader_jpg_consume (EomMetadataReaderJpg *emr, const guchar *buf, guint len) +{ + EomMetadataReaderJpgPrivate *priv; + EomJpegApp1Type app1_type; + int i; + EomMetadataReaderState next_state = EMR_READ; + guchar *chunk = NULL; + + g_return_if_fail (EOM_IS_METADATA_READER_JPG (emr)); + + priv = emr->priv; + + if (priv->state == EMR_FINISHED) return; + + for (i = 0; (i < len) && (priv->state != EMR_FINISHED); i++) { + + switch (priv->state) { + case EMR_READ: + if (buf[i] == EOM_JPEG_MARKER_START) { + priv->state = EMR_READ_MARKER; + } + else { + priv->state = EMR_FINISHED; + } + break; + + case EMR_READ_MARKER: + if ((buf [i] & 0xF0) == 0xE0 || buf[i] == 0xFE) { + /* we are reading some sort of APPxx or COM marker */ + /* these are always followed by 2 bytes of size information */ + priv->last_marker = buf [i]; + priv->size = 0; + priv->state = EMR_READ_SIZE_HIGH_BYTE; + + eom_debug_message (DEBUG_IMAGE_DATA, "APPx or COM Marker Found: %x", priv->last_marker); + } + else { + /* otherwise simply consume the byte */ + priv->state = EMR_READ; + } + break; + + case EMR_READ_SIZE_HIGH_BYTE: + priv->size = (buf [i] & 0xff) << 8; + priv->state = EMR_READ_SIZE_LOW_BYTE; + break; + + case EMR_READ_SIZE_LOW_BYTE: + priv->size |= (buf [i] & 0xff); + + if (priv->size > 2) /* ignore the two size-bytes */ + priv->size -= 2; + + if (priv->size == 0) { + priv->state = EMR_READ; + } else if (priv->last_marker == EOM_JPEG_MARKER_APP1 && + ((priv->exif_chunk == NULL) || (priv->xmp_chunk == NULL))) + { + priv->state = EMR_READ_APP1; + } else if (priv->last_marker == EOM_JPEG_MARKER_APP2 && + priv->icc_chunk == NULL && priv->size > 14) + { + /* Chunk has 14 bytes identification data */ + priv->state = EMR_READ_ICC; + } else if (priv->last_marker == EOM_JPEG_MARKER_APP14 && + priv->iptc_chunk == NULL) + { + priv->state = EMR_READ_IPTC; + } else { + priv->state = EMR_SKIP_BYTES; + } + + priv->last_marker = 0; + break; + + case EMR_SKIP_BYTES: + eom_debug_message (DEBUG_IMAGE_DATA, "Skip bytes: %i", priv->size); + + if (i + priv->size < len) { + i = i + priv->size - 1; /* the for-loop consumes the other byte */ + priv->size = 0; + } + else { + priv->size = (i + priv->size) - len; + i = len - 1; + } + if (priv->size == 0) { /* don't need to skip any more bytes */ + priv->state = EMR_READ; + } + break; + + case EMR_READ_APP1: + eom_debug_message (DEBUG_IMAGE_DATA, "Read APP1 data, Length: %i", priv->size); + + app1_type = eom_metadata_identify_app1 ((gchar*) &buf[i], priv->size); + + switch (app1_type) { + case EJA_EXIF: + if (priv->exif_chunk == NULL) { + priv->exif_chunk = g_new0 (guchar, priv->size); + priv->exif_len = priv->size; + priv->bytes_read = 0; + chunk = priv->exif_chunk; + next_state = EMR_READ_EXIF; + } else { + chunk = NULL; + priv->state = EMR_SKIP_BYTES; + } + break; + case EJA_XMP: + if (priv->xmp_chunk == NULL) { + priv->xmp_chunk = g_new0 (guchar, priv->size); + priv->xmp_len = priv->size; + priv->bytes_read = 0; + chunk = priv->xmp_chunk; + next_state = EMR_READ_XMP; + } else { + chunk = NULL; + priv->state = EMR_SKIP_BYTES; + } + break; + case EJA_OTHER: + default: + /* skip unknown data */ + chunk = NULL; + priv->state = EMR_SKIP_BYTES; + break; + } + + if (chunk) { + eom_metadata_reader_get_next_block (priv, chunk, + &i, buf, + len, + next_state); + } + + if (IS_FINISHED(priv)) + priv->state = EMR_FINISHED; + break; + + case EMR_READ_EXIF: + eom_debug_message (DEBUG_IMAGE_DATA, "Read continuation of EXIF data, length: %i", priv->size); + { + eom_metadata_reader_get_next_block (priv, priv->exif_chunk, + &i, buf, len, EMR_READ_EXIF); + } + if (IS_FINISHED(priv)) + priv->state = EMR_FINISHED; + break; + + case EMR_READ_XMP: + eom_debug_message (DEBUG_IMAGE_DATA, "Read continuation of XMP data, length: %i", priv->size); + { + eom_metadata_reader_get_next_block (priv, priv->xmp_chunk, + &i, buf, len, EMR_READ_XMP); + } + if (IS_FINISHED (priv)) + priv->state = EMR_FINISHED; + break; + + case EMR_READ_ICC: + eom_debug_message (DEBUG_IMAGE_DATA, + "Read continuation of ICC data, " + "length: %i", priv->size); + + if (priv->icc_chunk == NULL) { + priv->icc_chunk = g_new0 (guchar, priv->size); + priv->icc_len = priv->size; + priv->bytes_read = 0; + } + + eom_metadata_reader_get_next_block (priv, + priv->icc_chunk, + &i, buf, len, + EMR_READ_ICC); + + /* Test that the chunk actually contains ICC data. */ + if (priv->state == EMR_READ && priv->icc_chunk) { + const char* icc_chunk = priv->icc_chunk; + gboolean valid = TRUE; + + /* Chunk should begin with the + * ICC_PROFILE\0 identifier */ + valid &= strncmp (icc_chunk, + "ICC_PROFILE\0",12) == 0; + /* Make sure this is the first and only + * ICC chunk in the file as we don't + * support merging chunks yet. */ + valid &= *(guint16*)(icc_chunk+12) == 0x101; + + if (!valid) { + /* This no ICC data. Throw it away. */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Supposed ICC chunk didn't validate. " + "Ignoring."); + g_free (priv->icc_chunk); + priv->icc_chunk = NULL; + priv->icc_len = 0; + } + } + + if (IS_FINISHED(priv)) + priv->state = EMR_FINISHED; + break; + + case EMR_READ_IPTC: + eom_debug_message (DEBUG_IMAGE_DATA, + "Read continuation of IPTC data, " + "length: %i", priv->size); + + if (priv->iptc_chunk == NULL) { + priv->iptc_chunk = g_new0 (guchar, priv->size); + priv->iptc_len = priv->size; + priv->bytes_read = 0; + } + + eom_metadata_reader_get_next_block (priv, + priv->iptc_chunk, + &i, buf, len, + EMR_READ_IPTC); + + if (IS_FINISHED(priv)) + priv->state = EMR_FINISHED; + break; + + default: + g_assert_not_reached (); + } + } +} + +/* Returns the raw exif data. NOTE: The caller of this function becomes + * the new owner of this piece of memory and is responsible for freeing it! + */ +static void +eom_metadata_reader_jpg_get_exif_chunk (EomMetadataReaderJpg *emr, guchar **data, guint *len) +{ + EomMetadataReaderJpgPrivate *priv; + + g_return_if_fail (EOM_IS_METADATA_READER (emr)); + priv = emr->priv; + + *data = (guchar*) priv->exif_chunk; + *len = priv->exif_len; + + priv->exif_chunk = NULL; + priv->exif_len = 0; +} + +#ifdef HAVE_EXIF +static gpointer +eom_metadata_reader_jpg_get_exif_data (EomMetadataReaderJpg *emr) +{ + EomMetadataReaderJpgPrivate *priv; + ExifData *data = NULL; + + g_return_val_if_fail (EOM_IS_METADATA_READER (emr), NULL); + priv = emr->priv; + + if (priv->exif_chunk != NULL) { + data = exif_data_new_from_data (priv->exif_chunk, priv->exif_len); + } + + return data; +} +#endif + + +#ifdef HAVE_EXEMPI + +/* skip the signature */ +#define EOM_XMP_OFFSET (29) + +static gpointer +eom_metadata_reader_jpg_get_xmp_data (EomMetadataReaderJpg *emr ) +{ + EomMetadataReaderJpgPrivate *priv; + XmpPtr xmp = NULL; + + g_return_val_if_fail (EOM_IS_METADATA_READER (emr), NULL); + + priv = emr->priv; + + if (priv->xmp_chunk != NULL) { + xmp = xmp_new (priv->xmp_chunk+EOM_XMP_OFFSET, + priv->xmp_len-EOM_XMP_OFFSET); + } + + return (gpointer)xmp; +} +#endif + +/* + * FIXME: very broken, assumes the profile fits in a single chunk. Change to + * parse the sections and construct a single memory chunk, or maybe even parse + * the profile. + */ +#ifdef HAVE_LCMS +static gpointer +eom_metadata_reader_jpg_get_icc_profile (EomMetadataReaderJpg *emr) +{ + EomMetadataReaderJpgPrivate *priv; + cmsHPROFILE profile = NULL; + + g_return_val_if_fail (EOM_IS_METADATA_READER (emr), NULL); + + priv = emr->priv; + + if (priv->icc_chunk) { + cmsErrorAction (LCMS_ERROR_SHOW); + + profile = cmsOpenProfileFromMem(priv->icc_chunk + 14, priv->icc_len - 14); + + if (profile) { + eom_debug_message (DEBUG_LCMS, "JPEG has ICC profile"); + } else { + eom_debug_message (DEBUG_LCMS, "JPEG has invalid ICC profile"); + } + } + +#ifdef HAVE_EXIF + if (!profile && priv->exif_chunk != NULL) { + ExifEntry *entry; + ExifByteOrder o; + gint color_space; + ExifData *exif = eom_metadata_reader_jpg_get_exif_data (emr); + + if (!exif) return NULL; + + o = exif_data_get_byte_order (exif); + + entry = exif_data_get_entry (exif, EXIF_TAG_COLOR_SPACE); + + if (entry == NULL) { + exif_data_unref (exif); + return NULL; + } + + color_space = exif_get_short (entry->data, o); + + switch (color_space) { + case 1: + eom_debug_message (DEBUG_LCMS, "JPEG is sRGB"); + + profile = cmsCreate_sRGBProfile (); + + break; + case 2: + eom_debug_message (DEBUG_LCMS, "JPEG is Adobe RGB (Disabled)"); + + /* TODO: create Adobe RGB profile */ + //profile = cmsCreate_Adobe1998Profile (); + + break; + case 0xFFFF: + { + cmsCIExyY whitepoint; + cmsCIExyYTRIPLE primaries; + LPGAMMATABLE gamma[3]; + double gammaValue; + ExifRational r; + + const int offset = exif_format_get_size (EXIF_FORMAT_RATIONAL); + + entry = exif_data_get_entry (exif, EXIF_TAG_WHITE_POINT); + + if (entry && entry->components == 2) { + r = exif_get_rational (entry->data, o); + whitepoint.x = (double) r.numerator / r.denominator; + + r = exif_get_rational (entry->data + offset, o); + whitepoint.y = (double) r.numerator / r.denominator; + whitepoint.Y = 1.0; + } else { + eom_debug_message (DEBUG_LCMS, "No whitepoint found"); + break; + } + + entry = exif_data_get_entry (exif, EXIF_TAG_PRIMARY_CHROMATICITIES); + + if (entry && entry->components == 6) { + r = exif_get_rational (entry->data + 0 * offset, o); + primaries.Red.x = (double) r.numerator / r.denominator; + + r = exif_get_rational (entry->data + 1 * offset, o); + primaries.Red.y = (double) r.numerator / r.denominator; + + r = exif_get_rational (entry->data + 2 * offset, o); + primaries.Green.x = (double) r.numerator / r.denominator; + + r = exif_get_rational (entry->data + 3 * offset, o); + primaries.Green.y = (double) r.numerator / r.denominator; + + r = exif_get_rational (entry->data + 4 * offset, o); + primaries.Blue.x = (double) r.numerator / r.denominator; + + r = exif_get_rational (entry->data + 5 * offset, o); + primaries.Blue.y = (double) r.numerator / r.denominator; + + primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0; + } else { + eom_debug_message (DEBUG_LCMS, "No primary chromaticities found"); + break; + } + + entry = exif_data_get_entry (exif, EXIF_TAG_GAMMA); + + if (entry) { + r = exif_get_rational (entry->data, o); + gammaValue = (double) r.numerator / r.denominator; + } else { + eom_debug_message (DEBUG_LCMS, "No gamma found"); + gammaValue = 2.2; + } + + gamma[0] = gamma[1] = gamma[2] = cmsBuildGamma (256, gammaValue); + + profile = cmsCreateRGBProfile (&whitepoint, &primaries, gamma); + + cmsFreeGamma(gamma[0]); + + eom_debug_message (DEBUG_LCMS, "JPEG is calibrated"); + + break; + } + } + + exif_data_unref (exif); + } +#endif + return profile; +} +#endif + +static void +eom_metadata_reader_jpg_init_emr_iface (gpointer g_iface, gpointer iface_data) +{ + EomMetadataReaderInterface *iface; + + iface = (EomMetadataReaderInterface*)g_iface; + + iface->consume = + (void (*) (EomMetadataReader *self, const guchar *buf, guint len)) + eom_metadata_reader_jpg_consume; + iface->finished = + (gboolean (*) (EomMetadataReader *self)) + eom_metadata_reader_jpg_finished; + iface->get_raw_exif = + (void (*) (EomMetadataReader *self, guchar **data, guint *len)) + eom_metadata_reader_jpg_get_exif_chunk; +#ifdef HAVE_EXIF + iface->get_exif_data = + (gpointer (*) (EomMetadataReader *self)) + eom_metadata_reader_jpg_get_exif_data; +#endif +#ifdef HAVE_LCMS + iface->get_icc_profile = + (gpointer (*) (EomMetadataReader *self)) + eom_metadata_reader_jpg_get_icc_profile; +#endif +#ifdef HAVE_EXEMPI + iface->get_xmp_ptr = + (gpointer (*) (EomMetadataReader *self)) + eom_metadata_reader_jpg_get_xmp_data; +#endif +} + diff --git a/src/eom-metadata-reader-jpg.h b/src/eom-metadata-reader-jpg.h new file mode 100644 index 0000000..722a720 --- /dev/null +++ b/src/eom-metadata-reader-jpg.h @@ -0,0 +1,55 @@ +/* Eye Of MATE -- JPEG Metadata Reader + * + * Copyright (C) 2008 The Free Software Foundation + * + * Author: Felix Riemann <[email protected]> + * + * Based on the original EomMetadataReader code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _EOM_METADATA_READER_JPG_H_ +#define _EOM_METADATA_READER_JPG_H_ + +G_BEGIN_DECLS + +#define EOM_TYPE_METADATA_READER_JPG (eom_metadata_reader_jpg_get_type ()) +#define EOM_METADATA_READER_JPG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o),EOM_TYPE_METADATA_READER_JPG, EomMetadataReaderJpg)) +#define EOM_METADATA_READER_JPG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EOM_TYPE_METADATA_READER_JPG, EomMetadataReaderJpgClass)) +#define EOM_IS_METADATA_READER_JPG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_METADATA_READER_JPG)) +#define EOM_IS_METADATA_READER_JPG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EOM_TYPE_METADATA_READER_JPG)) +#define EOM_METADATA_READER_JPG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EOM_TYPE_METADATA_READER_JPG, EomMetadataReaderJpgClass)) + +typedef struct _EomMetadataReaderJpg EomMetadataReaderJpg; +typedef struct _EomMetadataReaderJpgClass EomMetadataReaderJpgClass; +typedef struct _EomMetadataReaderJpgPrivate EomMetadataReaderJpgPrivate; + +struct _EomMetadataReaderJpg { + GObject parent; + + EomMetadataReaderJpgPrivate *priv; +}; + +struct _EomMetadataReaderJpgClass { + GObjectClass parent_klass; +}; + +G_GNUC_INTERNAL +GType eom_metadata_reader_jpg_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* _EOM_METADATA_READER_JPG_H_ */ diff --git a/src/eom-metadata-reader-png.c b/src/eom-metadata-reader-png.c new file mode 100644 index 0000000..2092f62 --- /dev/null +++ b/src/eom-metadata-reader-png.c @@ -0,0 +1,648 @@ +/* Eye Of MATE -- PNG Metadata Reader + * + * Copyright (C) 2008 The Free Software Foundation + * + * Author: Felix Riemann <[email protected]> + * + * Based on the old EomMetadataReader code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <zlib.h> + +#include "eom-metadata-reader.h" +#include "eom-metadata-reader-png.h" +#include "eom-debug.h" + +typedef enum { + EMR_READ_MAGIC, + EMR_READ_SIZE_HIGH_HIGH_BYTE, + EMR_READ_SIZE_HIGH_LOW_BYTE, + EMR_READ_SIZE_LOW_HIGH_BYTE, + EMR_READ_SIZE_LOW_LOW_BYTE, + EMR_READ_CHUNK_NAME, + EMR_SKIP_BYTES, + EMR_CHECK_CRC, + EMR_SKIP_CRC, + EMR_READ_XMP_ITXT, + EMR_READ_ICCP, + EMR_READ_SRGB, + EMR_READ_CHRM, + EMR_READ_GAMA, + EMR_FINISHED +} EomMetadataReaderPngState; + +#if 0 +#define IS_FINISHED(priv) (priv->icc_chunk != NULL && \ + priv->xmp_chunk != NULL) +#endif + +struct _EomMetadataReaderPngPrivate { + EomMetadataReaderPngState state; + + /* data fields */ + guint32 icc_len; + gpointer icc_chunk; + + gpointer xmp_chunk; + guint32 xmp_len; + + guint32 sRGB_len; + gpointer sRGB_chunk; + + gpointer cHRM_chunk; + guint32 cHRM_len; + + guint32 gAMA_len; + gpointer gAMA_chunk; + + /* management fields */ + gsize size; + gsize bytes_read; + guint sub_step; + guchar chunk_name[4]; + gpointer *crc_chunk; + guint32 *crc_len; + guint32 target_crc; + gboolean hasIHDR; +}; + +#define EOM_METADATA_READER_PNG_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_METADATA_READER_PNG, EomMetadataReaderPngPrivate)) + +static void +eom_metadata_reader_png_init_emr_iface (gpointer g_iface, gpointer iface_data); + +G_DEFINE_TYPE_WITH_CODE (EomMetadataReaderPng, eom_metadata_reader_png, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (EOM_TYPE_METADATA_READER, + eom_metadata_reader_png_init_emr_iface)) + +static void +eom_metadata_reader_png_dispose (GObject *object) +{ + EomMetadataReaderPng *emr = EOM_METADATA_READER_PNG (object); + EomMetadataReaderPngPrivate *priv = emr->priv; + + g_free (priv->xmp_chunk); + priv->xmp_chunk = NULL; + + g_free (priv->icc_chunk); + priv->icc_chunk = NULL; + + g_free (priv->sRGB_chunk); + priv->sRGB_chunk = NULL; + + g_free (priv->cHRM_chunk); + priv->cHRM_chunk = NULL; + + g_free (priv->gAMA_chunk); + priv->gAMA_chunk = NULL; + + G_OBJECT_CLASS (eom_metadata_reader_png_parent_class)->dispose (object); +} + +static void +eom_metadata_reader_png_init (EomMetadataReaderPng *obj) +{ + EomMetadataReaderPngPrivate *priv; + + priv = obj->priv = EOM_METADATA_READER_PNG_GET_PRIVATE (obj); + priv->icc_chunk = NULL; + priv->icc_len = 0; + priv->xmp_chunk = NULL; + priv->xmp_len = 0; + priv->sRGB_chunk = NULL; + priv->sRGB_len = 0; + priv->cHRM_chunk = NULL; + priv->cHRM_len = 0; + priv->gAMA_chunk = NULL; + priv->gAMA_len = 0; + + priv->sub_step = 0; + priv->state = EMR_READ_MAGIC; + priv->hasIHDR = FALSE; +} + +static void +eom_metadata_reader_png_class_init (EomMetadataReaderPngClass *klass) +{ + GObjectClass *object_class = (GObjectClass*) klass; + + object_class->dispose = eom_metadata_reader_png_dispose; + + g_type_class_add_private (klass, sizeof (EomMetadataReaderPngPrivate)); +} + +static gboolean +eom_metadata_reader_png_finished (EomMetadataReaderPng *emr) +{ + g_return_val_if_fail (EOM_IS_METADATA_READER_PNG (emr), TRUE); + + return (emr->priv->state == EMR_FINISHED); +} + + +static void +eom_metadata_reader_png_get_next_block (EomMetadataReaderPngPrivate* priv, + guchar *chunk, + int* i, + const guchar *buf, + int len, + EomMetadataReaderPngState state) +{ + if (*i + priv->size < len) { + /* read data in one block */ + memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[*i], priv->size); + priv->state = EMR_CHECK_CRC; + *i = *i + priv->size - 1; /* the for-loop consumes the other byte */ + priv->size = 0; + } else { + int chunk_len = len - *i; + memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[*i], chunk_len); + priv->bytes_read += chunk_len; /* bytes already read */ + priv->size = (*i + priv->size) - len; /* remaining data to read */ + *i = len - 1; + priv->state = state; + } +} + +static void +eom_metadata_reader_png_consume (EomMetadataReaderPng *emr, const guchar *buf, guint len) +{ + EomMetadataReaderPngPrivate *priv; + int i; + guint32 chunk_crc; + static const gchar PNGMAGIC[8] = "\x89PNG\x0D\x0A\x1a\x0A"; + + g_return_if_fail (EOM_IS_METADATA_READER_PNG (emr)); + + priv = emr->priv; + + if (priv->state == EMR_FINISHED) return; + + for (i = 0; (i < len) && (priv->state != EMR_FINISHED); i++) { + + switch (priv->state) { + case EMR_READ_MAGIC: + /* Check PNG magic string */ + if (priv->sub_step < 8 && + (gchar)buf[i] == PNGMAGIC[priv->sub_step]) { + if (priv->sub_step == 7) + priv->state = EMR_READ_SIZE_HIGH_HIGH_BYTE; + priv->sub_step++; + } else { + priv->state = EMR_FINISHED; + } + break; + case EMR_READ_SIZE_HIGH_HIGH_BYTE: + /* Read the high byte of the size's high word */ + priv->size |= (buf[i] & 0xFF) << 24; + priv->state = EMR_READ_SIZE_HIGH_LOW_BYTE; + break; + case EMR_READ_SIZE_HIGH_LOW_BYTE: + /* Read the low byte of the size's high word */ + priv->size |= (buf[i] & 0xFF) << 16; + priv->state = EMR_READ_SIZE_LOW_HIGH_BYTE; + break; + case EMR_READ_SIZE_LOW_HIGH_BYTE: + /* Read the high byte of the size's low word */ + priv->size |= (buf [i] & 0xff) << 8; + priv->state = EMR_READ_SIZE_LOW_LOW_BYTE; + break; + case EMR_READ_SIZE_LOW_LOW_BYTE: + /* Read the high byte of the size's low word */ + priv->size |= (buf [i] & 0xff); + /* The maximum chunk length is 2^31-1 */ + if (G_LIKELY (priv->size <= (guint32) 0x7fffffff)) { + priv->state = EMR_READ_CHUNK_NAME; + /* Make sure sub_step is 0 before next step */ + priv->sub_step = 0; + } else { + priv->state = EMR_FINISHED; + eom_debug_message (DEBUG_IMAGE_DATA, + "chunk size larger than " + "2^31-1; stopping parser"); + } + + break; + case EMR_READ_CHUNK_NAME: + /* Read the 4-byte chunk name */ + if (priv->sub_step > 3) + g_assert_not_reached (); + + priv->chunk_name[priv->sub_step] = buf[i]; + + if (priv->sub_step++ != 3) + break; + + if (G_UNLIKELY (!priv->hasIHDR)) { + /* IHDR should be the first chunk in a PNG */ + if (priv->size == 13 + && memcmp (priv->chunk_name, "IHDR", 4) == 0){ + priv->hasIHDR = TRUE; + } else { + /* Stop parsing if it is not */ + priv->state = EMR_FINISHED; + } + } + + /* Try to identify the chunk by its name. + * Already do some sanity checks where possible */ + if (memcmp (priv->chunk_name, "iTXt", 4) == 0 && + priv->size > (22 + 54) && priv->xmp_chunk == NULL) { + priv->state = EMR_READ_XMP_ITXT; + } else if (memcmp (priv->chunk_name, "iCCP", 4) == 0 && + priv->icc_chunk == NULL) { + priv->state = EMR_READ_ICCP; + } else if (memcmp (priv->chunk_name, "sRGB", 4) == 0 && + priv->sRGB_chunk == NULL && priv->size == 1) { + priv->state = EMR_READ_SRGB; + } else if (memcmp (priv->chunk_name, "cHRM", 4) == 0 && + priv->cHRM_chunk == NULL && priv->size == 32) { + priv->state = EMR_READ_CHRM; + } else if (memcmp (priv->chunk_name, "gAMA", 4) == 0 && + priv->gAMA_chunk == NULL && priv->size == 4) { + priv->state = EMR_READ_GAMA; + } else if (memcmp (priv->chunk_name, "IEND", 4) == 0) { + priv->state = EMR_FINISHED; + } else { + /* Skip chunk + 4-byte CRC32 value */ + priv->size += 4; + priv->state = EMR_SKIP_BYTES; + } + priv->sub_step = 0; + break; + case EMR_SKIP_CRC: + /* Skip the 4-byte CRC32 value following every chunk */ + priv->size = 4; + case EMR_SKIP_BYTES: + /* Skip chunk and start reading the size of the next one */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Skip bytes: %" G_GSIZE_FORMAT, + priv->size); + + if (i + priv->size < len) { + i = i + priv->size - 1; /* the for-loop consumes the other byte */ + priv->size = 0; + priv->state = EMR_READ_SIZE_HIGH_HIGH_BYTE; + } + else { + priv->size = (i + priv->size) - len; + i = len - 1; + } + break; + case EMR_CHECK_CRC: + /* Read the chunks CRC32 value from the file,... */ + if (priv->sub_step == 0) + priv->target_crc = 0; + + priv->target_crc |= buf[i] << ((3 - priv->sub_step) * 8); + + if (priv->sub_step++ != 3) + break; + + /* ...generate the chunks CRC32,... */ + chunk_crc = crc32 (crc32 (0L, Z_NULL, 0), priv->chunk_name, 4); + chunk_crc = crc32 (chunk_crc, *priv->crc_chunk, *priv->crc_len); + + eom_debug_message (DEBUG_IMAGE_DATA, "Checking CRC: Chunk: 0x%X - Target: 0x%X", chunk_crc, priv->target_crc); + + /* ...and check if they match. If they don't throw + * the chunk away and stop parsing. */ + if (priv->target_crc == chunk_crc) { + priv->state = EMR_READ_SIZE_HIGH_HIGH_BYTE; + } else { + g_free (*priv->crc_chunk); + *priv->crc_chunk = NULL; + *priv->crc_len = 0; + /* Stop parsing for security reasons */ + priv->state = EMR_FINISHED; + } + priv->sub_step = 0; + break; + case EMR_READ_XMP_ITXT: + /* Extract an iTXt chunk possibly containing + * an XMP packet */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Read XMP Chunk - size: %" + G_GSIZE_FORMAT, priv->size); + + if (priv->xmp_chunk == NULL) { + priv->xmp_chunk = g_new0 (guchar, priv->size); + priv->xmp_len = priv->size; + priv->crc_len = &priv->xmp_len; + priv->bytes_read = 0; + priv->crc_chunk = &priv->xmp_chunk; + } + eom_metadata_reader_png_get_next_block (priv, + priv->xmp_chunk, + &i, buf, len, + EMR_READ_XMP_ITXT); + + if (priv->state == EMR_CHECK_CRC) { + /* Check if it is actually an XMP chunk. + * Throw it away if not. + * The check has 4 extra \0's to check + * if the chunk is configured correctly. */ + if (memcmp (priv->xmp_chunk, "XML:com.adobe.xmp\0\0\0\0\0", 22) != 0) { + priv->state = EMR_SKIP_CRC; + g_free (priv->xmp_chunk); + priv->xmp_chunk = NULL; + priv->xmp_len = 0; + } + } + break; + case EMR_READ_ICCP: + /* Extract an iCCP chunk containing a + * deflated ICC profile. */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Read ICC Chunk - size: %" + G_GSIZE_FORMAT, priv->size); + + if (priv->icc_chunk == NULL) { + priv->icc_chunk = g_new0 (guchar, priv->size); + priv->icc_len = priv->size; + priv->crc_len = &priv->icc_len; + priv->bytes_read = 0; + priv->crc_chunk = &priv->icc_chunk; + } + + eom_metadata_reader_png_get_next_block (priv, + priv->icc_chunk, + &i, buf, len, + EMR_READ_ICCP); + break; + case EMR_READ_SRGB: + /* Extract the sRGB chunk. Marks the image data as + * being in sRGB colorspace. */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Read sRGB Chunk - value: %u", *(buf+i)); + + if (priv->sRGB_chunk == NULL) { + priv->sRGB_chunk = g_new0 (guchar, priv->size); + priv->sRGB_len = priv->size; + priv->crc_len = &priv->sRGB_len; + priv->bytes_read = 0; + priv->crc_chunk = &priv->sRGB_chunk; + } + + eom_metadata_reader_png_get_next_block (priv, + priv->sRGB_chunk, + &i, buf, len, + EMR_READ_SRGB); + break; + case EMR_READ_CHRM: + /* Extract the cHRM chunk. Contains the coordinates of + * the image's whitepoint and primary chromacities. */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Read cHRM Chunk - size: %" + G_GSIZE_FORMAT, priv->size); + + if (priv->cHRM_chunk == NULL) { + priv->cHRM_chunk = g_new0 (guchar, priv->size); + priv->cHRM_len = priv->size; + priv->crc_len = &priv->cHRM_len; + priv->bytes_read = 0; + priv->crc_chunk = &priv->cHRM_chunk; + } + + eom_metadata_reader_png_get_next_block (priv, + priv->cHRM_chunk, + &i, buf, len, + EMR_READ_ICCP); + break; + case EMR_READ_GAMA: + /* Extract the gAMA chunk containing the + * image's gamma value */ + eom_debug_message (DEBUG_IMAGE_DATA, + "Read gAMA-Chunk - size: %" + G_GSIZE_FORMAT, priv->size); + + if (priv->gAMA_chunk == NULL) { + priv->gAMA_chunk = g_new0 (guchar, priv->size); + priv->gAMA_len = priv->size; + priv->crc_len = &priv->gAMA_len; + priv->bytes_read = 0; + priv->crc_chunk = &priv->gAMA_chunk; + } + + eom_metadata_reader_png_get_next_block (priv, + priv->gAMA_chunk, + &i, buf, len, + EMR_READ_ICCP); + break; + default: + g_assert_not_reached (); + } + } +} + +#ifdef HAVE_EXEMPI + +/* skip the chunk ID */ +#define EOM_XMP_OFFSET (22) + +static gpointer +eom_metadata_reader_png_get_xmp_data (EomMetadataReaderPng *emr ) +{ + EomMetadataReaderPngPrivate *priv; + XmpPtr xmp = NULL; + + g_return_val_if_fail (EOM_IS_METADATA_READER_PNG (emr), NULL); + + priv = emr->priv; + + if (priv->xmp_chunk != NULL) { + xmp = xmp_new (priv->xmp_chunk+EOM_XMP_OFFSET, + priv->xmp_len-EOM_XMP_OFFSET); + } + + return (gpointer) xmp; +} +#endif + +#ifdef HAVE_LCMS + +#define EXTRACT_DOUBLE_UINT_BLOCK_OFFSET(chunk,offset,divider) \ + (double)(GUINT32_FROM_BE(*((guint32*)((chunk)+((offset)*4))))/(double)(divider)) + +/* This is the amount of memory the inflate output buffer gets increased by + * while decompressing the ICC profile */ +#define EOM_ICC_INFLATE_BUFFER_STEP 1024 + +/* I haven't seen ICC profiles larger than 1MB yet. + * A maximum output buffer of 5MB should be enough. */ +#define EOM_ICC_INFLATE_BUFFER_LIMIT (1024*1024*5) + +static gpointer +eom_metadata_reader_png_get_icc_profile (EomMetadataReaderPng *emr) +{ + EomMetadataReaderPngPrivate *priv; + cmsHPROFILE profile = NULL; + + g_return_val_if_fail (EOM_IS_METADATA_READER_PNG (emr), NULL); + + priv = emr->priv; + + if (priv->icc_chunk) { + gpointer outbuf; + gsize offset = 0; + z_stream zstr; + int z_ret; + + /* Use default allocation functions */ + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + + /* Skip the name of the ICC profile */ + while (*((guchar*)priv->icc_chunk+offset) != '\0') + offset++; + /* Ensure the compression method (deflate) */ + if (*((guchar*)priv->icc_chunk+(++offset)) != '\0') + return NULL; + ++offset; //offset now points to the start of the deflated data + + /* Prepare the zlib data structure for decompression */ + zstr.next_in = priv->icc_chunk + offset; + zstr.avail_in = priv->icc_len - offset; + if (inflateInit (&zstr) != Z_OK) { + return NULL; + } + + /* Prepare output buffer and make zlib aware of it */ + outbuf = g_malloc (EOM_ICC_INFLATE_BUFFER_STEP); + zstr.next_out = outbuf; + zstr.avail_out = EOM_ICC_INFLATE_BUFFER_STEP; + + do { + if (zstr.avail_out == 0) { + /* The output buffer was not large enough to + * hold all the decompressed data. Increase its + * size and continue decompression. */ + gsize new_size = zstr.total_out + EOM_ICC_INFLATE_BUFFER_STEP; + + if (G_UNLIKELY (new_size > EOM_ICC_INFLATE_BUFFER_LIMIT)) { + /* Enforce a memory limit for the output + * buffer to avoid possible OOM cases */ + inflateEnd (&zstr); + g_free (outbuf); + eom_debug_message (DEBUG_IMAGE_DATA, "ICC profile is too large. Ignoring."); + return NULL; + } + outbuf = g_realloc(outbuf, new_size); + zstr.avail_out = EOM_ICC_INFLATE_BUFFER_STEP; + zstr.next_out = outbuf + zstr.total_out; + } + z_ret = inflate (&zstr, Z_SYNC_FLUSH); + } while (z_ret == Z_OK); + + if (G_UNLIKELY (z_ret != Z_STREAM_END)) { + eom_debug_message (DEBUG_IMAGE_DATA, "Error while inflating ICC profile: %s (%d)", zstr.msg, z_ret); + inflateEnd (&zstr); + g_free (outbuf); + return NULL; + } + + cmsErrorAction (LCMS_ERROR_SHOW); + + profile = cmsOpenProfileFromMem(outbuf, zstr.total_out); + inflateEnd (&zstr); + g_free (outbuf); + + eom_debug_message (DEBUG_LCMS, "PNG has %s ICC profile", profile ? "valid" : "invalid"); + } + + if (!profile && priv->sRGB_chunk) { + eom_debug_message (DEBUG_LCMS, "PNG is sRGB"); + /* If the file has an sRGB chunk the image data is in the sRGB + * colorspace. lcms has a built-in sRGB profile. */ + + profile = cmsCreate_sRGBProfile (); + } + + if (!profile && priv->cHRM_chunk) { + cmsCIExyY whitepoint; + cmsCIExyYTRIPLE primaries; + LPGAMMATABLE gamma[3]; + double gammaValue = 2.2; // 2.2 should be a sane default gamma + + /* This uglyness extracts the chromacity and whitepoint values + * from a PNG's cHRM chunk. These can be accurate up to the + * 5th decimal point. + * They are saved as integer values multiplied by 100000. */ + + eom_debug_message (DEBUG_LCMS, "Trying to calculate color profile"); + + whitepoint.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 0, 100000); + whitepoint.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 1, 100000); + + primaries.Red.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 2, 100000); + primaries.Red.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 3, 100000); + primaries.Green.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 4, 100000); + primaries.Green.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 5, 100000); + primaries.Blue.x = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 6, 100000); + primaries.Blue.y = EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->cHRM_chunk, 7, 100000); + + primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0; + + /* If the gAMA_chunk is present use its value which is saved + * the same way as the whitepoint. Use 2.2 as default value if + * the chunk is not present. */ + if (priv->gAMA_chunk) + gammaValue = (double) 1.0/EXTRACT_DOUBLE_UINT_BLOCK_OFFSET (priv->gAMA_chunk, 0, 100000); + + gamma[0] = gamma[1] = gamma[2] = cmsBuildGamma (256, gammaValue); + + profile = cmsCreateRGBProfile (&whitepoint, &primaries, gamma); + + cmsFreeGamma(gamma[0]); + } + + return profile; +} +#endif + +static void +eom_metadata_reader_png_init_emr_iface (gpointer g_iface, gpointer iface_data) +{ + EomMetadataReaderInterface *iface; + + iface = (EomMetadataReaderInterface*) g_iface; + + iface->consume = + (void (*) (EomMetadataReader *self, const guchar *buf, guint len)) + eom_metadata_reader_png_consume; + iface->finished = + (gboolean (*) (EomMetadataReader *self)) + eom_metadata_reader_png_finished; +#ifdef HAVE_LCMS + iface->get_icc_profile = + (cmsHPROFILE (*) (EomMetadataReader *self)) + eom_metadata_reader_png_get_icc_profile; +#endif +#ifdef HAVE_EXEMPI + iface->get_xmp_ptr = + (gpointer (*) (EomMetadataReader *self)) + eom_metadata_reader_png_get_xmp_data; +#endif +} diff --git a/src/eom-metadata-reader-png.h b/src/eom-metadata-reader-png.h new file mode 100644 index 0000000..7dde41f --- /dev/null +++ b/src/eom-metadata-reader-png.h @@ -0,0 +1,55 @@ +/* Eye Of MATE -- PNG Metadata Reader + * + * Copyright (C) 2008 The Free Software Foundation + * + * Author: Felix Riemann <[email protected]> + * + * Based on the old EomMetadataReader code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _EOM_METADATA_READER_PNG_H_ +#define _EOM_METADATA_READER_PNG_H_ + +G_BEGIN_DECLS + +#define EOM_TYPE_METADATA_READER_PNG (eom_metadata_reader_png_get_type ()) +#define EOM_METADATA_READER_PNG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EOM_TYPE_METADATA_READER_PNG, EomMetadataReaderPng)) +#define EOM_METADATA_READER_PNG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EOM_TYPE_METADATA_READER_PNG, EomMetadataReaderPngClass)) +#define EOM_IS_METADATA_READER_PNG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_METADATA_READER_PNG)) +#define EOM_IS_METADATA_READER_PNG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EOM_TYPE_METADATA_READER_PNG)) +#define EOM_METADATA_READER_PNG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EOM_TYPE_METADATA_READER_PNG, EomMetadataReaderPngClass)) + +typedef struct _EomMetadataReaderPng EomMetadataReaderPng; +typedef struct _EomMetadataReaderPngClass EomMetadataReaderPngClass; +typedef struct _EomMetadataReaderPngPrivate EomMetadataReaderPngPrivate; + +struct _EomMetadataReaderPng { + GObject parent; + + EomMetadataReaderPngPrivate *priv; +}; + +struct _EomMetadataReaderPngClass { + GObjectClass parent_klass; +}; + +G_GNUC_INTERNAL +GType eom_metadata_reader_png_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* _EOM_METADATA_READER_PNG_H_ */ diff --git a/src/eom-metadata-reader.c b/src/eom-metadata-reader.c new file mode 100644 index 0000000..82955e8 --- /dev/null +++ b/src/eom-metadata-reader.c @@ -0,0 +1,150 @@ +/* Eye Of MATE -- Metadata Reader Interface + * + * Copyright (C) 2008 The Free Software Foundation + * + * Author: Felix Riemann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eom-metadata-reader.h" +#include "eom-metadata-reader-jpg.h" +#include "eom-metadata-reader-png.h" +#include "eom-debug.h" + + +GType +eom_metadata_reader_get_type (void) +{ + static GType reader_type = 0; + + if (G_UNLIKELY (reader_type == 0)) { + reader_type = g_type_register_static_simple (G_TYPE_INTERFACE, + "EomMetadataReader", + sizeof (EomMetadataReaderInterface), + NULL, 0, NULL, 0); + } + + return reader_type; +} + +EomMetadataReader* +eom_metadata_reader_new (EomMetadataFileType type) +{ + EomMetadataReader *emr; + + switch (type) { + case EOM_METADATA_JPEG: + emr = EOM_METADATA_READER (g_object_new (EOM_TYPE_METADATA_READER_JPG, NULL)); + break; + case EOM_METADATA_PNG: + emr = EOM_METADATA_READER (g_object_new (EOM_TYPE_METADATA_READER_PNG, NULL)); + break; + default: + emr = NULL; + break; + } + + return emr; +} + +gboolean +eom_metadata_reader_finished (EomMetadataReader *emr) +{ + g_return_val_if_fail (EOM_IS_METADATA_READER (emr), TRUE); + + return EOM_METADATA_READER_GET_INTERFACE (emr)->finished (emr); +} + + +void +eom_metadata_reader_consume (EomMetadataReader *emr, const guchar *buf, guint len) +{ + EOM_METADATA_READER_GET_INTERFACE (emr)->consume (emr, buf, len); +} + +/* Returns the raw exif data. NOTE: The caller of this function becomes + * the new owner of this piece of memory and is responsible for freeing it! + */ +void +eom_metadata_reader_get_exif_chunk (EomMetadataReader *emr, guchar **data, guint *len) +{ + EomMetadataReaderInterface *iface; + + g_return_if_fail (data != NULL && len != NULL); + iface = EOM_METADATA_READER_GET_INTERFACE (emr); + + if (iface->get_raw_exif) { + iface->get_raw_exif (emr, data, len); + } else { + g_return_if_fail (data != NULL && len != NULL); + + *data = NULL; + *len = 0; + } + +} + +#ifdef HAVE_EXIF +ExifData* +eom_metadata_reader_get_exif_data (EomMetadataReader *emr) +{ + gpointer exif_data = NULL; + EomMetadataReaderInterface *iface; + + iface = EOM_METADATA_READER_GET_INTERFACE (emr); + + if (iface->get_exif_data) + exif_data = iface->get_exif_data (emr); + + return exif_data; +} +#endif + +#ifdef HAVE_EXEMPI +XmpPtr +eom_metadata_reader_get_xmp_data (EomMetadataReader *emr ) +{ + gpointer xmp_data = NULL; + EomMetadataReaderInterface *iface; + + iface = EOM_METADATA_READER_GET_INTERFACE (emr); + + if (iface->get_xmp_ptr) + xmp_data = iface->get_xmp_ptr (emr); + + return xmp_data; +} +#endif + +#ifdef HAVE_LCMS +cmsHPROFILE +eom_metadata_reader_get_icc_profile (EomMetadataReader *emr) +{ + EomMetadataReaderInterface *iface; + gpointer profile = NULL; + + iface = EOM_METADATA_READER_GET_INTERFACE (emr); + + if (iface->get_icc_profile) + profile = iface->get_icc_profile (emr); + + return profile; +} +#endif diff --git a/src/eom-metadata-reader.h b/src/eom-metadata-reader.h new file mode 100644 index 0000000..ad8803d --- /dev/null +++ b/src/eom-metadata-reader.h @@ -0,0 +1,112 @@ +/* Eye Of MATE -- Metadata Reader Interface + * + * Copyright (C) 2008 The Free Software Foundation + * + * Author: Felix Riemann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _EOM_METADATA_READER_H_ +#define _EOM_METADATA_READER_H_ + +#include <glib-object.h> +#if HAVE_EXIF +#include <libexif/exif-data.h> +#endif +#if HAVE_EXEMPI +#include <exempi/xmp.h> +#endif +#if HAVE_LCMS +#include <lcms.h> +#endif + +G_BEGIN_DECLS + +#define EOM_TYPE_METADATA_READER (eom_metadata_reader_get_type ()) +#define EOM_METADATA_READER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EOM_TYPE_METADATA_READER, EomMetadataReader)) +#define EOM_IS_METADATA_READER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_METADATA_READER)) +#define EOM_METADATA_READER_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), EOM_TYPE_METADATA_READER, EomMetadataReaderInterface)) + +typedef struct _EomMetadataReader EomMetadataReader; +typedef struct _EomMetadataReaderInterface EomMetadataReaderInterface; + +struct _EomMetadataReaderInterface { + GTypeInterface parent; + + void (*consume) (EomMetadataReader *self, + const guchar *buf, + guint len); + + gboolean (*finished) (EomMetadataReader *self); + + void (*get_raw_exif) (EomMetadataReader *self, + guchar **data, + guint *len); + + gpointer (*get_exif_data) (EomMetadataReader *self); + + gpointer (*get_icc_profile) (EomMetadataReader *self); + + gpointer (*get_xmp_ptr) (EomMetadataReader *self); +}; + +typedef enum { + EOM_METADATA_JPEG, + EOM_METADATA_PNG +} EomMetadataFileType; + +G_GNUC_INTERNAL +GType eom_metadata_reader_get_type (void) G_GNUC_CONST; + +G_GNUC_INTERNAL +EomMetadataReader* eom_metadata_reader_new (EomMetadataFileType type); + +G_GNUC_INTERNAL +void eom_metadata_reader_consume (EomMetadataReader *emr, + const guchar *buf, + guint len); + +G_GNUC_INTERNAL +gboolean eom_metadata_reader_finished (EomMetadataReader *emr); + +G_GNUC_INTERNAL +void eom_metadata_reader_get_exif_chunk (EomMetadataReader *emr, + guchar **data, + guint *len); + +#ifdef HAVE_EXIF +G_GNUC_INTERNAL +ExifData* eom_metadata_reader_get_exif_data (EomMetadataReader *emr); +#endif + +#ifdef HAVE_EXEMPI +G_GNUC_INTERNAL +XmpPtr eom_metadata_reader_get_xmp_data (EomMetadataReader *emr); +#endif + +#if 0 +gpointer eom_metadata_reader_get_iptc_chunk (EomMetadataReader *emr); +IptcData* eom_metadata_reader_get_iptc_data (EomMetadataReader *emr); +#endif + +#ifdef HAVE_LCMS +G_GNUC_INTERNAL +cmsHPROFILE eom_metadata_reader_get_icc_profile (EomMetadataReader *emr); +#endif + +G_END_DECLS + +#endif /* _EOM_METADATA_READER_H_ */ diff --git a/src/eom-module.c b/src/eom-module.c new file mode 100644 index 0000000..79fac47 --- /dev/null +++ b/src/eom-module.c @@ -0,0 +1,167 @@ +/* Eye Of Mate - EOM Module + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * - Marco Pesenti Gritti <[email protected]> + * - Christian Persch <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eom-module.h" +#include "eom-debug.h" + +#include <gmodule.h> + +#define EOM_MODULE_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_MODULE, EomModulePrivate)) + +G_DEFINE_TYPE (EomModule, eom_module, G_TYPE_TYPE_MODULE); + +typedef GType (*EomModuleRegisterFunc) (GTypeModule *); + +static gboolean +eom_module_load (GTypeModule *gmodule) +{ + EomModule *module = EOM_MODULE (gmodule); + EomModuleRegisterFunc register_func; + + eom_debug_message (DEBUG_PLUGINS, "Loading %s", module->path); + + module->library = g_module_open (module->path, 0); + + if (module->library == NULL) { + g_warning ("%s", g_module_error()); + + return FALSE; + } + + /* Extract symbols from the lib */ + if (!g_module_symbol (module->library, + "register_eom_plugin", + (void *) ®ister_func)) { + g_warning ("%s", g_module_error()); + g_module_close (module->library); + + return FALSE; + } + + /* Symbol can still be NULL even though g_module_symbol + * returned TRUE */ + if (register_func == NULL) { + g_warning ("Symbol 'register_eom_plugin' should not be NULL"); + g_module_close (module->library); + + return FALSE; + } + + module->type = register_func (gmodule); + + if (module->type == 0) { + g_warning ("Invalid eom plugin contained by module %s", module->path); + return FALSE; + } + + return TRUE; +} + +static void +eom_module_unload (GTypeModule *gmodule) +{ + EomModule *module = EOM_MODULE (gmodule); + + eom_debug_message (DEBUG_PLUGINS, "Unloading %s", module->path); + + g_module_close (module->library); + + module->library = NULL; + module->type = 0; +} + +const gchar * +eom_module_get_path (EomModule *module) +{ + g_return_val_if_fail (EOM_IS_MODULE (module), NULL); + + return module->path; +} + +GObject * +eom_module_new_object (EomModule *module) +{ + eom_debug_message (DEBUG_PLUGINS, "Creating object of type %s", g_type_name (module->type)); + + if (module->type == 0) { + return NULL; + } + + return g_object_new (module->type, NULL); +} + +static void +eom_module_init (EomModule *module) +{ + eom_debug_message (DEBUG_PLUGINS, "EomModule %p initialising", module); +} + +static void +eom_module_finalize (GObject *object) +{ + EomModule *module = EOM_MODULE (object); + + eom_debug_message (DEBUG_PLUGINS, "EomModule %p finalising", module); + + g_free (module->path); + + G_OBJECT_CLASS (eom_module_parent_class)->finalize (object); +} + +static void +eom_module_class_init (EomModuleClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class); + + object_class->finalize = eom_module_finalize; + + module_class->load = eom_module_load; + module_class->unload = eom_module_unload; +} + +EomModule * +eom_module_new (const gchar *path) +{ + EomModule *module; + + if (path == NULL || path[0] == '\0') { + return NULL; + } + + module = g_object_new (EOM_TYPE_MODULE, NULL); + + g_type_module_set_name (G_TYPE_MODULE (module), path); + + module->path = g_strdup (path); + + return module; +} diff --git a/src/eom-module.h b/src/eom-module.h new file mode 100644 index 0000000..241ba28 --- /dev/null +++ b/src/eom-module.h @@ -0,0 +1,72 @@ +/* Eye Of Mate - Main Window + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * - Marco Pesenti Gritti <[email protected]> + * - Christian Persch <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EOM_MODULE_H +#define EOM_MODULE_H + +#include <glib-object.h> +#include <gmodule.h> + +G_BEGIN_DECLS + +typedef struct _EomModule EomModule; +typedef struct _EomModuleClass EomModuleClass; +typedef struct _EomModulePrivate EomModulePrivate; + +#define EOM_TYPE_MODULE (eom_module_get_type ()) +#define EOM_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_MODULE, EomModule)) +#define EOM_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_MODULE, EomModuleClass)) +#define EOM_IS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_MODULE)) +#define EOM_IS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EOM_TYPE_MODULE)) +#define EOM_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_MODULE, EomModuleClass)) + +struct _EomModule { + GTypeModule parent_instance; + + GModule *library; + gchar *path; + GType type; +}; + +struct _EomModuleClass { + GTypeModuleClass parent_class; +}; + +G_GNUC_INTERNAL +GType eom_module_get_type (void) G_GNUC_CONST; + +G_GNUC_INTERNAL +EomModule *eom_module_new (const gchar *path); + +G_GNUC_INTERNAL +const gchar *eom_module_get_path (EomModule *module); + +G_GNUC_INTERNAL +GObject *eom_module_new_object (EomModule *module); + +G_END_DECLS + +#endif diff --git a/src/eom-pixbuf-util.c b/src/eom-pixbuf-util.c new file mode 100644 index 0000000..837bb42 --- /dev/null +++ b/src/eom-pixbuf-util.c @@ -0,0 +1,136 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include "eom-pixbuf-util.h" + +GSList* +eom_pixbuf_get_savable_formats (void) +{ + GSList *list; + GSList *write_list = NULL; + GSList *it; + + list = gdk_pixbuf_get_formats (); + + for (it = list; it != NULL; it = it->next) { + GdkPixbufFormat *format; + + format = (GdkPixbufFormat*) it->data; + if (gdk_pixbuf_format_is_writable (format)) { + write_list = g_slist_prepend (write_list, format); + } + } + + g_slist_free (list); + write_list = g_slist_reverse (write_list); + + return write_list; +} + +GdkPixbufFormat* +eom_pixbuf_get_format_by_suffix (const char *suffix) +{ + GSList *list; + GSList *it; + GdkPixbufFormat *result = NULL; + + g_return_val_if_fail (suffix != NULL, NULL); + + list = gdk_pixbuf_get_formats (); + + for (it = list; (it != NULL) && (result == NULL); it = it->next) { + GdkPixbufFormat *format; + gchar **extensions; + int i; + + format = (GdkPixbufFormat*) it->data; + + extensions = gdk_pixbuf_format_get_extensions (format); + for (i = 0; extensions[i] != NULL; i++) { + /* g_print ("check extension: %s against %s\n", extensions[i], suffix); */ + if (g_ascii_strcasecmp (suffix, extensions[i]) == 0) { + result = format; + break; + } + } + + g_strfreev (extensions); + } + + g_slist_free (list); + + return result; +} + +char* +eom_pixbuf_get_common_suffix (GdkPixbufFormat *format) +{ + char **extensions; + int i; + char *result = NULL; + + if (format == NULL) return NULL; + + extensions = gdk_pixbuf_format_get_extensions (format); + if (extensions[0] == NULL) return NULL; + + /* try to find 3-char suffix first, use the last occurence */ + for (i = 0; extensions [i] != NULL; i++) { + if (strlen (extensions[i]) <= 3) { + g_free (result); + result = g_ascii_strdown (extensions[i], -1); + } + } + + /* otherwise take the first one */ + if (result == NULL) { + result = g_ascii_strdown (extensions[0], -1); + } + + g_strfreev (extensions); + + return result; +} + +static char* +get_suffix_from_basename (const char *basename) +{ + char *suffix; + char *suffix_start; + guint len; + + /* FIXME: does this work for all locales? */ + suffix_start = g_utf8_strrchr (basename, -1, '.'); + + if (suffix_start == NULL) + return NULL; + + len = strlen (suffix_start) - 1; + suffix = g_strndup (suffix_start+1, len); + + return suffix; + +} + +GdkPixbufFormat * +eom_pixbuf_get_format (GFile *file) +{ + GdkPixbufFormat *format; + char *path, *basename, *suffix; + g_return_val_if_fail (file != NULL, NULL); + + path = g_file_get_path (file); + basename = g_path_get_basename (path); + suffix = get_suffix_from_basename (basename); + + format = eom_pixbuf_get_format_by_suffix (suffix); + + g_free (path); + g_free (basename); + g_free (suffix); + + return format; +} + diff --git a/src/eom-pixbuf-util.h b/src/eom-pixbuf-util.h new file mode 100644 index 0000000..e3d6268 --- /dev/null +++ b/src/eom-pixbuf-util.h @@ -0,0 +1,20 @@ +#ifndef _EOM_PIXBUF_UTIL_H_ +#define _EOM_PIXBUF_UTIL_H_ + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gio/gio.h> + +G_GNUC_INTERNAL +GSList* eom_pixbuf_get_savable_formats (void); + +G_GNUC_INTERNAL +GdkPixbufFormat* eom_pixbuf_get_format_by_suffix (const char *suffix); + +G_GNUC_INTERNAL +GdkPixbufFormat* eom_pixbuf_get_format (GFile *file); + +G_GNUC_INTERNAL +char* eom_pixbuf_get_common_suffix (GdkPixbufFormat *format); + +#endif /* _EOM_PIXBUF_UTIL_H_ */ + diff --git a/src/eom-plugin-engine.c b/src/eom-plugin-engine.c new file mode 100644 index 0000000..09662b5 --- /dev/null +++ b/src/eom-plugin-engine.c @@ -0,0 +1,943 @@ +/* Eye Of Mate - EOM Plugin Manager + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "eom-plugin-engine.h" +#include "eom-plugin.h" +#include "eom-module.h" +#include "eom-debug.h" +#include "eom-application.h" +#include "eom-config-keys.h" +#include "eom-util.h" + +#include <glib/gi18n.h> +#include <glib.h> +#include <mateconf/mateconf-client.h> + +#ifdef ENABLE_PYTHON +#include "eom-python-module.h" +#endif + +#define USER_EOM_PLUGINS_LOCATION "plugins/" + +#define EOM_PLUGINS_ENGINE_BASE_KEY "/apps/eom/plugins" + +#define PLUGIN_EXT ".eom-plugin" + +typedef enum { + EOM_PLUGIN_LOADER_C, + EOM_PLUGIN_LOADER_PY, +} EomPluginLoader; + +struct _EomPluginInfo +{ + gchar *file; + + gchar *location; + EomPluginLoader loader; + GTypeModule *module; + + gchar *name; + gchar *desc; + gchar *icon_name; + gchar **authors; + gchar *copyright; + gchar *website; + + EomPlugin *plugin; + + gint active : 1; + + /* A plugin is unavailable if it is not possible to activate it + due to an error loading the plugin module (e.g. for Python plugins + when the interpreter has not been correctly initializated) */ + gint available : 1; +}; + +static void eom_plugin_engine_active_plugins_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data); + +static GList *eom_plugins_list = NULL; + +static MateConfClient *eom_plugin_engine_mateconf_client = NULL; + +static GSList *active_plugins = NULL; + +static void +eom_plugin_info_free (EomPluginInfo *info) +{ + if (info->plugin != NULL) { + eom_debug_message (DEBUG_PLUGINS, "Unref plugin %s", info->name); + + g_object_unref (info->plugin); + } + + g_free (info->file); + g_free (info->location); + g_free (info->name); + g_free (info->desc); + g_free (info->icon_name); + g_free (info->website); + g_free (info->copyright); + g_strfreev (info->authors); + + g_free (info); +} + +static EomPluginInfo * +eom_plugin_engine_load (const gchar *file) +{ + EomPluginInfo *info; + GKeyFile *plugin_file = NULL; + gchar *str; + + g_return_val_if_fail (file != NULL, NULL); + + eom_debug_message (DEBUG_PLUGINS, "Loading plugin: %s", file); + + info = g_new0 (EomPluginInfo, 1); + info->file = g_strdup (file); + + plugin_file = g_key_file_new (); + + if (!g_key_file_load_from_file (plugin_file, file, G_KEY_FILE_NONE, NULL)) { + g_warning ("Bad plugin file: %s", file); + + goto error; + } + + if (!g_key_file_has_key (plugin_file, + "Eom Plugin", + "IAge", + NULL)) { + eom_debug_message (DEBUG_PLUGINS, + "IAge key does not exist in file: %s", file); + + goto error; + } + + /* Check IAge=2 */ + if (g_key_file_get_integer (plugin_file, + "Eom Plugin", + "IAge", + NULL) != 2) { + eom_debug_message (DEBUG_PLUGINS, + "Wrong IAge in file: %s", file); + + goto error; + } + + /* Get Location */ + str = g_key_file_get_string (plugin_file, + "Eom Plugin", + "Module", + NULL); + + if ((str != NULL) && (*str != '\0')) { + info->location = str; + } else { + g_warning ("Could not find 'Module' in %s", file); + + goto error; + } + + /* Get the loader for this plugin */ + str = g_key_file_get_string (plugin_file, + "Eom Plugin", + "Loader", + NULL); + + if (str && strcmp(str, "python") == 0) { + info->loader = EOM_PLUGIN_LOADER_PY; + +#ifndef ENABLE_PYTHON + g_warning ("Cannot load Python plugin '%s' since eom was not " + "compiled with Python support.", file); + + goto error; +#endif + + } else { + info->loader = EOM_PLUGIN_LOADER_C; + } + + g_free (str); + + /* Get Name */ + str = g_key_file_get_locale_string (plugin_file, + "Eom Plugin", + "Name", + NULL, NULL); + if (str) { + info->name = str; + } else { + g_warning ("Could not find 'Name' in %s", file); + + goto error; + } + + /* Get Description */ + str = g_key_file_get_locale_string (plugin_file, + "Eom Plugin", + "Description", + NULL, NULL); + if (str) { + info->desc = str; + } else { + eom_debug_message (DEBUG_PLUGINS, "Could not find 'Description' in %s", file); + } + + /* Get Icon */ + str = g_key_file_get_locale_string (plugin_file, + "Eom Plugin", + "Icon", + NULL, NULL); + if (str) { + info->icon_name = str; + } else { + eom_debug_message (DEBUG_PLUGINS, "Could not find 'Icon' in %s, " + "using 'eom-plugin'", file); + } + + /* Get Authors */ + info->authors = g_key_file_get_string_list (plugin_file, + "Eom Plugin", + "Authors", + NULL, + NULL); + + if (info->authors == NULL) + eom_debug_message (DEBUG_PLUGINS, "Could not find 'Authors' in %s", file); + + + /* Get Copyright */ + str = g_key_file_get_string (plugin_file, + "Eom Plugin", + "Copyright", + NULL); + if (str) { + info->copyright = str; + } else { + eom_debug_message (DEBUG_PLUGINS, "Could not find 'Copyright' in %s", file); + } + + /* Get Website */ + str = g_key_file_get_string (plugin_file, + "Eom Plugin", + "Website", + NULL); + if (str) { + info->website = str; + } else { + eom_debug_message (DEBUG_PLUGINS, "Could not find 'Website' in %s", file); + } + + g_key_file_free (plugin_file); + + /* If we know nothing about the availability of the plugin, + set it as available */ + info->available = TRUE; + + return info; + +error: + g_free (info->file); + g_free (info->location); + g_free (info->name); + g_free (info); + g_key_file_free (plugin_file); + + return NULL; +} + +static gint +compare_plugin_info (EomPluginInfo *info1, + EomPluginInfo *info2) +{ + return strcmp (info1->location, info2->location); +} + +static void +eom_plugin_engine_load_dir (const gchar *dir) +{ + GError *error = NULL; + GDir *d; + const gchar *dirent; + + if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) { + return; + } + + g_return_if_fail (eom_plugin_engine_mateconf_client != NULL); + + eom_debug_message (DEBUG_PLUGINS, "DIR: %s", dir); + + d = g_dir_open (dir, 0, &error); + + if (!d) { + g_warning ("%s", error->message); + g_error_free (error); + + return; + } + + while ((dirent = g_dir_read_name (d))) { + if (g_str_has_suffix (dirent, PLUGIN_EXT)) { + gchar *plugin_file; + EomPluginInfo *info; + + plugin_file = g_build_filename (dir, dirent, NULL); + info = eom_plugin_engine_load (plugin_file); + g_free (plugin_file); + + if (info == NULL) + continue; + + /* If a plugin with this name has already been loaded + * drop this one (user plugins override system plugins) */ + if (g_list_find_custom (eom_plugins_list, + info, + (GCompareFunc)compare_plugin_info) != NULL) { + g_warning ("Two or more plugins named '%s'. " + "Only the first will be considered.\n", + info->location); + + eom_plugin_info_free (info); + + continue; + } + + /* Actually, the plugin will be activated when reactivate_all + * will be called for the first time. */ + info->active = (g_slist_find_custom (active_plugins, + info->location, + (GCompareFunc)strcmp) != NULL); + + eom_plugins_list = g_list_prepend (eom_plugins_list, info); + + eom_debug_message (DEBUG_PLUGINS, "Plugin %s loaded", info->name); + } + } + + eom_plugins_list = g_list_reverse (eom_plugins_list); + + g_dir_close (d); +} + +static void +eom_plugin_engine_load_all (void) +{ + gchar *pdir; + + pdir = g_build_filename (eom_util_dot_dir (), + USER_EOM_PLUGINS_LOCATION, NULL); + + /* Load user's plugins */ + eom_plugin_engine_load_dir (pdir); + + g_free (pdir); + + /* Load system plugins */ + eom_plugin_engine_load_dir (EOM_PLUGIN_DIR "/"); +} + +gboolean +eom_plugin_engine_init (void) +{ + eom_debug (DEBUG_PLUGINS); + + g_return_val_if_fail (eom_plugins_list == NULL, FALSE); + + if (!g_module_supported ()) { + g_warning ("eom is not able to initialize the plugins engine."); + + return FALSE; + } + + eom_plugin_engine_mateconf_client = mateconf_client_get_default (); + + g_return_val_if_fail (eom_plugin_engine_mateconf_client != NULL, FALSE); + + mateconf_client_add_dir (eom_plugin_engine_mateconf_client, + EOM_PLUGINS_ENGINE_BASE_KEY, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + + mateconf_client_notify_add (eom_plugin_engine_mateconf_client, + EOM_CONF_PLUGINS_ACTIVE_PLUGINS, + eom_plugin_engine_active_plugins_changed, + NULL, NULL, NULL); + + active_plugins = mateconf_client_get_list (eom_plugin_engine_mateconf_client, + EOM_CONF_PLUGINS_ACTIVE_PLUGINS, + MATECONF_VALUE_STRING, + NULL); + + eom_plugin_engine_load_all (); + + return TRUE; +} + +void +eom_plugin_engine_garbage_collect (void) +{ +#ifdef ENABLE_PYTHON + eom_python_garbage_collect (); +#endif +} + +void +eom_plugin_engine_shutdown (void) +{ + GList *pl; + + eom_debug (DEBUG_PLUGINS); + +#ifdef ENABLE_PYTHON + /* Note: that this may cause finalization of objects (typically + * the EomWindow) by running the garbage collector. Since some + * of the plugin may have installed callbacks upon object + * finalization (typically they need to free the WindowData) + * it must run before we get rid of the plugins. + */ + eom_python_shutdown (); +#endif + + g_return_if_fail (eom_plugin_engine_mateconf_client != NULL); + + for (pl = eom_plugins_list; pl; pl = pl->next) { + EomPluginInfo *info = (EomPluginInfo*) pl->data; + + eom_plugin_info_free (info); + } + + g_slist_foreach (active_plugins, (GFunc)g_free, NULL); + g_slist_free (active_plugins); + + active_plugins = NULL; + + g_list_free (eom_plugins_list); + eom_plugins_list = NULL; + + g_object_unref (eom_plugin_engine_mateconf_client); + eom_plugin_engine_mateconf_client = NULL; +} + +const GList * +eom_plugin_engine_get_plugins_list (void) +{ + eom_debug (DEBUG_PLUGINS); + + return eom_plugins_list; +} + +static gboolean +load_plugin_module (EomPluginInfo *info) +{ + gchar *path; + gchar *dirname; + + eom_debug (DEBUG_PLUGINS); + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (info->file != NULL, FALSE); + g_return_val_if_fail (info->location != NULL, FALSE); + g_return_val_if_fail (info->plugin == NULL, FALSE); + g_return_val_if_fail (info->available, FALSE); + + switch (info->loader) { + case EOM_PLUGIN_LOADER_C: + dirname = g_path_get_dirname (info->file); + g_return_val_if_fail (dirname != NULL, FALSE); + + path = g_module_build_path (dirname, info->location); + + g_free (dirname); + + g_return_val_if_fail (path != NULL, FALSE); + + info->module = G_TYPE_MODULE (eom_module_new (path)); + + g_free (path); + + break; + +#ifdef ENABLE_PYTHON + case EOM_PLUGIN_LOADER_PY: + { + gchar *dir; + + if (!eom_python_init ()) { + /* Mark plugin as unavailable and fails */ + info->available = FALSE; + + g_warning ("Cannot load Python plugin '%s' since eom " + "was not able to initialize the Python interpreter.", + info->name); + + return FALSE; + } + + dir = g_path_get_dirname (info->file); + + g_return_val_if_fail ((info->location != NULL) && + (info->location[0] != '\0'), + FALSE); + + info->module = G_TYPE_MODULE ( + eom_python_module_new (dir, info->location)); + + g_free (dir); + + break; + } +#endif + default: + g_return_val_if_reached (FALSE); + } + + if (!g_type_module_use (info->module)) { + switch (info->loader) { + case EOM_PLUGIN_LOADER_C: + g_warning ("Cannot load plugin '%s' since file '%s' cannot be read.", + info->name, + eom_module_get_path (EOM_MODULE (info->module))); + break; + + case EOM_PLUGIN_LOADER_PY: + g_warning ("Cannot load Python plugin '%s' since file '%s' cannot be read.", + info->name, + info->location); + break; + + default: + g_return_val_if_reached (FALSE); + } + + g_object_unref (G_OBJECT (info->module)); + + info->module = NULL; + + /* Mark plugin as unavailable and fails */ + info->available = FALSE; + + return FALSE; + } + + switch (info->loader) { + case EOM_PLUGIN_LOADER_C: + info->plugin = + EOM_PLUGIN (eom_module_new_object (EOM_MODULE (info->module))); + break; + +#ifdef ENABLE_PYTHON + case EOM_PLUGIN_LOADER_PY: + info->plugin = + EOM_PLUGIN (eom_python_module_new_object (EOM_PYTHON_MODULE (info->module))); + break; +#endif + + default: + g_return_val_if_reached (FALSE); + } + + g_type_module_unuse (info->module); + + eom_debug_message (DEBUG_PLUGINS, "End"); + + return TRUE; +} + +static gboolean +eom_plugin_engine_activate_plugin_real (EomPluginInfo *info) +{ + gboolean res = TRUE; + + /* Plugin is not available, don't try to activate/load it */ + if (!info->available) { + return FALSE; + } + + if (info->plugin == NULL) + res = load_plugin_module (info); + + if (res) { + const GList *wins = eom_application_get_windows (EOM_APP); + + while (wins != NULL) { + eom_plugin_activate (info->plugin, + EOM_WINDOW (wins->data)); + + wins = g_list_next (wins); + } + } else { + g_warning ("Error activating plugin '%s'", info->name); + } + + return res; +} + +gboolean +eom_plugin_engine_activate_plugin (EomPluginInfo *info) +{ + eom_debug (DEBUG_PLUGINS); + + g_return_val_if_fail (info != NULL, FALSE); + + if (!info->available) + return FALSE; + + if (info->active) + return TRUE; + + if (eom_plugin_engine_activate_plugin_real (info)) { + gboolean res; + GSList *list; + + /* Update plugin state */ + info->active = TRUE; + + list = active_plugins; + + while (list != NULL) { + if (strcmp (info->location, (gchar *)list->data) == 0) { + g_warning ("Plugin '%s' is already active.", info->name); + + return TRUE; + } + + list = g_slist_next (list); + } + + active_plugins = g_slist_insert_sorted (active_plugins, + g_strdup (info->location), + (GCompareFunc)strcmp); + + res = mateconf_client_set_list (eom_plugin_engine_mateconf_client, + EOM_CONF_PLUGINS_ACTIVE_PLUGINS, + MATECONF_VALUE_STRING, + active_plugins, + NULL); + + if (!res) + g_warning ("Error saving the list of active plugins."); + + return TRUE; + } + + return FALSE; +} + +static void +eom_plugin_engine_deactivate_plugin_real (EomPluginInfo *info) +{ + const GList *wins = eom_application_get_windows (EOM_APP); + + while (wins != NULL) { + eom_plugin_deactivate (info->plugin, + EOM_WINDOW (wins->data)); + + wins = g_list_next (wins); + } +} + +gboolean +eom_plugin_engine_deactivate_plugin (EomPluginInfo *info) +{ + gboolean res; + GSList *list; + + eom_debug (DEBUG_PLUGINS); + + g_return_val_if_fail (info != NULL, FALSE); + + if (!info->active || !info->available) + return TRUE; + + eom_plugin_engine_deactivate_plugin_real (info); + + /* Update plugin state */ + info->active = FALSE; + + list = active_plugins; + res = (list == NULL); + + while (list != NULL) { + if (strcmp (info->location, (gchar *)list->data) == 0) { + g_free (list->data); + active_plugins = g_slist_delete_link (active_plugins, list); + list = NULL; + res = TRUE; + } else { + list = g_slist_next (list); + } + } + + if (!res) { + g_warning ("Plugin '%s' is already deactivated.", info->name); + + return TRUE; + } + + res = mateconf_client_set_list (eom_plugin_engine_mateconf_client, + EOM_CONF_PLUGINS_ACTIVE_PLUGINS, + MATECONF_VALUE_STRING, + active_plugins, + NULL); + + if (!res) + g_warning ("Error saving the list of active plugins."); + + return TRUE; +} + +gboolean +eom_plugin_engine_plugin_is_active (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, FALSE); + + return (info->available && info->active); +} + +gboolean +eom_plugin_engine_plugin_is_available (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, FALSE); + + return (info->available != FALSE); +} + +static void +reactivate_all (EomWindow *window) +{ + GList *pl; + + eom_debug (DEBUG_PLUGINS); + + for (pl = eom_plugins_list; pl; pl = pl->next) { + gboolean res = TRUE; + + EomPluginInfo *info = (EomPluginInfo*)pl->data; + + /* If plugin is not available, don't try to activate/load it */ + if (info->available && info->active) { + if (info->plugin == NULL) + res = load_plugin_module (info); + + if (res) + eom_plugin_activate (info->plugin, + window); + } + } + + eom_debug_message (DEBUG_PLUGINS, "End"); +} + +void +eom_plugin_engine_update_plugins_ui (EomWindow *window, + gboolean new_window) +{ + GList *pl; + + eom_debug (DEBUG_PLUGINS); + + g_return_if_fail (EOM_IS_WINDOW (window)); + + if (new_window) + reactivate_all (window); + + /* Updated ui of all the plugins that implement update_ui */ + for (pl = eom_plugins_list; pl; pl = pl->next) { + EomPluginInfo *info = (EomPluginInfo*)pl->data; + + if (!info->available || !info->active) + continue; + + eom_debug_message (DEBUG_PLUGINS, "Updating UI of %s", info->name); + + eom_plugin_update_ui (info->plugin, window); + } +} + +gboolean +eom_plugin_engine_plugin_is_configurable (EomPluginInfo *info) { + eom_debug (DEBUG_PLUGINS); + + g_return_val_if_fail (info != NULL, FALSE); + + if ((info->plugin == NULL) || !info->active || !info->available) + return FALSE; + + return eom_plugin_is_configurable (info->plugin); +} + +void +eom_plugin_engine_configure_plugin (EomPluginInfo *info, + GtkWindow *parent) +{ + GtkWidget *conf_dlg; + + GtkWindowGroup *wg; + + eom_debug (DEBUG_PLUGINS); + + g_return_if_fail (info != NULL); + + conf_dlg = eom_plugin_create_configure_dialog (info->plugin); + + g_return_if_fail (conf_dlg != NULL); + + gtk_window_set_transient_for (GTK_WINDOW (conf_dlg), + parent); + + // Will return a default group if no group is set + wg = gtk_window_get_group (parent); + + // For now assign a dedicated window group if it is + // the default one until we know if this is really needed + if (wg == gtk_window_get_group (NULL)) { + wg = gtk_window_group_new (); + gtk_window_group_add_window (wg, parent); + } + + gtk_window_group_add_window (wg, + GTK_WINDOW (conf_dlg)); + + gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE); + + gtk_widget_show (conf_dlg); +} + +static void +eom_plugin_engine_active_plugins_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GList *pl; + gboolean to_activate; + + eom_debug (DEBUG_PLUGINS); + + g_return_if_fail (entry->key != NULL); + g_return_if_fail (entry->value != NULL); + + if (!((entry->value->type == MATECONF_VALUE_LIST) && + (mateconf_value_get_list_type (entry->value) == MATECONF_VALUE_STRING))) { + g_warning ("The mateconf key '%s' may be corrupted.", EOM_CONF_PLUGINS_ACTIVE_PLUGINS); + return; + } + + active_plugins = mateconf_client_get_list (eom_plugin_engine_mateconf_client, + EOM_CONF_PLUGINS_ACTIVE_PLUGINS, + MATECONF_VALUE_STRING, + NULL); + + for (pl = eom_plugins_list; pl; pl = pl->next) { + EomPluginInfo *info = (EomPluginInfo*)pl->data; + + if (!info->available) + continue; + + to_activate = (g_slist_find_custom (active_plugins, + info->location, + (GCompareFunc)strcmp) != NULL); + + if (!info->active && to_activate) { + /* Activate plugin */ + if (eom_plugin_engine_activate_plugin_real (info)) + /* Update plugin state */ + info->active = TRUE; + } else { + if (info->active && !to_activate) { + eom_plugin_engine_deactivate_plugin_real (info); + + /* Update plugin state */ + info->active = FALSE; + } + } + } +} + +const gchar * +eom_plugin_engine_get_plugin_name (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->name; +} + +const gchar * +eom_plugin_engine_get_plugin_description (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->desc; +} + +const gchar * +eom_plugin_engine_get_plugin_icon_name (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + /* Use the eom-plugin icon as a default if the plugin does not + have its own */ + if (info->icon_name != NULL && + gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), + info->icon_name)) + return info->icon_name; + else + return "eom-plugin"; +} + +const gchar ** +eom_plugin_engine_get_plugin_authors (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, (const gchar **)NULL); + + return (const gchar **) info->authors; +} + +const gchar * +eom_plugin_engine_get_plugin_website (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->website; +} + +const gchar * +eom_plugin_engine_get_plugin_copyright (EomPluginInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->copyright; +} diff --git a/src/eom-plugin-engine.h b/src/eom-plugin-engine.h new file mode 100644 index 0000000..bd2ce7f --- /dev/null +++ b/src/eom-plugin-engine.h @@ -0,0 +1,89 @@ +/* Eye Of Mate - EOM Plugin Engine + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-plugins-engine.h) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_PLUGIN_ENGINE_H__ +#define __EOM_PLUGIN_ENGINE_H__ + +#include "eom-window.h" + +#include <glib.h> + +typedef struct _EomPluginInfo EomPluginInfo; + +G_GNUC_INTERNAL +gboolean eom_plugin_engine_init (void); + +G_GNUC_INTERNAL +void eom_plugin_engine_shutdown (void); + +G_GNUC_INTERNAL +void eom_plugin_engine_garbage_collect (void); + +G_GNUC_INTERNAL +const GList *eom_plugin_engine_get_plugins_list (void); + +G_GNUC_INTERNAL +gboolean eom_plugin_engine_activate_plugin (EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean eom_plugin_engine_deactivate_plugin (EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean eom_plugin_engine_plugin_is_active (EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean eom_plugin_engine_plugin_is_available (EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean eom_plugin_engine_plugin_is_configurable + (EomPluginInfo *info); + +G_GNUC_INTERNAL +void eom_plugin_engine_configure_plugin (EomPluginInfo *info, + GtkWindow *parent); + +G_GNUC_INTERNAL +void eom_plugin_engine_update_plugins_ui (EomWindow *window, + gboolean new_window); + +G_GNUC_INTERNAL +const gchar *eom_plugin_engine_get_plugin_name (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar *eom_plugin_engine_get_plugin_description + (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar *eom_plugin_engine_get_plugin_icon_name (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar **eom_plugin_engine_get_plugin_authors (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar *eom_plugin_engine_get_plugin_website (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar *eom_plugin_engine_get_plugin_copyright (EomPluginInfo *info); + +#endif /* __EOM_PLUGIN_ENGINE_H__ */ diff --git a/src/eom-plugin-manager.c b/src/eom-plugin-manager.c new file mode 100644 index 0000000..623827f --- /dev/null +++ b/src/eom-plugin-manager.c @@ -0,0 +1,917 @@ +/* Eye Of Mate - EOM Plugin Manager + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "eom-plugin-manager.h" +#include "eom-plugin-engine.h" +#include "eom-util.h" +#include "eom-plugin.h" +#include "eom-debug.h" + +#include <glib/gi18n.h> + +enum { + ACTIVE_COLUMN, + AVAILABLE_COLUMN, + INFO_COLUMN, + N_COLUMNS +}; + +#define EOM_PLUGIN_MANAGER_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PLUGIN_MANAGER, EomPluginManagerPrivate)) + +G_DEFINE_TYPE (EomPluginManager, eom_plugin_manager, GTK_TYPE_VBOX) + +#define PLUGIN_MANAGER_NAME_TITLE _("Plugin") +#define PLUGIN_MANAGER_ACTIVE_TITLE _("Enabled") + +struct _EomPluginManagerPrivate { + GtkWidget *tree; + + GtkWidget *about_button; + GtkWidget *configure_button; + + const GList *plugins; + + GtkWidget *about; + + GtkWidget *popup_menu; +}; + +static EomPluginInfo *plugin_manager_get_selected_plugin (EomPluginManager *pm); +static void plugin_manager_toggle_active (GtkTreeIter *iter, GtkTreeModel *model); +static void eom_plugin_manager_finalize (GObject *object); + +static void +eom_plugin_manager_class_init (EomPluginManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = eom_plugin_manager_finalize; + + g_type_class_add_private (object_class, sizeof (EomPluginManagerPrivate)); +} + +static void +about_button_cb (GtkWidget *button, + EomPluginManager *pm) +{ + EomPluginInfo *info; + + eom_debug (DEBUG_PLUGINS); + + info = plugin_manager_get_selected_plugin (pm); + + g_return_if_fail (info != NULL); + + /* If there is another about dialog already open destroy it */ + if (pm->priv->about) + gtk_widget_destroy (pm->priv->about); + + pm->priv->about = g_object_new (GTK_TYPE_ABOUT_DIALOG, + "program-name" , eom_plugin_engine_get_plugin_name (info), + "copyright", eom_plugin_engine_get_plugin_copyright (info), + "authors", eom_plugin_engine_get_plugin_authors (info), + "comments", eom_plugin_engine_get_plugin_description (info), + "website", eom_plugin_engine_get_plugin_website (info), + "logo-icon-name", eom_plugin_engine_get_plugin_icon_name (info), + NULL); + + gtk_window_set_destroy_with_parent (GTK_WINDOW (pm->priv->about), + TRUE); + + g_signal_connect (pm->priv->about, + "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + g_signal_connect (pm->priv->about, + "destroy", + G_CALLBACK (gtk_widget_destroyed), + &pm->priv->about); + + gtk_window_set_transient_for (GTK_WINDOW (pm->priv->about), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm)))); + + gtk_widget_show (pm->priv->about); +} + +static void +configure_button_cb (GtkWidget *button, + EomPluginManager *pm) +{ + EomPluginInfo *info; + GtkWindow *toplevel; + + eom_debug (DEBUG_PLUGINS); + + info = plugin_manager_get_selected_plugin (pm); + + g_return_if_fail (info != NULL); + + eom_debug_message (DEBUG_PLUGINS, "Configuring: %s\n", + eom_plugin_engine_get_plugin_name (info)); + + toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm))); + + eom_plugin_engine_configure_plugin (info, toplevel); + + eom_debug_message (DEBUG_PLUGINS, "Done"); +} + +static void +plugin_manager_view_info_cell_cb (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + EomPluginInfo *info; + gchar *text; + + g_return_if_fail (tree_model != NULL); + g_return_if_fail (tree_column != NULL); + + gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1); + + if (info == NULL) + return; + + text = g_markup_printf_escaped ("<b>%s</b>\n%s", + eom_plugin_engine_get_plugin_name (info), + eom_plugin_engine_get_plugin_description (info)); + + g_object_set (G_OBJECT (cell), + "markup", text, + "sensitive", eom_plugin_engine_plugin_is_available (info), + NULL); + + g_free (text); +} + +static void +plugin_manager_view_icon_cell_cb (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + EomPluginInfo *info; + + g_return_if_fail (tree_model != NULL); + g_return_if_fail (tree_column != NULL); + + gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1); + + if (info == NULL) + return; + + g_object_set (G_OBJECT (cell), + "icon-name", eom_plugin_engine_get_plugin_icon_name (info), + "sensitive", eom_plugin_engine_plugin_is_available (info), + NULL); +} + + +static void +active_toggled_cb (GtkCellRendererToggle *cell, + gchar *path_str, + EomPluginManager *pm) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeModel *model; + + eom_debug (DEBUG_PLUGINS); + + path = gtk_tree_path_new_from_string (path_str); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_if_fail (model != NULL); + + gtk_tree_model_get_iter (model, &iter, path); + + if (&iter != NULL) + plugin_manager_toggle_active (&iter, model); + + gtk_tree_path_free (path); +} + +static void +cursor_changed_cb (GtkTreeView *view, + gpointer data) +{ + EomPluginManager *pm = data; + EomPluginInfo *info; + + eom_debug (DEBUG_PLUGINS); + + info = plugin_manager_get_selected_plugin (pm); + + gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->about_button), + info != NULL); + + gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button), + (info != NULL) && + eom_plugin_engine_plugin_is_configurable (info)); +} + +static void +row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + EomPluginManager *pm = data; + GtkTreeIter iter; + GtkTreeModel *model; + + eom_debug (DEBUG_PLUGINS); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_if_fail (model != NULL); + + gtk_tree_model_get_iter (model, &iter, path); + + g_return_if_fail (&iter != NULL); + + plugin_manager_toggle_active (&iter, model); +} + +static void +plugin_manager_populate_lists (EomPluginManager *pm) +{ + const GList *plugins; + GtkListStore *model; + GtkTreeIter iter; + + eom_debug (DEBUG_PLUGINS); + + plugins = pm->priv->plugins; + + model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree))); + + while (plugins) { + EomPluginInfo *info; + info = (EomPluginInfo *)plugins->data; + + gtk_list_store_append (model, &iter); + gtk_list_store_set (model, &iter, + ACTIVE_COLUMN, eom_plugin_engine_plugin_is_active (info), + AVAILABLE_COLUMN, eom_plugin_engine_plugin_is_available (info), + INFO_COLUMN, info, + -1); + + plugins = plugins->next; + } + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) { + GtkTreeSelection *selection; + EomPluginInfo* info; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_if_fail (selection != NULL); + + gtk_tree_selection_select_iter (selection, &iter); + + gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, + INFO_COLUMN, &info, -1); + + gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button), + eom_plugin_engine_plugin_is_configurable (info)); + } +} + +static gboolean +plugin_manager_set_active (GtkTreeIter *iter, + GtkTreeModel *model, + gboolean active) +{ + EomPluginInfo *info; + gboolean res = TRUE; + + eom_debug (DEBUG_PLUGINS); + + gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1); + + g_return_val_if_fail (info != NULL, FALSE); + + if (active) { + /* Activate the plugin */ + if (!eom_plugin_engine_activate_plugin (info)) { + eom_debug_message (DEBUG_PLUGINS, "Could not activate %s.\n", + eom_plugin_engine_get_plugin_name (info)); + + res = FALSE; + } + } else { + /* Deactivate the plugin */ + if (!eom_plugin_engine_deactivate_plugin (info)) { + eom_debug_message (DEBUG_PLUGINS, "Could not deactivate %s.\n", + eom_plugin_engine_get_plugin_name (info)); + + res = FALSE; + } + } + + /* Set new value */ + gtk_list_store_set (GTK_LIST_STORE (model), + iter, + ACTIVE_COLUMN, eom_plugin_engine_plugin_is_active (info), + AVAILABLE_COLUMN, eom_plugin_engine_plugin_is_available (info), + -1); + + return res; +} + +static void +plugin_manager_toggle_active (GtkTreeIter *iter, + GtkTreeModel *model) +{ + gboolean active; + + eom_debug (DEBUG_PLUGINS); + + gtk_tree_model_get (model, iter, ACTIVE_COLUMN, &active, -1); + + active ^= 1; + + plugin_manager_set_active (iter, model, active); +} + +static EomPluginInfo * +plugin_manager_get_selected_plugin (EomPluginManager *pm) +{ + EomPluginInfo *info = NULL; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection; + + eom_debug (DEBUG_PLUGINS); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_val_if_fail (model != NULL, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_val_if_fail (selection != NULL, NULL); + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { + gtk_tree_model_get (model, &iter, INFO_COLUMN, &info, -1); + } + + return info; +} + +static void +plugin_manager_set_active_all (EomPluginManager *pm, + gboolean active) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + eom_debug (DEBUG_PLUGINS); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_if_fail (model != NULL); + + gtk_tree_model_get_iter_first (model, &iter); + + do { + plugin_manager_set_active (&iter, model, active); + } while (gtk_tree_model_iter_next (model, &iter)); +} + +/* Callback used as the interactive search comparison function */ +static gboolean +name_search_cb (GtkTreeModel *model, + gint column, + const gchar *key, + GtkTreeIter *iter, + gpointer data) +{ + EomPluginInfo *info; + gchar *normalized_string; + gchar *normalized_key; + gchar *case_normalized_string; + gchar *case_normalized_key; + gint key_len; + gboolean retval; + + gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1); + + if (!info) + return FALSE; + + normalized_string = g_utf8_normalize (eom_plugin_engine_get_plugin_name (info), -1, G_NORMALIZE_ALL); + normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); + case_normalized_string = g_utf8_casefold (normalized_string, -1); + case_normalized_key = g_utf8_casefold (normalized_key, -1); + + key_len = strlen (case_normalized_key); + + /* Oddly enough, this callback must return whether to stop the search + * because we found a match, not whether we actually matched. */ + retval = (strncmp (case_normalized_key, case_normalized_string, key_len) != 0); + + g_free (normalized_key); + g_free (normalized_string); + g_free (case_normalized_key); + g_free (case_normalized_string); + + return retval; +} + +static void +enable_plugin_menu_cb (GtkMenu *menu, + EomPluginManager *pm) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_if_fail (model != NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + + g_return_if_fail (selection != NULL); + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + plugin_manager_toggle_active (&iter, model); +} + +static void +enable_all_menu_cb (GtkMenu *menu, + EomPluginManager *pm) +{ + plugin_manager_set_active_all (pm, TRUE); +} + +static void +disable_all_menu_cb (GtkMenu *menu, + EomPluginManager *pm) +{ + plugin_manager_set_active_all (pm, FALSE); +} + +static GtkWidget * +create_tree_popup_menu (EomPluginManager *pm) +{ + GtkWidget *menu; + GtkWidget *item; + GtkWidget *image; + EomPluginInfo *info; + + info = plugin_manager_get_selected_plugin (pm); + + if (info == NULL) + return NULL; + + menu = gtk_menu_new (); + + item = gtk_image_menu_item_new_with_mnemonic (_("_About")); + image = gtk_image_new_from_stock (GTK_STOCK_ABOUT, + GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + g_signal_connect (item, "activate", + G_CALLBACK (about_button_cb), pm); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_image_menu_item_new_with_mnemonic (_("C_onfigure")); + image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, + GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + g_signal_connect (item, "activate", + G_CALLBACK (configure_button_cb), pm); + gtk_widget_set_sensitive (item, + eom_plugin_engine_plugin_is_configurable (info)); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_check_menu_item_new_with_mnemonic (_("A_ctivate")); + gtk_widget_set_sensitive (item, + eom_plugin_engine_plugin_is_available (info)); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + eom_plugin_engine_plugin_is_active (info)); + g_signal_connect (item, "toggled", + G_CALLBACK (enable_plugin_menu_cb), pm); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("Ac_tivate All")); + g_signal_connect (item, "activate", + G_CALLBACK (enable_all_menu_cb), pm); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Deactivate All")); + g_signal_connect (item, "activate", + G_CALLBACK (disable_all_menu_cb), pm); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + gtk_widget_show_all (menu); + + return menu; +} + +static void +tree_popup_menu_detach (EomPluginManager *pm, + GtkMenu *menu) +{ + pm->priv->popup_menu = NULL; +} + +static void +menu_position_under_widget (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) +{ + GtkWidget *w = GTK_WIDGET (user_data); + GtkRequisition requisition; + GtkAllocation allocation; + + gdk_window_get_origin (gtk_widget_get_window (w), x, y); + gtk_widget_size_request (GTK_WIDGET (menu), &requisition); + gtk_widget_get_allocation (w, &allocation); + + if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL) { + *x += allocation.x + allocation.width - requisition.width; + } else { + *x += allocation.x; + } + + *y += allocation.y + allocation.height; + + *push_in = TRUE; +} + +static void +menu_position_under_tree_view (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) +{ + GtkTreeView *tree = GTK_TREE_VIEW (user_data); + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (tree); + + g_return_if_fail (model != NULL); + + selection = gtk_tree_view_get_selection (tree); + + g_return_if_fail (selection != NULL); + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { + GtkTreePath *path; + GdkRectangle rect; + + gdk_window_get_origin (gtk_widget_get_window(GTK_WIDGET (tree)), + x, y); + + path = gtk_tree_model_get_path (model, &iter); + + gtk_tree_view_get_cell_area (tree, + path, + gtk_tree_view_get_column (tree, 0), /* FIXME 0 for RTL ? */ + &rect); + gtk_tree_path_free (path); + + *x += rect.x; + *y += rect.y + rect.height; + + if (gtk_widget_get_direction (GTK_WIDGET (tree)) == GTK_TEXT_DIR_RTL) { + GtkRequisition requisition; + + gtk_widget_size_request (GTK_WIDGET (menu), &requisition); + + *x += rect.width - requisition.width; + } + } else { + /* No selection -> regular "under widget" positioning */ + menu_position_under_widget (menu, + x, y, push_in, + tree); + } +} +static void +show_tree_popup_menu (GtkTreeView *tree, + EomPluginManager *pm, + GdkEventButton *event) +{ + if (pm->priv->popup_menu) + gtk_widget_destroy (pm->priv->popup_menu); + + pm->priv->popup_menu = create_tree_popup_menu (pm); + + if (pm->priv->popup_menu == NULL) + return; + + gtk_menu_attach_to_widget (GTK_MENU (pm->priv->popup_menu), + GTK_WIDGET (pm), + (GtkMenuDetachFunc) tree_popup_menu_detach); + + if (event != NULL) { + gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL, + NULL, NULL, + event->button, event->time); + } else { + gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL, + menu_position_under_tree_view, tree, + 0, gtk_get_current_event_time ()); + + gtk_menu_shell_select_first (GTK_MENU_SHELL (pm->priv->popup_menu), + FALSE); + } +} + +static gboolean +button_press_event_cb (GtkWidget *tree, + GdkEventButton *event, + EomPluginManager *pm) +{ + /* We want the treeview selection to be updated before showing the menu. + * This code is evil, thanks to Federico Mena Quintero's black magic. + * See: http://mail.gnome.org/archives/gtk-devel-list/2006-February/msg00168.html + * FIXME: Let's remove it asap. + */ + static gboolean in_press = FALSE; + gboolean handled; + + if (in_press) + return FALSE; /* we re-entered */ + + if (GDK_BUTTON_PRESS != event->type || 3 != event->button) + return FALSE; /* let the normal handler run */ + + in_press = TRUE; + handled = gtk_widget_event (tree, (GdkEvent *) event); + in_press = FALSE; + + if (!handled) + return FALSE; + + /* The selection is fully updated by now */ + show_tree_popup_menu (GTK_TREE_VIEW (tree), pm, event); + + return TRUE; +} + +static gboolean +popup_menu_cb (GtkTreeView *tree, + EomPluginManager *pm) +{ + show_tree_popup_menu (tree, pm, NULL); + + return TRUE; +} + +static gint +model_name_sort_func (GtkTreeModel *model, + GtkTreeIter *iter1, + GtkTreeIter *iter2, + gpointer user_data) +{ + EomPluginInfo *info1, *info2; + + gtk_tree_model_get (model, iter1, INFO_COLUMN, &info1, -1); + gtk_tree_model_get (model, iter2, INFO_COLUMN, &info2, -1); + + return g_utf8_collate (eom_plugin_engine_get_plugin_name (info1), + eom_plugin_engine_get_plugin_name (info2)); +} + +static void +plugin_manager_construct_tree (EomPluginManager *pm) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GtkListStore *model; + + eom_debug (DEBUG_PLUGINS); + + model = gtk_list_store_new (N_COLUMNS, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_POINTER); + + gtk_tree_view_set_model (GTK_TREE_VIEW (pm->priv->tree), + GTK_TREE_MODEL (model)); + + g_object_unref (model); + + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (pm->priv->tree), TRUE); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (pm->priv->tree), FALSE); + + /* First column */ + cell = gtk_cell_renderer_toggle_new (); + g_object_set (cell, "xpad", 6, NULL); + g_signal_connect (cell, + "toggled", + G_CALLBACK (active_toggled_cb), + pm); + column = gtk_tree_view_column_new_with_attributes (PLUGIN_MANAGER_ACTIVE_TITLE, + cell, + "active", ACTIVE_COLUMN, + "activatable", AVAILABLE_COLUMN, + "sensitive", AVAILABLE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column); + + /* Second column */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, PLUGIN_MANAGER_NAME_TITLE); + gtk_tree_view_column_set_resizable (column, TRUE); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + g_object_set (cell, "stock-size", GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); + gtk_tree_view_column_set_cell_data_func (column, cell, + plugin_manager_view_icon_cell_cb, + pm, NULL); + + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, cell, TRUE); + g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_set_cell_data_func (column, cell, + plugin_manager_view_info_cell_cb, + pm, NULL); + + gtk_tree_view_column_set_spacing (column, 6); + gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column); + + /* Sort on the plugin names */ + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model), + model_name_sort_func, + NULL, + NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + /* Enable search for our non-string column */ + gtk_tree_view_set_search_column (GTK_TREE_VIEW (pm->priv->tree), + INFO_COLUMN); + gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (pm->priv->tree), + name_search_cb, + NULL, + NULL); + + g_signal_connect (pm->priv->tree, + "cursor_changed", + G_CALLBACK (cursor_changed_cb), + pm); + + g_signal_connect (pm->priv->tree, + "row_activated", + G_CALLBACK (row_activated_cb), + pm); + + g_signal_connect (pm->priv->tree, + "button-press-event", + G_CALLBACK (button_press_event_cb), + pm); + + g_signal_connect (pm->priv->tree, + "popup-menu", + G_CALLBACK (popup_menu_cb), + pm); + + gtk_widget_show (pm->priv->tree); +} + +static void +eom_plugin_manager_init (EomPluginManager *pm) +{ + GtkWidget *label; + GtkWidget *viewport; + GtkWidget *hbuttonbox; + + eom_debug (DEBUG_PLUGINS); + + pm->priv = EOM_PLUGIN_MANAGER_GET_PRIVATE (pm); + + gtk_box_set_spacing (GTK_BOX (pm), 6); + + label = gtk_label_new_with_mnemonic (_("Active _Plugins:")); + + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + + gtk_box_pack_start (GTK_BOX (pm), label, FALSE, TRUE, 0); + + viewport = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (viewport), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (viewport), + GTK_SHADOW_IN); + + gtk_box_pack_start (GTK_BOX (pm), viewport, TRUE, TRUE, 0); + + pm->priv->tree = gtk_tree_view_new (); + gtk_container_add (GTK_CONTAINER (viewport), pm->priv->tree); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), pm->priv->tree); + + hbuttonbox = gtk_hbutton_box_new (); + + gtk_box_pack_start (GTK_BOX (pm), hbuttonbox, FALSE, FALSE, 0); + + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (hbuttonbox), 8); + + pm->priv->about_button = gtk_button_new_with_mnemonic (_("_About Plugin")); + gtk_button_set_image (GTK_BUTTON (pm->priv->about_button), + gtk_image_new_from_stock (GTK_STOCK_ABOUT, + GTK_ICON_SIZE_BUTTON)); + + gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->about_button); + + pm->priv->configure_button = gtk_button_new_with_mnemonic (_("C_onfigure Plugin")); + gtk_button_set_image (GTK_BUTTON (pm->priv->configure_button), + gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, + GTK_ICON_SIZE_BUTTON)); + + gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->configure_button); + + gtk_widget_set_size_request (GTK_WIDGET (viewport), 270, 100); + + g_signal_connect (pm->priv->about_button, + "clicked", + G_CALLBACK (about_button_cb), + pm); + + g_signal_connect (pm->priv->configure_button, + "clicked", + G_CALLBACK (configure_button_cb), + pm); + + plugin_manager_construct_tree (pm); + + /* Get the list of available plugins (or installed) */ + pm->priv->plugins = eom_plugin_engine_get_plugins_list (); + + if (pm->priv->plugins != NULL) { + plugin_manager_populate_lists (pm); + } else { + gtk_widget_set_sensitive (pm->priv->about_button, FALSE); + gtk_widget_set_sensitive (pm->priv->configure_button, FALSE); + } +} + +static void +eom_plugin_manager_finalize (GObject *object) +{ + EomPluginManager *pm = EOM_PLUGIN_MANAGER (object); + + if (pm->priv->popup_menu) + gtk_widget_destroy (pm->priv->popup_menu); + + G_OBJECT_CLASS (eom_plugin_manager_parent_class)->finalize (object); +} + +GtkWidget * +eom_plugin_manager_new (void) +{ + return g_object_new (EOM_TYPE_PLUGIN_MANAGER, NULL); +} diff --git a/src/eom-plugin-manager.h b/src/eom-plugin-manager.h new file mode 100644 index 0000000..e35c817 --- /dev/null +++ b/src/eom-plugin-manager.h @@ -0,0 +1,61 @@ +/* Eye Of Mate - EOM Plugin Manager + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_PLUGIN_MANAGER_H__ +#define __EOM_PLUGIN_MANAGER_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _EomPluginManager EomPluginManager; +typedef struct _EomPluginManagerClass EomPluginManagerClass; +typedef struct _EomPluginManagerPrivate EomPluginManagerPrivate; + +#define EOM_TYPE_PLUGIN_MANAGER (eom_plugin_manager_get_type()) +#define EOM_PLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PLUGIN_MANAGER, EomPluginManager)) +#define EOM_PLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_PLUGIN_MANAGER, EomPluginManagerClass)) +#define EOM_IS_PLUGIN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_PLUGIN_MANAGER)) +#define EOM_IS_PLUGIN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_PLUGIN_MANAGER)) +#define EOM_PLUGIN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_PLUGIN_MANAGER, EomPluginManagerClass)) + +struct _EomPluginManager { + GtkVBox vbox; + + EomPluginManagerPrivate *priv; +}; + +struct _EomPluginManagerClass { + GtkVBoxClass parent_class; +}; + +G_GNUC_INTERNAL +GType eom_plugin_manager_get_type (void) G_GNUC_CONST; + +G_GNUC_INTERNAL +GtkWidget *eom_plugin_manager_new (void); + +G_END_DECLS + +#endif /* __EOM_PLUGIN_MANAGER_H__ */ diff --git a/src/eom-plugin.c b/src/eom-plugin.c new file mode 100644 index 0000000..7210128 --- /dev/null +++ b/src/eom-plugin.c @@ -0,0 +1,108 @@ +/* Eye Of Mate - EOM Plugin + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eom-plugin.h" + +G_DEFINE_TYPE (EomPlugin, eom_plugin, G_TYPE_OBJECT) + +static void +dummy (EomPlugin *plugin, EomWindow *window) +{ +} + +static GtkWidget * +create_configure_dialog (EomPlugin *plugin) +{ + return NULL; +} + +static gboolean +is_configurable (EomPlugin *plugin) +{ + return (EOM_PLUGIN_GET_CLASS (plugin)->create_configure_dialog != + create_configure_dialog); +} + +static void +eom_plugin_class_init (EomPluginClass *klass) +{ + klass->activate = dummy; + klass->deactivate = dummy; + klass->update_ui = dummy; + + klass->create_configure_dialog = create_configure_dialog; + klass->is_configurable = is_configurable; +} + +static void +eom_plugin_init (EomPlugin *plugin) +{ +} + +void +eom_plugin_activate (EomPlugin *plugin, EomWindow *window) +{ + g_return_if_fail (EOM_IS_PLUGIN (plugin)); + g_return_if_fail (EOM_IS_WINDOW (window)); + + EOM_PLUGIN_GET_CLASS (plugin)->activate (plugin, window); +} + +void +eom_plugin_deactivate (EomPlugin *plugin, EomWindow *window) +{ + g_return_if_fail (EOM_IS_PLUGIN (plugin)); + g_return_if_fail (EOM_IS_WINDOW (window)); + + EOM_PLUGIN_GET_CLASS (plugin)->deactivate (plugin, window); +} + +void +eom_plugin_update_ui (EomPlugin *plugin, EomWindow *window) +{ + g_return_if_fail (EOM_IS_PLUGIN (plugin)); + g_return_if_fail (EOM_IS_WINDOW (window)); + + EOM_PLUGIN_GET_CLASS (plugin)->update_ui (plugin, window); +} + +gboolean +eom_plugin_is_configurable (EomPlugin *plugin) +{ + g_return_val_if_fail (EOM_IS_PLUGIN (plugin), FALSE); + + return EOM_PLUGIN_GET_CLASS (plugin)->is_configurable (plugin); +} + +GtkWidget * +eom_plugin_create_configure_dialog (EomPlugin *plugin) +{ + g_return_val_if_fail (EOM_IS_PLUGIN (plugin), NULL); + + return EOM_PLUGIN_GET_CLASS (plugin)->create_configure_dialog (plugin); +} diff --git a/src/eom-plugin.h b/src/eom-plugin.h new file mode 100644 index 0000000..83151e9 --- /dev/null +++ b/src/eom-plugin.h @@ -0,0 +1,222 @@ +/* Eye Of Mate - EOM Plugin + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * - Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_PLUGIN_H__ +#define __EOM_PLUGIN_H__ + +#include "eom-window.h" +#include "eom-debug.h" + +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _EomPlugin EomPlugin; +typedef struct _EomPluginClass EomPluginClass; +typedef struct _EomPluginPrivate EomPluginPrivate; + +#define EOM_TYPE_PLUGIN (eom_plugin_get_type()) +#define EOM_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PLUGIN, EomPlugin)) +#define EOM_PLUGIN_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PLUGIN, EomPlugin const)) +#define EOM_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_PLUGIN, EomPluginClass)) +#define EOM_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_PLUGIN)) +#define EOM_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_PLUGIN)) +#define EOM_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_PL |