summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am239
-rw-r--r--src/Makefile.in1326
-rw-r--r--src/eom-application-service.xml23
-rw-r--r--src/eom-application.c566
-rw-r--r--src/eom-application.h118
-rw-r--r--src/eom-close-confirmation-dialog.c698
-rw-r--r--src/eom-close-confirmation-dialog.h79
-rw-r--r--src/eom-config-keys.h63
-rw-r--r--src/eom-debug.c160
-rw-r--r--src/eom-debug.h75
-rw-r--r--src/eom-dialog.c237
-rw-r--r--src/eom-dialog.h76
-rw-r--r--src/eom-enum-types.c.template39
-rw-r--r--src/eom-enum-types.h.template27
-rw-r--r--src/eom-enums.h37
-rw-r--r--src/eom-error-message-area.c192
-rw-r--r--src/eom-error-message-area.h37
-rw-r--r--src/eom-exif-details.c572
-rw-r--r--src/eom-exif-details.h72
-rw-r--r--src/eom-exif-util.c214
-rw-r--r--src/eom-exif-util.h41
-rw-r--r--src/eom-file-chooser.c497
-rw-r--r--src/eom-file-chooser.h60
-rw-r--r--src/eom-image-jpeg.c518
-rw-r--r--src/eom-image-jpeg.h22
-rw-r--r--src/eom-image-private.h96
-rw-r--r--src/eom-image-save-info.c150
-rw-r--r--src/eom-image-save-info.h54
-rw-r--r--src/eom-image.c2237
-rw-r--r--src/eom-image.h218
-rw-r--r--src/eom-job-queue.c238
-rw-r--r--src/eom-job-queue.h42
-rw-r--r--src/eom-jobs.c885
-rw-r--r--src/eom-jobs.h275
-rw-r--r--src/eom-list-store.c931
-rw-r--r--src/eom-list-store.h113
-rw-r--r--src/eom-marshal.list2
-rw-r--r--src/eom-metadata-reader-jpg.c673
-rw-r--r--src/eom-metadata-reader-jpg.h55
-rw-r--r--src/eom-metadata-reader-png.c648
-rw-r--r--src/eom-metadata-reader-png.h55
-rw-r--r--src/eom-metadata-reader.c150
-rw-r--r--src/eom-metadata-reader.h112
-rw-r--r--src/eom-module.c167
-rw-r--r--src/eom-module.h72
-rw-r--r--src/eom-pixbuf-util.c136
-rw-r--r--src/eom-pixbuf-util.h20
-rw-r--r--src/eom-plugin-engine.c943
-rw-r--r--src/eom-plugin-engine.h89
-rw-r--r--src/eom-plugin-manager.c917
-rw-r--r--src/eom-plugin-manager.h61
-rw-r--r--src/eom-plugin.c108
-rw-r--r--src/eom-plugin.h222
-rw-r--r--src/eom-preferences-dialog.c489
-rw-r--r--src/eom-preferences-dialog.h66
-rw-r--r--src/eom-print-image-setup.c1074
-rw-r--r--src/eom-print-image-setup.h71
-rw-r--r--src/eom-print-preview.c1226
-rw-r--r--src/eom-print-preview.h84
-rw-r--r--src/eom-print.c369
-rw-r--r--src/eom-print.h49
-rw-r--r--src/eom-properties-dialog.c834
-rw-r--r--src/eom-properties-dialog.h80
-rw-r--r--src/eom-python-module.c527
-rw-r--r--src/eom-python-module.h72
-rw-r--r--src/eom-python-plugin.c282
-rw-r--r--src/eom-python-plugin.h55
-rw-r--r--src/eom-save-as-dialog-helper.c311
-rw-r--r--src/eom-save-as-dialog-helper.h20
-rw-r--r--src/eom-scroll-view.c2633
-rw-r--r--src/eom-scroll-view.h73
-rw-r--r--src/eom-session.c52
-rw-r--r--src/eom-session.h46
-rw-r--r--src/eom-sidebar.c594
-rw-r--r--src/eom-sidebar.h82
-rw-r--r--src/eom-statusbar.c157
-rw-r--r--src/eom-statusbar.h71
-rw-r--r--src/eom-thumb-nav.c591
-rw-r--r--src/eom-thumb-nav.h79
-rw-r--r--src/eom-thumb-view.c918
-rw-r--r--src/eom-thumb-view.h87
-rw-r--r--src/eom-thumbnail.c509
-rw-r--r--src/eom-thumbnail.h48
-rw-r--r--src/eom-transform.c418
-rw-r--r--src/eom-transform.h75
-rw-r--r--src/eom-uri-converter.c988
-rw-r--r--src/eom-uri-converter.h107
-rw-r--r--src/eom-util.c348
-rw-r--r--src/eom-util.h72
-rw-r--r--src/eom-window.c5796
-rw-r--r--src/eom-window.h122
-rw-r--r--src/main.c270
-rw-r--r--src/uta.c1116
-rw-r--r--src/uta.h75
-rw-r--r--src/zoom.c118
-rw-r--r--src/zoom.h38
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/eom-@EOM_API_VERSION@/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)) > $@
+
+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)) > $@
+
+eom-marshal.h: eom-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --header --internal --prefix=eom_marshal > $@
+
+eom-marshal.c: eom-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --body --header --prefix=eom_marshal > $@
+
+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.
+# @configure_input@
+
+# 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.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = eom$(EXEEXT)
+@ENABLE_PYTHON_TRUE@am__append_1 = \
+@ENABLE_PYTHON_TRUE@ eom-python-module.h \
+@ENABLE_PYTHON_TRUE@ eom-python-plugin.h
+
+@HAVE_EXIF_TRUE@am__append_2 = \
+@HAVE_EXIF_TRUE@ eom-exif-util.h \
+@HAVE_EXIF_TRUE@ eom-exif-details.h
+
+@HAVE_EXIF_TRUE@am__append_3 = \
+@HAVE_EXIF_TRUE@ eom-exif-util.c \
+@HAVE_EXIF_TRUE@ eom-exif-details.c
+
+@ENABLE_PYTHON_TRUE@am__append_4 = \
+@ENABLE_PYTHON_TRUE@ eom-python-module.c \
+@ENABLE_PYTHON_TRUE@ eom-python-module.h \
+@ENABLE_PYTHON_TRUE@ eom-python-plugin.c \
+@ENABLE_PYTHON_TRUE@ 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
+@HAVE_EXEMPI_TRUE@@HAVE_EXIF_FALSE@am__append_5 = \
+@HAVE_EXEMPI_TRUE@@HAVE_EXIF_FALSE@ eom-exif-details.h
+
+@HAVE_EXEMPI_TRUE@am__append_6 = \
+@HAVE_EXEMPI_TRUE@ eom-exif-details.c
+
+@HAVE_LCMS_TRUE@am__append_7 = \
+@HAVE_LCMS_TRUE@ $(X11_CFLAGS)
+
+@HAVE_LCMS_TRUE@am__append_8 = \
+@HAVE_LCMS_TRUE@ $(X11_LIBS)
+
+@ENABLE_PYTHON_TRUE@am__append_9 = \
+@ENABLE_PYTHON_TRUE@ $(NO_STRICT_ALIASING_CFLAGS) \
+@ENABLE_PYTHON_TRUE@ $(PYGTK_CFLAGS) \
+@ENABLE_PYTHON_TRUE@ $(PYTHON_CFLAGS) \
+@ENABLE_PYTHON_TRUE@ $(AM_CFLAGS)
+
+@ENABLE_PYTHON_TRUE@am__append_10 = \
+@ENABLE_PYTHON_TRUE@ $(top_builddir)/bindings/python/eom.la
+
+@HAVE_DBUS_TRUE@am__append_11 = eom-application-service.h
+@HAVE_DBUS_TRUE@am__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 =
+@HAVE_LCMS_TRUE@am__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)
+@HAVE_EXIF_TRUE@am__objects_6 = libeom_la-eom-exif-util.lo \
+@HAVE_EXIF_TRUE@ libeom_la-eom-exif-details.lo
+@ENABLE_PYTHON_TRUE@am__objects_7 = libeom_la-eom-python-module.lo \
+@ENABLE_PYTHON_TRUE@ libeom_la-eom-python-plugin.lo
+@HAVE_EXEMPI_TRUE@am__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 $@
+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 $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -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 " $@;
+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 $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+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 = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_BINDING_TOOL = @DBUS_BINDING_TOOL@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DOC_USER_FORMATS = @DOC_USER_FORMATS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EOM_API_VERSION = @EOM_API_VERSION@
+EOM_CFLAGS = @EOM_CFLAGS@
+EOM_DOC_EXIF_START = @EOM_DOC_EXIF_START@
+EOM_DOC_EXIF_STOP = @EOM_DOC_EXIF_STOP@
+EOM_LIBS = @EOM_LIBS@
+EOM_MAJOR_VERSION = @EOM_MAJOR_VERSION@
+EOM_MICRO_VERSION = @EOM_MICRO_VERSION@
+EOM_MINOR_VERSION = @EOM_MINOR_VERSION@
+EXEEXT = @EXEEXT@
+EXEMPI_CFLAGS = @EXEMPI_CFLAGS@
+EXEMPI_LIBS = @EXEMPI_LIBS@
+EXIF_CFLAGS = @EXIF_CFLAGS@
+EXIF_LIBS = @EXIF_LIBS@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+HELP_DIR = @HELP_DIR@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBJPEG = @LIBJPEG@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATECONFTOOL = @MATECONFTOOL@
+MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@
+MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NO_STRICT_ALIASING_CFLAGS = @NO_STRICT_ALIASING_CFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OMF_DIR = @OMF_DIR@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYGOBJECT_CODEGEN = @PYGOBJECT_CODEGEN@
+PYGOBJECT_DEFSDIR = @PYGOBJECT_DEFSDIR@
+PYGOBJECT_H2DEF = @PYGOBJECT_H2DEF@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYTHON = @PYTHON@
+PYTHON_CFLAGS = @PYTHON_CFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LIBS = @PYTHON_LIBS@
+PYTHON_LIB_LOC = @PYTHON_LIB_LOC@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_CFLAGS = @RSVG_CFLAGS@
+RSVG_LIBS = @RSVG_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+X11_CFLAGS = @X11_CFLAGS@
+X11_LIBS = @X11_LIBS@
+XGETTEXT = @XGETTEXT@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@ENABLE_JPEG_TRUE@jpeg_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/eom-@EOM_API_VERSION@/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: @MAINTAINER_MODE_TRUE@ $(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 $@; 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)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(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: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+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
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eom-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-application.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-close-confirmation-dialog.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-debug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-dialog.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-enum-types.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-error-message-area.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-exif-details.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-exif-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-file-chooser.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-image-jpeg.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-image-save-info.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-image.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-job-queue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-jobs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-list-store.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-marshal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-metadata-reader-jpg.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-metadata-reader-png.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-metadata-reader.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-module.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-pixbuf-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-plugin-engine.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-plugin-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-plugin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-preferences-dialog.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-print-image-setup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-print-preview.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-print.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-properties-dialog.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-python-module.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-python-plugin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-save-as-dialog-helper.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-scroll-view.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-session.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-sidebar.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-statusbar.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-thumb-nav.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-thumb-view.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-thumbnail.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-transform.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-uri-converter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-eom-window.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-uta.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libeom_la-zoom.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libeom_la-eom-application.lo: eom-application.c
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-application.Tpo $(DEPDIR)/libeom_la-eom-application.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-application.c' object='libeom_la-eom-application.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-session.Tpo $(DEPDIR)/libeom_la-eom-session.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-session.c' object='libeom_la-eom-session.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-debug.Tpo $(DEPDIR)/libeom_la-eom-debug.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-debug.c' object='libeom_la-eom-debug.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-util.Tpo $(DEPDIR)/libeom_la-eom-util.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-util.c' object='libeom_la-eom-util.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-pixbuf-util.Tpo $(DEPDIR)/libeom_la-eom-pixbuf-util.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-pixbuf-util.c' object='libeom_la-eom-pixbuf-util.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-window.Tpo $(DEPDIR)/libeom_la-eom-window.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-window.c' object='libeom_la-eom-window.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-sidebar.Tpo $(DEPDIR)/libeom_la-eom-sidebar.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-sidebar.c' object='libeom_la-eom-sidebar.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-dialog.Tpo $(DEPDIR)/libeom_la-eom-dialog.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-dialog.c' object='libeom_la-eom-dialog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-preferences-dialog.Tpo $(DEPDIR)/libeom_la-eom-preferences-dialog.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-preferences-dialog.c' object='libeom_la-eom-preferences-dialog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-properties-dialog.Tpo $(DEPDIR)/libeom_la-eom-properties-dialog.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-properties-dialog.c' object='libeom_la-eom-properties-dialog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-error-message-area.Tpo $(DEPDIR)/libeom_la-eom-error-message-area.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-error-message-area.c' object='libeom_la-eom-error-message-area.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-file-chooser.Tpo $(DEPDIR)/libeom_la-eom-file-chooser.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-file-chooser.c' object='libeom_la-eom-file-chooser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-statusbar.Tpo $(DEPDIR)/libeom_la-eom-statusbar.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-statusbar.c' object='libeom_la-eom-statusbar.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-thumb-nav.Tpo $(DEPDIR)/libeom_la-eom-thumb-nav.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-thumb-nav.c' object='libeom_la-eom-thumb-nav.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-transform.Tpo $(DEPDIR)/libeom_la-eom-transform.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-transform.c' object='libeom_la-eom-transform.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-image.Tpo $(DEPDIR)/libeom_la-eom-image.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-image.c' object='libeom_la-eom-image.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-image-jpeg.Tpo $(DEPDIR)/libeom_la-eom-image-jpeg.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-image-jpeg.c' object='libeom_la-eom-image-jpeg.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-image-save-info.Tpo $(DEPDIR)/libeom_la-eom-image-save-info.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-image-save-info.c' object='libeom_la-eom-image-save-info.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-scroll-view.Tpo $(DEPDIR)/libeom_la-eom-scroll-view.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-scroll-view.c' object='libeom_la-eom-scroll-view.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-thumb-view.Tpo $(DEPDIR)/libeom_la-eom-thumb-view.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-thumb-view.c' object='libeom_la-eom-thumb-view.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-list-store.Tpo $(DEPDIR)/libeom_la-eom-list-store.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-list-store.c' object='libeom_la-eom-list-store.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-thumbnail.Tpo $(DEPDIR)/libeom_la-eom-thumbnail.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-thumbnail.c' object='libeom_la-eom-thumbnail.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-job-queue.Tpo $(DEPDIR)/libeom_la-eom-job-queue.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-job-queue.c' object='libeom_la-eom-job-queue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-jobs.Tpo $(DEPDIR)/libeom_la-eom-jobs.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-jobs.c' object='libeom_la-eom-jobs.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-uri-converter.Tpo $(DEPDIR)/libeom_la-eom-uri-converter.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-uri-converter.c' object='libeom_la-eom-uri-converter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-metadata-reader.Tpo $(DEPDIR)/libeom_la-eom-metadata-reader.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-metadata-reader.c' object='libeom_la-eom-metadata-reader.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-metadata-reader-jpg.Tpo $(DEPDIR)/libeom_la-eom-metadata-reader-jpg.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-metadata-reader-jpg.c' object='libeom_la-eom-metadata-reader-jpg.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-metadata-reader-png.Tpo $(DEPDIR)/libeom_la-eom-metadata-reader-png.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-metadata-reader-png.c' object='libeom_la-eom-metadata-reader-png.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-save-as-dialog-helper.Tpo $(DEPDIR)/libeom_la-eom-save-as-dialog-helper.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-save-as-dialog-helper.c' object='libeom_la-eom-save-as-dialog-helper.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-print-image-setup.Tpo $(DEPDIR)/libeom_la-eom-print-image-setup.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-print-image-setup.c' object='libeom_la-eom-print-image-setup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-print-preview.Tpo $(DEPDIR)/libeom_la-eom-print-preview.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-print-preview.c' object='libeom_la-eom-print-preview.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-print.Tpo $(DEPDIR)/libeom_la-eom-print.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-print.c' object='libeom_la-eom-print.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-module.Tpo $(DEPDIR)/libeom_la-eom-module.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-module.c' object='libeom_la-eom-module.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-close-confirmation-dialog.Tpo $(DEPDIR)/libeom_la-eom-close-confirmation-dialog.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-close-confirmation-dialog.c' object='libeom_la-eom-close-confirmation-dialog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-plugin.Tpo $(DEPDIR)/libeom_la-eom-plugin.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-plugin.c' object='libeom_la-eom-plugin.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-plugin-manager.Tpo $(DEPDIR)/libeom_la-eom-plugin-manager.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-plugin-manager.c' object='libeom_la-eom-plugin-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-plugin-engine.Tpo $(DEPDIR)/libeom_la-eom-plugin-engine.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-plugin-engine.c' object='libeom_la-eom-plugin-engine.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-uta.Tpo $(DEPDIR)/libeom_la-uta.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='uta.c' object='libeom_la-uta.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-zoom.Tpo $(DEPDIR)/libeom_la-zoom.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zoom.c' object='libeom_la-zoom.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-enum-types.Tpo $(DEPDIR)/libeom_la-eom-enum-types.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-enum-types.c' object='libeom_la-eom-enum-types.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-marshal.Tpo $(DEPDIR)/libeom_la-eom-marshal.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-marshal.c' object='libeom_la-eom-marshal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-exif-util.Tpo $(DEPDIR)/libeom_la-eom-exif-util.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-exif-util.c' object='libeom_la-eom-exif-util.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-exif-details.Tpo $(DEPDIR)/libeom_la-eom-exif-details.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-exif-details.c' object='libeom_la-eom-exif-details.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-python-module.Tpo $(DEPDIR)/libeom_la-eom-python-module.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-python-module.c' object='libeom_la-eom-python-module.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libeom_la-eom-python-plugin.Tpo $(DEPDIR)/libeom_la-eom-python-plugin.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eom-python-plugin.c' object='libeom_la-eom-python-plugin.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/eom-main.Tpo $(DEPDIR)/eom-main.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='eom-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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
+@am__fastdepCC_TRUE@ $(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`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/eom-main.Tpo $(DEPDIR)/eom-main.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='main.c' object='eom-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(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) \
+ "$$@" $$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)) > $@
+
+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)) > $@
+
+eom-marshal.h: eom-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --header --internal --prefix=eom_marshal > $@
+
+eom-marshal.c: eom-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --body --header --prefix=eom_marshal > $@
+
+@[email protected]: eom-application-service.xml
+@HAVE_DBUS_TRUE@ $(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 (&nothumb_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 "@filename@" */
+#include "@filename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type (void)
+{
+ static GType the_type = 0;
+
+ if (the_type == 0)
+ {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@,
+ "@VALUENAME@",
+ "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ the_type = g_@type@_register_static (
+ g_intern_static_string ("@EnumName@"),
+ 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 "@filename@" */
+
+/*** END file-production ***/
+
+/*** BEGIN enumeration-production ***/
+#define EOM_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+G_GNUC_INTERNAL GType @enum_name@_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 *) &register_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_PLUGIN, EomPluginClass))
+
+struct _EomPlugin {
+ GObject parent;
+};
+
+struct _EomPluginClass {
+ GObjectClass parent_class;
+
+ void (*activate) (EomPlugin *plugin,
+ EomWindow *window);
+
+ void (*deactivate) (EomPlugin *plugin,
+ EomWindow *window);
+
+ void (*update_ui) (EomPlugin *plugin,
+ EomWindow *window);
+
+ GtkWidget *(*create_configure_dialog)
+ (EomPlugin *plugin);
+
+ /* Plugins should not override this, it's handled automatically
+ * by the EomPluginClass */
+ gboolean (*is_configurable)
+ (EomPlugin *plugin);
+
+ /* Padding for future expansion */
+ void (*_eom_reserved1) (void);
+ void (*_eom_reserved2) (void);
+ void (*_eom_reserved3) (void);
+ void (*_eom_reserved4) (void);
+};
+
+GType eom_plugin_get_type (void) G_GNUC_CONST;
+
+void eom_plugin_activate (EomPlugin *plugin,
+ EomWindow *window);
+
+void eom_plugin_deactivate (EomPlugin *plugin,
+ EomWindow *window);
+
+void eom_plugin_update_ui (EomPlugin *plugin,
+ EomWindow *window);
+
+gboolean eom_plugin_is_configurable (EomPlugin *plugin);
+
+GtkWidget *eom_plugin_create_configure_dialog
+ (EomPlugin *plugin);
+
+/*
+ * Utility macro used to register plugins
+ *
+ * use: EOM_PLUGIN_REGISTER_TYPE_WITH_CODE(PluginName, plugin_name, CODE)
+ */
+#define EOM_PLUGIN_REGISTER_TYPE_WITH_CODE(PluginName, plugin_name, CODE) \
+ \
+static GType plugin_name##_type = 0; \
+ \
+GType \
+plugin_name##_get_type (void) \
+{ \
+ return plugin_name##_type; \
+} \
+ \
+static void plugin_name##_init (PluginName *self); \
+static void plugin_name##_class_init (PluginName##Class *klass); \
+static gpointer plugin_name##_parent_class = NULL; \
+static void plugin_name##_class_intern_init (gpointer klass) \
+{ \
+ plugin_name##_parent_class = g_type_class_peek_parent (klass); \
+ plugin_name##_class_init ((PluginName##Class *) klass); \
+} \
+ \
+G_MODULE_EXPORT GType \
+register_eom_plugin (GTypeModule *module) \
+{ \
+ static const GTypeInfo our_info = \
+ { \
+ sizeof (PluginName##Class), \
+ NULL, /* base_init */ \
+ NULL, /* base_finalize */ \
+ (GClassInitFunc) plugin_name##_class_intern_init, \
+ NULL, \
+ NULL, /* class_data */ \
+ sizeof (PluginName), \
+ 0, /* n_preallocs */ \
+ (GInstanceInitFunc) plugin_name##_init \
+ }; \
+ \
+ eom_debug_message (DEBUG_PLUGINS, "Registering " #PluginName); \
+ \
+ /* Initialise the i18n stuff */ \
+ bindtextdomain (GETTEXT_PACKAGE, EOM_LOCALEDIR); \
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); \
+ \
+ plugin_name##_type = g_type_module_register_type (module, \
+ EOM_TYPE_PLUGIN, \
+ #PluginName, \
+ &our_info, \
+ 0); \
+ \
+ CODE \
+ \
+ return plugin_name##_type; \
+}
+
+/*
+ * Utility macro used to register plugins
+ *
+ * use: EOM_PLUGIN_REGISTER_TYPE(PluginName, plugin_name)
+ */
+#define EOM_PLUGIN_REGISTER_TYPE(PluginName, plugin_name) \
+ EOM_PLUGIN_REGISTER_TYPE_WITH_CODE(PluginName, plugin_name, ;)
+
+/*
+ * Utility macro used to register gobject types in plugins with additional code
+ *
+ * use: EOM_PLUGIN_DEFINE_TYPE_WITH_CODE(ObjectName, object_name, PARENT_TYPE, CODE)
+ */
+#define EOM_PLUGIN_DEFINE_TYPE_WITH_CODE(ObjectName, object_name, PARENT_TYPE, CODE) \
+ \
+static GType g_define_type_id = 0; \
+ \
+GType \
+object_name##_get_type (void) \
+{ \
+ return g_define_type_id; \
+} \
+ \
+static void object_name##_init (ObjectName *self); \
+static void object_name##_class_init (ObjectName##Class *klass); \
+static gpointer object_name##_parent_class = NULL; \
+static void object_name##_class_intern_init (gpointer klass) \
+{ \
+ object_name##_parent_class = g_type_class_peek_parent (klass); \
+ object_name##_class_init ((ObjectName##Class *) klass); \
+} \
+ \
+GType \
+object_name##_register_type (GTypeModule *module) \
+{ \
+ static const GTypeInfo our_info = \
+ { \
+ sizeof (ObjectName##Class), \
+ NULL, /* base_init */ \
+ NULL, /* base_finalize */ \
+ (GClassInitFunc) object_name##_class_intern_init, \
+ NULL, \
+ NULL, /* class_data */ \
+ sizeof (ObjectName), \
+ 0, /* n_preallocs */ \
+ (GInstanceInitFunc) object_name##_init \
+ }; \
+ \
+ eom_debug_message (DEBUG_PLUGINS, "Registering " #ObjectName); \
+ \
+ g_define_type_id = g_type_module_register_type (module, \
+ PARENT_TYPE, \
+ #ObjectName, \
+ &our_info, \
+ 0); \
+ \
+ CODE \
+ \
+ return g_define_type_id; \
+}
+
+/*
+ * Utility macro used to register gobject types in plugins
+ *
+ * use: EOM_PLUGIN_DEFINE_TYPE(ObjectName, object_name, PARENT_TYPE)
+ */
+#define EOM_PLUGIN_DEFINE_TYPE(ObjectName, object_name, PARENT_TYPE) \
+ EOM_PLUGIN_DEFINE_TYPE_WITH_CODE(ObjectName, object_name, PARENT_TYPE, ;)
+
+G_END_DECLS
+
+#endif /* __EOM_PLUGIN_H__ */
diff --git a/src/eom-preferences-dialog.c b/src/eom-preferences-dialog.c
new file mode 100644
index 0000000..52a9772
--- /dev/null
+++ b/src/eom-preferences-dialog.c
@@ -0,0 +1,489 @@
+/* Eye Of Mate - EOM Preferences Dialog
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[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
+
+#include "eom-preferences-dialog.h"
+#include "eom-plugin-manager.h"
+#include "eom-util.h"
+#include "eom-config-keys.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#define EOM_PREFERENCES_DIALOG_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PREFERENCES_DIALOG, EomPreferencesDialogPrivate))
+
+G_DEFINE_TYPE (EomPreferencesDialog, eom_preferences_dialog, EOM_TYPE_DIALOG);
+
+enum {
+ PROP_0,
+ PROP_MATECONF_CLIENT,
+};
+
+#define MATECONF_OBJECT_KEY "MATECONF_KEY"
+#define MATECONF_OBJECT_VALUE "MATECONF_VALUE"
+#define TOGGLE_INVERT_VALUE "TOGGLE_INVERT_VALUE"
+
+struct _EomPreferencesDialogPrivate {
+ MateConfClient *client;
+};
+
+static GObject *instance = NULL;
+
+static void
+pd_check_toggle_cb (GtkWidget *widget, gpointer data)
+{
+ char *key = NULL;
+ gboolean invert = FALSE;
+ gboolean value;
+
+ key = g_object_get_data (G_OBJECT (widget), MATECONF_OBJECT_KEY);
+ invert = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), TOGGLE_INVERT_VALUE));
+
+ value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if (key == NULL) return;
+
+ mateconf_client_set_bool (MATECONF_CLIENT (data),
+ key,
+ (invert) ? !value : value,
+ NULL);
+}
+
+static void
+pd_spin_button_changed_cb (GtkWidget *widget, gpointer data)
+{
+ char *key = NULL;
+
+ key = g_object_get_data (G_OBJECT (widget), MATECONF_OBJECT_KEY);
+
+ if (key == NULL) return;
+
+ mateconf_client_set_int (MATECONF_CLIENT (data),
+ key,
+ gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)),
+ NULL);
+}
+
+static void
+pd_color_change_cb (GtkColorButton *button, gpointer data)
+{
+ GdkColor color;
+ char *key = NULL;
+ char *value = NULL;
+
+ gtk_color_button_get_color (button, &color);
+
+ value = g_strdup_printf ("#%02X%02X%02X",
+ color.red / 256,
+ color.green / 256,
+ color.blue / 256);
+
+ key = g_object_get_data (G_OBJECT (button), MATECONF_OBJECT_KEY);
+
+ if (key == NULL || value == NULL)
+ return;
+
+ mateconf_client_set_string (MATECONF_CLIENT (data),
+ key,
+ value,
+ NULL);
+ g_free (value);
+}
+
+static void
+pd_radio_toggle_cb (GtkWidget *widget, gpointer data)
+{
+ char *key = NULL;
+ char *value = NULL;
+
+ if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ return;
+
+ key = g_object_get_data (G_OBJECT (widget), MATECONF_OBJECT_KEY);
+ value = g_object_get_data (G_OBJECT (widget), MATECONF_OBJECT_VALUE);
+
+ if (key == NULL || value == NULL)
+ return;
+
+ mateconf_client_set_string (MATECONF_CLIENT (data),
+ key,
+ value,
+ NULL);
+}
+
+static void
+eom_preferences_response_cb (GtkDialog *dlg, gint res_id, gpointer data)
+{
+ switch (res_id) {
+ case GTK_RESPONSE_HELP:
+ eom_util_show_help ("eom-prefs", NULL);
+ break;
+ default:
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+ instance = NULL;
+ }
+}
+
+static void
+eom_preferences_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomPreferencesDialog *pref_dlg = EOM_PREFERENCES_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_MATECONF_CLIENT:
+ pref_dlg->priv->client = g_value_get_object (value);
+ break;
+ }
+}
+
+static void
+eom_preferences_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomPreferencesDialog *pref_dlg = EOM_PREFERENCES_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_MATECONF_CLIENT:
+ g_value_set_object (value, pref_dlg->priv->client);
+ break;
+ }
+}
+
+static GObject *
+eom_preferences_dialog_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+
+{
+ EomPreferencesDialogPrivate *priv;
+ GtkWidget *dlg;
+ GtkWidget *interpolate_check;
+ GtkWidget *extrapolate_check;
+ GtkWidget *autorotate_check;
+ GtkWidget *bg_color_check;
+ GtkWidget *bg_color_button;
+ GtkWidget *color_radio;
+ GtkWidget *checkpattern_radio;
+ GtkWidget *background_radio;
+ GtkWidget *color_button;
+ GtkWidget *upscale_check;
+ GtkWidget *loop_check;
+ GtkWidget *seconds_spin;
+ GtkWidget *plugin_manager;
+ GtkWidget *plugin_manager_container;
+ GObject *object;
+ GdkColor color;
+ gchar *value;
+
+ object = G_OBJECT_CLASS (eom_preferences_dialog_parent_class)->constructor
+ (type, n_construct_properties, construct_params);
+
+ priv = EOM_PREFERENCES_DIALOG (object)->priv;
+
+ eom_dialog_construct (EOM_DIALOG (object),
+ "eom-preferences-dialog.ui",
+ "eom_preferences_dialog");
+
+ eom_dialog_get_controls (EOM_DIALOG (object),
+ "eom_preferences_dialog", &dlg,
+ "interpolate_check", &interpolate_check,
+ "extrapolate_check", &extrapolate_check,
+ "autorotate_check", &autorotate_check,
+ "bg_color_check", &bg_color_check,
+ "bg_color_button", &bg_color_button,
+ "color_radio", &color_radio,
+ "checkpattern_radio", &checkpattern_radio,
+ "background_radio", &background_radio,
+ "color_button", &color_button,
+ "upscale_check", &upscale_check,
+ "loop_check", &loop_check,
+ "seconds_spin", &seconds_spin,
+ "plugin_manager_container", &plugin_manager_container,
+ NULL);
+
+ g_signal_connect (G_OBJECT (dlg),
+ "response",
+ G_CALLBACK (eom_preferences_response_cb),
+ dlg);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (interpolate_check),
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_VIEW_INTERPOLATE,
+ NULL));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extrapolate_check),
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_VIEW_EXTRAPOLATE,
+ NULL));
+
+ g_object_set_data (G_OBJECT (interpolate_check),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_INTERPOLATE);
+
+ g_object_set_data (G_OBJECT (extrapolate_check),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_EXTRAPOLATE);
+
+ g_signal_connect (G_OBJECT (interpolate_check),
+ "toggled",
+ G_CALLBACK (pd_check_toggle_cb),
+ priv->client);
+
+ g_signal_connect (G_OBJECT (extrapolate_check),
+ "toggled",
+ G_CALLBACK (pd_check_toggle_cb),
+ priv->client);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (autorotate_check),
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_VIEW_AUTOROTATE,
+ NULL));
+
+ g_object_set_data (G_OBJECT (autorotate_check),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_AUTOROTATE);
+
+ g_signal_connect (G_OBJECT (autorotate_check),
+ "toggled",
+ G_CALLBACK (pd_check_toggle_cb),
+ priv->client);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bg_color_check),
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_VIEW_USE_BG_COLOR, NULL));
+ g_object_set_data (G_OBJECT (bg_color_check),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_USE_BG_COLOR);
+ g_signal_connect (G_OBJECT (bg_color_check),
+ "toggled", G_CALLBACK (pd_check_toggle_cb),
+ priv->client);
+
+ value = mateconf_client_get_string (priv->client,
+ EOM_CONF_VIEW_BACKGROUND_COLOR,
+ NULL);
+ if (gdk_color_parse (value, &color)){
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (bg_color_button),
+ &color);
+ }
+ g_free (value);
+
+ g_object_set_data (G_OBJECT (bg_color_button),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_BACKGROUND_COLOR);
+
+ g_signal_connect (G_OBJECT (bg_color_button),
+ "color-set",
+ G_CALLBACK (pd_color_change_cb),
+ priv->client);
+
+
+
+ g_object_set_data (G_OBJECT (color_radio),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_TRANSPARENCY);
+
+ g_object_set_data (G_OBJECT (color_radio),
+ MATECONF_OBJECT_VALUE,
+ "COLOR");
+
+ g_signal_connect (G_OBJECT (color_radio),
+ "toggled",
+ G_CALLBACK (pd_radio_toggle_cb),
+ priv->client);
+
+ g_object_set_data (G_OBJECT (checkpattern_radio),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_TRANSPARENCY);
+
+ g_object_set_data (G_OBJECT (checkpattern_radio),
+ MATECONF_OBJECT_VALUE,
+ "CHECK_PATTERN");
+
+ g_signal_connect (G_OBJECT (checkpattern_radio),
+ "toggled",
+ G_CALLBACK (pd_radio_toggle_cb),
+ priv->client);
+
+ g_object_set_data (G_OBJECT (background_radio),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_TRANSPARENCY);
+
+ g_object_set_data (G_OBJECT (background_radio),
+ MATECONF_OBJECT_VALUE,
+ "NONE");
+
+ g_signal_connect (G_OBJECT (background_radio),
+ "toggled",
+ G_CALLBACK (pd_radio_toggle_cb),
+ priv->client);
+
+ value = mateconf_client_get_string (priv->client,
+ EOM_CONF_VIEW_TRANSPARENCY,
+ NULL);
+
+ if (g_ascii_strcasecmp (value, "COLOR") == 0) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (color_radio), TRUE);
+ }
+ else if (g_ascii_strcasecmp (value, "CHECK_PATTERN") == 0) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkpattern_radio), TRUE);
+ }
+ else {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (background_radio), TRUE);
+ }
+
+ g_free (value);
+
+ value = mateconf_client_get_string (priv->client,
+ EOM_CONF_VIEW_TRANS_COLOR,
+ NULL);
+
+ if (gdk_color_parse (value, &color)) {
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (color_button),
+ &color);
+ }
+
+ g_object_set_data (G_OBJECT (color_button),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_VIEW_TRANS_COLOR);
+
+ g_signal_connect (G_OBJECT (color_button),
+ "color-set",
+ G_CALLBACK (pd_color_change_cb),
+ priv->client);
+
+ g_free (value);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (upscale_check),
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_FULLSCREEN_UPSCALE,
+ NULL));
+
+ g_object_set_data (G_OBJECT (upscale_check),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_FULLSCREEN_UPSCALE);
+
+ g_signal_connect (G_OBJECT (upscale_check),
+ "toggled",
+ G_CALLBACK (pd_check_toggle_cb),
+ priv->client);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (loop_check),
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_FULLSCREEN_LOOP,
+ NULL));
+
+ g_object_set_data (G_OBJECT (loop_check),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_FULLSCREEN_LOOP);
+
+ g_signal_connect (G_OBJECT (loop_check),
+ "toggled",
+ G_CALLBACK (pd_check_toggle_cb),
+ priv->client);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (seconds_spin),
+ mateconf_client_get_int (priv->client,
+ EOM_CONF_FULLSCREEN_SECONDS,
+ NULL));
+
+ g_object_set_data (G_OBJECT (seconds_spin),
+ MATECONF_OBJECT_KEY,
+ EOM_CONF_FULLSCREEN_SECONDS);
+
+ g_signal_connect (G_OBJECT (seconds_spin),
+ "value-changed",
+ G_CALLBACK (pd_spin_button_changed_cb),
+ priv->client);
+
+ plugin_manager = eom_plugin_manager_new ();
+
+ g_assert (plugin_manager != NULL);
+
+ gtk_box_pack_start (GTK_BOX (plugin_manager_container),
+ plugin_manager,
+ TRUE,
+ TRUE,
+ 0);
+
+ gtk_widget_show_all (plugin_manager);
+
+ return object;
+}
+
+static void
+eom_preferences_dialog_class_init (EomPreferencesDialogClass *class)
+{
+ GObjectClass *g_object_class = (GObjectClass *) class;
+
+ g_object_class->constructor = eom_preferences_dialog_constructor;
+ g_object_class->set_property = eom_preferences_dialog_set_property;
+ g_object_class->get_property = eom_preferences_dialog_get_property;
+
+ g_object_class_install_property (g_object_class,
+ PROP_MATECONF_CLIENT,
+ g_param_spec_object ("mateconf-client",
+ "MateConf Client",
+ "MateConf Client",
+ MATECONF_TYPE_CLIENT,
+ 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 (EomPreferencesDialogPrivate));
+}
+
+static void
+eom_preferences_dialog_init (EomPreferencesDialog *pref_dlg)
+{
+ pref_dlg->priv = EOM_PREFERENCES_DIALOG_GET_PRIVATE (pref_dlg);
+
+ pref_dlg->priv->client = NULL;
+}
+
+GObject *
+eom_preferences_dialog_get_instance (GtkWindow *parent, MateConfClient *client)
+{
+ if (instance == NULL) {
+ instance = g_object_new (EOM_TYPE_PREFERENCES_DIALOG,
+ "parent-window", parent,
+ "mateconf-client", client,
+ NULL);
+ }
+
+ return instance;
+}
diff --git a/src/eom-preferences-dialog.h b/src/eom-preferences-dialog.h
new file mode 100644
index 0000000..7a05f67
--- /dev/null
+++ b/src/eom-preferences-dialog.h
@@ -0,0 +1,66 @@
+/* Eye Of Mate - EOM Preferences 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_PREFERENCES_DIALOG_H__
+#define __EOM_PREFERENCES_DIALOG_H__
+
+#include "eom-dialog.h"
+#include "eom-image.h"
+#include "eom-thumb-view.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EomPreferencesDialog EomPreferencesDialog;
+typedef struct _EomPreferencesDialogClass EomPreferencesDialogClass;
+typedef struct _EomPreferencesDialogPrivate EomPreferencesDialogPrivate;
+
+#define EOM_TYPE_PREFERENCES_DIALOG (eom_preferences_dialog_get_type ())
+#define EOM_PREFERENCES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PREFERENCES_DIALOG, EomPreferencesDialog))
+#define EOM_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_PREFERENCES_DIALOG, EomPreferencesDialogClass))
+#define EOM_IS_PREFERENCES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_PREFERENCES_DIALOG))
+#define EOM_IS_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_PREFERENCES_DIALOG))
+#define EOM_PREFERENCES_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_PREFERENCES_DIALOG, EomPreferencesDialogClass))
+
+struct _EomPreferencesDialog {
+ EomDialog dialog;
+
+ EomPreferencesDialogPrivate *priv;
+};
+
+struct _EomPreferencesDialogClass {
+ EomDialogClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType eom_preferences_dialog_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GObject *eom_preferences_dialog_get_instance (GtkWindow *parent,
+ MateConfClient *client);
+
+G_END_DECLS
+
+#endif /* __EOM_PREFERENCES_DIALOG_H__ */
diff --git a/src/eom-print-image-setup.c b/src/eom-print-image-setup.c
new file mode 100644
index 0000000..100d7c9
--- /dev/null
+++ b/src/eom-print-image-setup.c
@@ -0,0 +1,1074 @@
+/* Eye Of MATE -- Print Dialog Custom Widget
+ *
+ * Copyright (C) 2006-2007 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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 <gtk/gtk.h>
+#include <gtk/gtkunixprint.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+
+#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
+#include <langinfo.h>
+#endif
+
+#include "eom-print-image-setup.h"
+#include "eom-print-preview.h"
+
+/**
+ * SECTION:
+ * @Title: Printing Custom Widget
+ * @Short_Description: a custom widget to setup image prints.
+ * @Stability_Level: Internal
+ * @See_Also: #EomPrintPreview
+ *
+ * This widget is to be used as the custom widget in a #GtkPrintUnixDialog in
+ * EOM. Through it, you can set the position and scaling of a image
+ * interactively.
+ */
+
+#define EOM_PRINT_IMAGE_SETUP_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PRINT_IMAGE_SETUP, EomPrintImageSetupPrivate))
+
+G_DEFINE_TYPE (EomPrintImageSetup, eom_print_image_setup, GTK_TYPE_TABLE);
+
+struct EomPrintImageSetupPrivate {
+ GtkWidget *left;
+ GtkWidget *right;
+ GtkWidget *top;
+ GtkWidget *bottom;
+
+ GtkWidget *center;
+
+ GtkWidget *width;
+ GtkWidget *height;
+
+ GtkWidget *scaling;
+ GtkWidget *unit;
+
+ GtkUnit current_unit;
+
+ EomImage *image;
+ GtkPageSetup *page_setup;
+
+ GtkWidget *preview;
+};
+
+enum {
+ PROP_0,
+ PROP_IMAGE,
+ PROP_PAGE_SETUP
+};
+
+enum {
+ CENTER_NONE,
+ CENTER_HORIZONTAL,
+ CENTER_VERTICAL,
+ CENTER_BOTH
+};
+
+enum {
+ CHANGE_HORIZ,
+ CHANGE_VERT
+};
+
+enum {
+ UNIT_INCH,
+ UNIT_MM
+};
+
+#define FACTOR_INCH_TO_MM 25.4
+#define FACTOR_INCH_TO_PIXEL 72.
+#define FACTOR_MM_TO_INCH 0.03937007874015748
+#define FACTOR_MM_TO_PIXEL 2.834645669
+
+static void eom_print_image_setup_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void eom_print_image_setup_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+
+static void on_left_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
+static void on_right_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
+static void on_top_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
+static void on_bottom_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
+
+static void on_width_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
+static void on_height_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
+
+
+static void
+block_handlers (EomPrintImageSetup *setup)
+{
+ EomPrintImageSetupPrivate *priv = setup->priv;
+
+ g_signal_handlers_block_by_func (priv->left, on_left_value_changed, setup);
+ g_signal_handlers_block_by_func (priv->right, on_right_value_changed, setup);
+ g_signal_handlers_block_by_func (priv->width, on_width_value_changed, setup);
+ g_signal_handlers_block_by_func (priv->top, on_top_value_changed, setup);
+ g_signal_handlers_block_by_func (priv->bottom, on_bottom_value_changed, setup);
+ g_signal_handlers_block_by_func (priv->height, on_height_value_changed, setup);
+}
+
+static void
+unblock_handlers (EomPrintImageSetup *setup)
+{
+ EomPrintImageSetupPrivate *priv = setup->priv;
+
+ g_signal_handlers_unblock_by_func (priv->left, on_left_value_changed, setup);
+ g_signal_handlers_unblock_by_func (priv->right, on_right_value_changed, setup);
+ g_signal_handlers_unblock_by_func (priv->width, on_width_value_changed, setup);
+ g_signal_handlers_unblock_by_func (priv->top, on_top_value_changed, setup);
+ g_signal_handlers_unblock_by_func (priv->bottom, on_bottom_value_changed, setup);
+ g_signal_handlers_unblock_by_func (priv->height, on_height_value_changed, setup);
+}
+
+static gdouble
+get_scale_to_px_factor (EomPrintImageSetup *setup)
+{
+ gdouble factor = 0.;
+
+ switch (setup->priv->current_unit) {
+ case GTK_UNIT_MM:
+ factor = FACTOR_MM_TO_PIXEL;
+ break;
+ case GTK_UNIT_INCH:
+ factor = FACTOR_INCH_TO_PIXEL;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return factor;
+}
+
+static gdouble
+get_max_percentage (EomPrintImageSetup *setup)
+{
+ EomPrintImageSetupPrivate *priv = setup->priv;
+ gdouble p_width, p_height;
+ gdouble width, height;
+ gint pix_width, pix_height;
+ gdouble perc;
+
+ p_width = gtk_page_setup_get_page_width (priv->page_setup, GTK_UNIT_INCH);
+ p_height = gtk_page_setup_get_page_height (priv->page_setup, GTK_UNIT_INCH);
+
+ eom_image_get_size (priv->image, &pix_width, &pix_height);
+
+ width = (gdouble)pix_width/FACTOR_INCH_TO_PIXEL;
+ height = (gdouble)pix_height/FACTOR_INCH_TO_PIXEL;
+
+ if (p_width > width && p_height > height) {
+ perc = 1.;
+ } else {
+ perc = MIN (p_width/width, p_height/height);
+ }
+
+ return perc;
+}
+
+static void
+center (gdouble page_width,
+ gdouble width,
+ GtkSpinButton *s_left,
+ GtkSpinButton *s_right)
+{
+ gdouble left, right;
+
+ left = (page_width - width)/2;
+ right = page_width - left - width;
+ gtk_spin_button_set_value (s_left, left);
+ gtk_spin_button_set_value (s_right, right);
+}
+
+static void
+on_center_changed (GtkComboBox *combobox,
+ gpointer user_data)
+{
+ EomPrintImageSetup *setup;
+ EomPrintImageSetupPrivate *priv;
+ gint active;
+
+ setup = EOM_PRINT_IMAGE_SETUP (user_data);
+ priv = setup->priv;
+
+ active = gtk_combo_box_get_active (combobox);
+
+ switch (active) {
+ case CENTER_HORIZONTAL:
+ center (gtk_page_setup_get_page_width (priv->page_setup,
+ priv->current_unit),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->width)),
+ GTK_SPIN_BUTTON (priv->left),
+ GTK_SPIN_BUTTON (priv->right));
+ break;
+ case CENTER_VERTICAL:
+ center (gtk_page_setup_get_page_height (priv->page_setup,
+ priv->current_unit),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->height)),
+ GTK_SPIN_BUTTON (priv->top),
+ GTK_SPIN_BUTTON (priv->bottom));
+ break;
+ case CENTER_BOTH:
+ center (gtk_page_setup_get_page_width (priv->page_setup,
+ priv->current_unit),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->width)),
+ GTK_SPIN_BUTTON (priv->left),
+ GTK_SPIN_BUTTON (priv->right));
+ center (gtk_page_setup_get_page_height (priv->page_setup,
+ priv->current_unit),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->height)),
+ GTK_SPIN_BUTTON (priv->top),
+ GTK_SPIN_BUTTON (priv->bottom));
+ break;
+ case CENTER_NONE:
+ default:
+ break;
+ }
+
+ gtk_combo_box_set_active (combobox, active);
+}
+
+static void
+update_image_pos_ranges (EomPrintImageSetup *setup,
+ gdouble page_width,
+ gdouble page_height,
+ gdouble width,
+ gdouble height)
+{
+ EomPrintImageSetupPrivate *priv;
+
+ priv = setup->priv;
+
+ gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->left),
+ 0, page_width - width);
+ gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->right),
+ 0, page_width - width);
+ gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->top),
+ 0, page_height - height);
+ gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->bottom),
+ 0, page_height - height);
+}
+
+static gboolean
+on_scale_changed (GtkRange *range,
+ gpointer user_data)
+{
+ gdouble scale;
+ gdouble width, height;
+ gint pix_width, pix_height;
+ gdouble left, right, top, bottom;
+ gdouble page_width, page_height;
+ EomPrintImageSetupPrivate *priv;
+ EomPrintImageSetup *setup;
+ gdouble factor;
+ EomImage *image;
+
+ setup = EOM_PRINT_IMAGE_SETUP (user_data);
+ priv = setup->priv;
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (priv->center), CENTER_NONE);
+
+ image = priv->image;
+ eom_image_get_size (image, &pix_width, &pix_height);
+
+ factor = get_scale_to_px_factor (setup);
+
+ width = (gdouble)pix_width/factor;
+ height = (gdouble)pix_height/factor;
+
+ left = gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->left));
+ top = gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->top));
+
+ scale = CLAMP (0.01*gtk_range_get_value (range), 0, get_max_percentage (setup));
+
+ eom_print_preview_set_scale (EOM_PRINT_PREVIEW (priv->preview), scale);
+
+ width *= scale;
+ height *= scale;
+
+ page_width = gtk_page_setup_get_page_width (priv->page_setup, priv->current_unit);
+ page_height = gtk_page_setup_get_page_height (priv->page_setup, priv->current_unit);
+
+ update_image_pos_ranges (setup, page_width, page_height, width, height);
+
+ right = page_width - left - width;
+ bottom = page_height - top - height;
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->width), width);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->height), height);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->right), right);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->bottom), bottom);
+
+ return FALSE;
+}
+
+static gchar *
+on_scale_format_value (GtkScale *scale,
+ gdouble value)
+{
+ return g_strdup_printf ("%i%%", (gint)value);
+}
+
+static void
+position_values_changed (EomPrintImageSetup *setup,
+ GtkWidget *w_changed,
+ GtkWidget *w_to_update,
+ GtkWidget *w_size,
+ gdouble total_size,
+ gint change)
+{
+ EomPrintImageSetupPrivate *priv;
+ gdouble changed, to_update, size;
+ gdouble pos;
+
+ priv = setup->priv;
+ size = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_size));
+ changed = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_changed));
+
+ to_update = total_size - changed - size;
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_to_update), to_update);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (priv->center), CENTER_NONE);
+
+ switch (change) {
+ case CHANGE_HORIZ:
+ pos = gtk_spin_button_get_value (GTK_SPIN_BUTTON (setup->priv->left));
+ if (setup->priv->current_unit == GTK_UNIT_MM) {
+ pos *= FACTOR_MM_TO_INCH;
+ }
+ eom_print_preview_set_image_position (EOM_PRINT_PREVIEW (priv->preview), pos, -1);
+ break;
+ case CHANGE_VERT:
+ pos = gtk_spin_button_get_value (GTK_SPIN_BUTTON (setup->priv->top));
+ if (setup->priv->current_unit == GTK_UNIT_MM) {
+ pos *= FACTOR_MM_TO_INCH;
+ }
+ eom_print_preview_set_image_position (EOM_PRINT_PREVIEW (priv->preview), -1, pos);
+ break;
+ }
+}
+
+static void
+on_left_value_changed (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ EomPrintImageSetup *setup;
+ EomPrintImageSetupPrivate *priv;
+
+ setup = EOM_PRINT_IMAGE_SETUP (user_data);
+ priv = setup->priv;
+
+ position_values_changed (setup,
+ priv->left, priv->right, priv->width,
+ gtk_page_setup_get_page_width (priv->page_setup,
+ priv->current_unit),
+ CHANGE_HORIZ);
+}
+
+static void
+on_right_value_changed (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ EomPrintImageSetupPrivate *priv;
+
+ priv = EOM_PRINT_IMAGE_SETUP (user_data)->priv;
+
+ position_values_changed (EOM_PRINT_IMAGE_SETUP (user_data),
+ priv->right, priv->left, priv->width,
+ gtk_page_setup_get_page_width (priv->page_setup,
+ priv->current_unit),
+ CHANGE_HORIZ);
+}
+
+static void
+on_top_value_changed (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ EomPrintImageSetupPrivate *priv;
+
+ priv = EOM_PRINT_IMAGE_SETUP (user_data)->priv;
+
+ position_values_changed (EOM_PRINT_IMAGE_SETUP (user_data),
+ priv->top, priv->bottom, priv->height,
+ gtk_page_setup_get_page_height (priv->page_setup,
+ priv->current_unit),
+ CHANGE_VERT);
+}
+
+static void
+on_bottom_value_changed (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ EomPrintImageSetupPrivate *priv;
+
+ priv = EOM_PRINT_IMAGE_SETUP (user_data)->priv;
+
+ position_values_changed (EOM_PRINT_IMAGE_SETUP (user_data),
+ priv->bottom, priv->top, priv->height,
+ gtk_page_setup_get_page_height (priv->page_setup,
+ priv->current_unit),
+ CHANGE_VERT);
+}
+
+static void
+size_changed (EomPrintImageSetup *setup,
+ GtkWidget *w_size_x,
+ GtkWidget *w_size_y,
+ GtkWidget *w_margin_x_1,
+ GtkWidget *w_margin_x_2,
+ GtkWidget *w_margin_y_1,
+ GtkWidget *w_margin_y_2,
+ gdouble page_size_x,
+ gdouble page_size_y,
+ gint change)
+{
+ EomPrintImageSetupPrivate *priv;
+ gdouble margin_x_1, margin_x_2;
+ gdouble margin_y_1, margin_y_2;
+ gdouble orig_size_x = -1, orig_size_y = -1, scale;
+ gdouble size_x, size_y;
+ gint pix_width, pix_height;
+ gdouble factor;
+
+ priv = setup->priv;
+
+ size_x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_size_x));
+ margin_x_1 = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_margin_x_1));
+ margin_y_1 = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_margin_y_1));
+
+ eom_image_get_size (priv->image, &pix_width, &pix_height);
+
+ factor = get_scale_to_px_factor (setup);
+
+ switch (change) {
+ case CHANGE_HORIZ:
+ orig_size_x = (gdouble) pix_width / factor;
+ orig_size_y = (gdouble) pix_height / factor;
+ break;
+ case CHANGE_VERT:
+ orig_size_y = (gdouble) pix_width / factor;
+ orig_size_x = (gdouble) pix_height / factor;
+ break;
+ }
+
+ scale = CLAMP (size_x / orig_size_x, 0, 1);
+
+ size_y = scale * orig_size_y;
+
+ margin_x_2 = page_size_x - margin_x_1 - size_x;
+ margin_y_2 = page_size_y - margin_y_1 - size_y;
+
+ eom_print_preview_set_scale (EOM_PRINT_PREVIEW (priv->preview), scale);
+
+ switch (change) {
+ case CHANGE_HORIZ:
+ update_image_pos_ranges (setup, page_size_x, page_size_y, size_x, size_y);
+ break;
+ case CHANGE_VERT:
+ update_image_pos_ranges (setup, page_size_y, page_size_x, size_y, size_x);
+ break;
+ }
+
+ gtk_range_set_value (GTK_RANGE (priv->scaling), 100*scale);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_margin_x_2), margin_x_2);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_size_y), size_y);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_margin_y_2), margin_y_2);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (priv->center), CENTER_NONE);
+}
+
+static void
+on_width_value_changed (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ EomPrintImageSetupPrivate *priv = EOM_PRINT_IMAGE_SETUP (user_data)->priv;
+
+ size_changed (EOM_PRINT_IMAGE_SETUP (user_data),
+ priv->width, priv->height,
+ priv->left, priv->right,
+ priv->top, priv->bottom,
+ gtk_page_setup_get_page_width (priv->page_setup,
+ priv->current_unit),
+ gtk_page_setup_get_page_height (priv->page_setup,
+ priv->current_unit),
+ CHANGE_HORIZ);
+}
+
+static void
+on_height_value_changed (GtkSpinButton *spinbutton,
+ gpointer user_data)
+{
+ EomPrintImageSetupPrivate *priv = EOM_PRINT_IMAGE_SETUP (user_data)->priv;
+
+ size_changed (EOM_PRINT_IMAGE_SETUP (user_data),
+ priv->height, priv->width,
+ priv->top, priv->bottom,
+ priv->left, priv->right,
+ gtk_page_setup_get_page_height (priv->page_setup,
+ priv->current_unit),
+ gtk_page_setup_get_page_width (priv->page_setup,
+ priv->current_unit),
+ CHANGE_VERT);
+}
+
+static void
+change_unit (GtkSpinButton *spinbutton,
+ gdouble factor,
+ gint digits,
+ gdouble step,
+ gdouble page)
+{
+ gdouble value;
+ gdouble range;
+
+ gtk_spin_button_get_range (spinbutton, NULL, &range);
+ range *= factor;
+
+ value = gtk_spin_button_get_value (spinbutton);
+ value *= factor;
+
+ gtk_spin_button_set_range (spinbutton, 0, range);
+ gtk_spin_button_set_value (spinbutton, value);
+ gtk_spin_button_set_digits (spinbutton, digits);
+ gtk_spin_button_set_increments (spinbutton, step, page);
+}
+
+static void
+set_scale_unit (EomPrintImageSetup *setup,
+ GtkUnit unit)
+{
+ EomPrintImageSetupPrivate *priv = setup->priv;
+ gdouble factor;
+ gdouble step, page;
+ gint digits;
+
+ if (G_UNLIKELY (priv->current_unit == unit))
+ return;
+
+ switch (unit) {
+ case GTK_UNIT_MM:
+ factor = FACTOR_INCH_TO_MM;
+ digits = 0;
+ step = 1;
+ page = 10;
+ break;
+ case GTK_UNIT_INCH:
+ factor = FACTOR_MM_TO_INCH;
+ digits = 2;
+ step = 0.01;
+ page = 0.1;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ block_handlers (setup);
+
+ change_unit (GTK_SPIN_BUTTON (priv->width), factor, digits, step, page);
+ change_unit (GTK_SPIN_BUTTON (priv->height), factor, digits, step, page);
+ change_unit (GTK_SPIN_BUTTON (priv->left), factor, digits, step, page);
+ change_unit (GTK_SPIN_BUTTON (priv->right), factor, digits, step, page);
+ change_unit (GTK_SPIN_BUTTON (priv->top), factor, digits, step, page);
+ change_unit (GTK_SPIN_BUTTON (priv->bottom), factor, digits, step, page);
+
+ unblock_handlers (setup);
+
+ priv->current_unit = unit;
+}
+
+static void
+on_unit_changed (GtkComboBox *combobox,
+ gpointer user_data)
+{
+ GtkUnit unit = GTK_UNIT_INCH;
+
+ switch (gtk_combo_box_get_active (combobox)) {
+ case UNIT_INCH:
+ unit = GTK_UNIT_INCH;
+ break;
+ case UNIT_MM:
+ unit = GTK_UNIT_MM;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ set_scale_unit (EOM_PRINT_IMAGE_SETUP (user_data), unit);
+}
+
+static void
+on_preview_image_moved (EomPrintPreview *preview,
+ gpointer user_data)
+{
+ EomPrintImageSetupPrivate *priv = EOM_PRINT_IMAGE_SETUP (user_data)->priv;
+ gdouble x, y;
+
+ eom_print_preview_get_image_position (preview, &x, &y);
+
+ if (priv->current_unit == GTK_UNIT_MM) {
+ x *= FACTOR_INCH_TO_MM;
+ y *= FACTOR_INCH_TO_MM;
+ }
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->left), x);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->top), y);
+}
+
+/* Function taken from gtkprintunixdialog.c */
+static GtkWidget *
+wrap_in_frame (const gchar *label,
+ GtkWidget *child)
+{
+ GtkWidget *frame, *alignment, *label_widget;
+ gchar *bold_text;
+
+ label_widget = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
+ gtk_widget_show (label_widget);
+
+ bold_text = g_markup_printf_escaped ("<b>%s</b>", label);
+ gtk_label_set_markup (GTK_LABEL (label_widget), bold_text);
+ g_free (bold_text);
+
+ frame = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (frame), label_widget, FALSE, FALSE, 0);
+
+ alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
+ 0, 0, 12, 0);
+ gtk_box_pack_start (GTK_BOX (frame), alignment, FALSE, FALSE, 0);
+
+ gtk_container_add (GTK_CONTAINER (alignment), child);
+
+ gtk_widget_show (frame);
+ gtk_widget_show (alignment);
+
+ return frame;
+}
+
+static GtkWidget *
+table_attach_spin_button_with_label (GtkWidget *table,
+ const gchar* text_label,
+ gint left, gint top)
+{
+ GtkWidget *label, *spin_button;
+
+ label = gtk_label_new_with_mnemonic (text_label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ spin_button = gtk_spin_button_new_with_range (0, 100, 0.01);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin_button), 2);
+ gtk_entry_set_width_chars (GTK_ENTRY (spin_button), 6);
+ gtk_table_attach (GTK_TABLE (table), label, left, left + 1,
+ top, top + 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), spin_button, left + 1, left + 2,
+ top, top + 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin_button);
+
+ return spin_button;
+}
+
+static void
+eom_print_image_setup_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomPrintImageSetup *setup = EOM_PRINT_IMAGE_SETUP (object);
+ EomPrintImageSetupPrivate *priv = setup->priv;
+ GdkPixbuf *pixbuf;
+
+ switch (prop_id) {
+ case PROP_IMAGE:
+ if (priv->image) {
+ g_object_unref (priv->image);
+ }
+ priv->image = EOM_IMAGE (g_value_dup_object (value));
+ if (EOM_IS_IMAGE (priv->image)) {
+ pixbuf = eom_image_get_pixbuf (priv->image);
+ g_object_set (priv->preview, "image",
+ pixbuf, NULL);
+ g_object_unref (pixbuf);
+ }
+ break;
+ case PROP_PAGE_SETUP:
+ priv->page_setup = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+eom_print_image_setup_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomPrintImageSetup *setup = EOM_PRINT_IMAGE_SETUP (object);
+ EomPrintImageSetupPrivate *priv = setup->priv;
+
+ switch (prop_id) {
+ case PROP_IMAGE:
+ g_value_set_object (value, priv->image);
+ break;
+ case PROP_PAGE_SETUP:
+ g_value_set_object (value, priv->page_setup);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+set_initial_values (EomPrintImageSetup *setup)
+{
+ EomPrintImageSetupPrivate *priv;
+ GtkPageSetup *page_setup;
+ EomImage *image;
+ gdouble page_width, page_height;
+ gint pix_width, pix_height;
+ gdouble factor;
+ gdouble width, height;
+ gdouble max_perc;
+
+ priv = setup->priv;
+ page_setup = priv->page_setup;
+ image = priv->image;
+
+ factor = get_scale_to_px_factor (setup);
+
+ eom_image_get_size (image, &pix_width, &pix_height);
+ width = (gdouble)pix_width/factor;
+ height = (gdouble)pix_height/factor;
+
+ max_perc = get_max_percentage (setup);
+
+ width *= max_perc;
+ height *= max_perc;
+
+ gtk_range_set_range (GTK_RANGE (priv->scaling), 1, 100*max_perc);
+ gtk_range_set_increments (GTK_RANGE (priv->scaling), max_perc, 10*max_perc);
+ gtk_range_set_value (GTK_RANGE (priv->scaling), 100*max_perc);
+
+ eom_print_preview_set_scale (EOM_PRINT_PREVIEW (priv->preview), max_perc);
+ gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->width), 0, width);
+ gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->height), 0, height);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->width), width);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->height), height);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (priv->center),
+ CENTER_BOTH);
+
+ center (gtk_page_setup_get_page_width (priv->page_setup, priv->current_unit),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->width)),
+ GTK_SPIN_BUTTON (priv->left), GTK_SPIN_BUTTON (priv->right));
+ center (gtk_page_setup_get_page_height (priv->page_setup, priv->current_unit),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->height)),
+ GTK_SPIN_BUTTON (priv->top), GTK_SPIN_BUTTON (priv->bottom));
+
+ page_width = gtk_page_setup_get_page_width (page_setup, priv->current_unit);
+ page_height = gtk_page_setup_get_page_height (page_setup, priv->current_unit);
+
+ update_image_pos_ranges (setup, page_width, page_height, width, height);
+
+
+}
+
+static void
+connect_signals (EomPrintImageSetup *setup)
+{
+ EomPrintImageSetupPrivate *priv;
+
+ priv = setup->priv;
+
+ g_signal_connect (G_OBJECT (priv->left), "value-changed",
+ G_CALLBACK (on_left_value_changed), setup);
+ g_signal_connect (G_OBJECT (priv->right), "value-changed",
+ G_CALLBACK (on_right_value_changed), setup);
+ g_signal_connect (G_OBJECT (priv->top), "value-changed",
+ G_CALLBACK (on_top_value_changed), setup);
+ g_signal_connect (G_OBJECT (priv->bottom), "value-changed",
+ G_CALLBACK (on_bottom_value_changed), setup);
+ g_signal_connect (G_OBJECT (priv->width), "value-changed",
+ G_CALLBACK (on_width_value_changed), setup);
+ g_signal_connect (G_OBJECT (priv->height), "value-changed",
+ G_CALLBACK (on_height_value_changed), setup);
+ g_signal_connect (G_OBJECT (priv->scaling), "value-changed",
+ G_CALLBACK (on_scale_changed), setup);
+ g_signal_connect (G_OBJECT (priv->scaling), "format-value",
+ G_CALLBACK (on_scale_format_value), NULL);
+ g_signal_connect (G_OBJECT (priv->preview), "image-moved",
+ G_CALLBACK (on_preview_image_moved), setup);
+}
+
+static void
+eom_print_image_setup_class_init (EomPrintImageSetupClass *class)
+{
+ GObjectClass *object_class = (GObjectClass *)class;
+
+ object_class->set_property = eom_print_image_setup_set_property;
+ object_class->get_property = eom_print_image_setup_get_property;
+
+ g_object_class_install_property (object_class, PROP_IMAGE,
+ g_param_spec_object ("image",
+ _("Image"),
+ _("The image whose printing properties will be set up"),
+ EOM_TYPE_IMAGE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_PAGE_SETUP,
+ g_param_spec_object ("page-setup",
+ _("Page Setup"),
+ _("The information for the page where the image will be printed"),
+ GTK_TYPE_PAGE_SETUP,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (class, sizeof (EomPrintImageSetupPrivate));
+}
+
+static void
+eom_print_image_setup_init (EomPrintImageSetup *setup)
+{
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *hscale;
+ GtkWidget *combobox;
+ EomPrintImageSetupPrivate *priv;
+
+#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
+ gchar *locale_scale = NULL;
+#endif
+
+ priv = setup->priv = EOM_PRINT_IMAGE_SETUP_GET_PRIVATE (setup);
+
+ priv->image = NULL;
+
+ table = gtk_table_new (3, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ frame = wrap_in_frame (_("Position"), table);
+ gtk_table_attach (GTK_TABLE (setup), frame,
+ 0, 1, 0, 1, GTK_FILL, 0,
+ 0, 0);
+
+ priv->left = table_attach_spin_button_with_label (table, _("_Left:"), 0, 0);
+ priv->right = table_attach_spin_button_with_label (table, _("_Right:"), 0, 1);
+ priv->top = table_attach_spin_button_with_label (table, _("_Top:"), 2, 0);
+ priv->bottom = table_attach_spin_button_with_label (table, _("_Bottom:"), 2, 1);
+
+ label = gtk_label_new_with_mnemonic (_("C_enter:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ combobox = gtk_combo_box_new_text ();
+ gtk_combo_box_insert_text (GTK_COMBO_BOX (combobox),
+ CENTER_NONE, _("None"));
+ gtk_combo_box_insert_text (GTK_COMBO_BOX (combobox),
+ CENTER_HORIZONTAL, _("Horizontal"));
+ gtk_combo_box_insert_text (GTK_COMBO_BOX (combobox),
+ CENTER_VERTICAL, _("Vertical"));
+ gtk_combo_box_insert_text (GTK_COMBO_BOX (combobox),
+ CENTER_BOTH, _("Both"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), CENTER_NONE);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1, 2, 3, GTK_FILL, GTK_FILL,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table), combobox,
+ 1, 4, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combobox);
+ priv->center = combobox;
+ g_signal_connect (G_OBJECT (combobox), "changed",
+ G_CALLBACK (on_center_changed), setup);
+
+ table = gtk_table_new (3, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ frame = wrap_in_frame (_("Size"), table);
+ gtk_table_attach (GTK_TABLE (setup), frame,
+ 0, 1, 1, 2, GTK_FILL, 0,
+ 0, 0);
+
+ priv->width = table_attach_spin_button_with_label (table, _("_Width:"),
+ 0, 0);
+ priv->height = table_attach_spin_button_with_label (table, _("_Height:"),
+ 2, 0);
+
+ label = gtk_label_new_with_mnemonic (_("_Scaling:"));
+ hscale = gtk_hscale_new_with_range (1, 100, 1);
+ gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT);
+ gtk_range_set_value (GTK_RANGE (hscale), 100);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1, 1, 2, GTK_FILL, GTK_FILL,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table), hscale,
+ 1, 4, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), hscale);
+ priv->scaling = hscale;
+
+ label = gtk_label_new_with_mnemonic (_("_Unit:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ combobox = gtk_combo_box_new_text ();
+ gtk_combo_box_insert_text (GTK_COMBO_BOX (combobox), UNIT_MM,
+ _("Millimeters"));
+ gtk_combo_box_insert_text (GTK_COMBO_BOX (combobox), UNIT_INCH,
+ _("Inches"));
+
+#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
+ locale_scale = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
+ if (locale_scale && locale_scale[0] == 2) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), UNIT_INCH);
+ set_scale_unit (setup, GTK_UNIT_INCH);
+ } else
+#endif
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), UNIT_MM);
+ set_scale_unit (setup, GTK_UNIT_MM);
+ }
+
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1, 2, 3, GTK_FILL, GTK_FILL,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table), combobox,
+ 1, 4, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combobox);
+ priv->unit = combobox;
+ g_signal_connect (G_OBJECT (combobox), "changed",
+ G_CALLBACK (on_unit_changed), setup);
+
+ priv->preview = eom_print_preview_new ();
+
+ /* FIXME: This shouldn't be set by hand */
+ gtk_widget_set_size_request (priv->preview, 250, 250);
+
+ frame = wrap_in_frame (_("Preview"), priv->preview);
+ gtk_table_attach (GTK_TABLE (setup), frame,
+ 1, 2, 0, 2, GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_widget_show_all (GTK_WIDGET (setup));
+}
+
+
+/**
+ * eom_print_image_setup_new:
+ * @image: the #EomImage to print
+ * @page_setup: a #GtkPageSetup specifying the page where
+ * the image will be print
+ *
+ * Creates a new #EomPrintImageSetup widget, to be used as a custom
+ * widget in a #GtkPrintUnixDialog. This widgets allows to set
+ * the image position and scale in a page.
+ *
+ * Returns: a new #EomPrintImageSetup
+ **/
+GtkWidget *
+eom_print_image_setup_new (EomImage *image, GtkPageSetup *page_setup)
+{
+ GtkWidget *setup;
+ GtkWidget *preview;
+
+ setup = g_object_new (EOM_TYPE_PRINT_IMAGE_SETUP,
+ "n-rows", 2,
+ "n-columns", 2,
+ "homogeneous", FALSE,
+ "row-spacing", 18,
+ "column-spacing", 18,
+ "border-width", 12,
+ "image", image,
+ "page-setup", page_setup,
+ NULL);
+
+ set_initial_values (EOM_PRINT_IMAGE_SETUP (setup));
+
+ preview = EOM_PRINT_IMAGE_SETUP (setup)->priv->preview;
+ eom_print_preview_set_from_page_setup (EOM_PRINT_PREVIEW (preview),
+ page_setup);
+
+ connect_signals (EOM_PRINT_IMAGE_SETUP (setup));
+
+ return setup;
+}
+
+/**
+ * eom_print_image_setup_get_options:
+ * @setup: a #EomPrintImageSetup
+ * @left: a pointer where to store the image's left position
+ * @top: a pointer where to store the image's top position
+ * @scale: a pointer where to store the image's scale
+ * @unit: a pointer where to store the #GtkUnit used by the @left and @top values.
+ *
+ * Gets the options set by the #EomPrintImageSetup.
+ **/
+void
+eom_print_image_setup_get_options (EomPrintImageSetup *setup,
+ gdouble *left,
+ gdouble *top,
+ gdouble *scale,
+ GtkUnit *unit)
+{
+ EomPrintImageSetupPrivate *priv;
+
+ g_return_if_fail (EOM_IS_PRINT_IMAGE_SETUP (setup));
+
+ priv = setup->priv;
+
+ *left = gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->left));
+ *top = gtk_spin_button_get_value (GTK_SPIN_BUTTON (priv->top));
+ *scale = gtk_range_get_value (GTK_RANGE (priv->scaling));
+ *unit = priv->current_unit;
+}
+
+void
+eom_print_image_setup_update (GtkPrintOperation *operation,
+ GtkWidget *custom_widget,
+ GtkPageSetup *page_setup,
+ GtkPrintSettings *print_settings,
+ gpointer user_data)
+{
+ GtkWidget *preview;
+ gdouble pos_x;
+ gdouble pos_y;
+ EomPrintImageSetup *setup;
+
+ setup = EOM_PRINT_IMAGE_SETUP (custom_widget);
+
+ setup->priv->page_setup = gtk_page_setup_copy (page_setup);
+
+ set_initial_values (EOM_PRINT_IMAGE_SETUP (setup));
+
+ preview = EOM_PRINT_IMAGE_SETUP (setup)->priv->preview;
+ eom_print_preview_set_from_page_setup (EOM_PRINT_PREVIEW (preview),
+ setup->priv->page_setup);
+
+ pos_x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (setup->priv->left));
+ pos_y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (setup->priv->top));
+ if (setup->priv->current_unit == GTK_UNIT_MM) {
+ pos_x *= FACTOR_MM_TO_INCH;
+ pos_y *= FACTOR_MM_TO_INCH;
+ }
+ eom_print_preview_set_image_position (EOM_PRINT_PREVIEW (setup->priv->preview), pos_x, pos_y);
+}
diff --git a/src/eom-print-image-setup.h b/src/eom-print-image-setup.h
new file mode 100644
index 0000000..9ed60cd
--- /dev/null
+++ b/src/eom-print-image-setup.h
@@ -0,0 +1,71 @@
+/* Eye Of MATE -- Print Dialog Custom Widget
+ *
+ * Copyright (C) 2006-2007 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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-image.h"
+
+#ifndef EOM_PRINT_IMAGE_SETUP_H
+#define EOM_PRINT_IMAGE_SETUP_H
+
+G_BEGIN_DECLS
+
+typedef struct _EomPrintImageSetup EomPrintImageSetup;
+typedef struct _EomPrintImageSetupClass EomPrintImageSetupClass;
+typedef struct EomPrintImageSetupPrivate EomPrintImageSetupPrivate;
+
+#define EOM_TYPE_PRINT_IMAGE_SETUP (eom_print_image_setup_get_type ())
+#define EOM_PRINT_IMAGE_SETUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_PRINT_IMAGE_SETUP, EomPrintImageSetup))
+#define EOM_PRINT_IMAGE_SETUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_PRINT_IMAGE_SETUP, EomPrintImageSetupClass))
+#define EOM_IS_PRINT_IMAGE_SETUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_PRINT_IMAGE_SETUP))
+#define EOM_IS_PRINT_IMAGE_SETUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_PRINT_IMAGE_SETUP))
+#define EOM_PRINT_IMAGE_SETUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_PRINT_IMAGE_SETUP, EomPrintImageSetupClass))
+
+struct _EomPrintImageSetup {
+ GtkTable parent_instance;
+
+ EomPrintImageSetupPrivate *priv;
+};
+
+struct _EomPrintImageSetupClass {
+ GtkTableClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType eom_print_image_setup_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkWidget *eom_print_image_setup_new (EomImage *image,
+ GtkPageSetup *page_setup);
+
+G_GNUC_INTERNAL
+void eom_print_image_setup_get_options (EomPrintImageSetup *setup,
+ gdouble *left,
+ gdouble *top,
+ gdouble *scale,
+ GtkUnit *unit);
+void eom_print_image_setup_update (GtkPrintOperation *operation,
+ GtkWidget *custom_widget,
+ GtkPageSetup *page_setup,
+ GtkPrintSettings *print_settings,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* EOM_PRINT_IMAGE_SETUP_H */
diff --git a/src/eom-print-preview.c b/src/eom-print-preview.c
new file mode 100644
index 0000000..13957c6
--- /dev/null
+++ b/src/eom-print-preview.c
@@ -0,0 +1,1226 @@
+/* Eye Of MATE -- Print Preview Widget
+ *
+ * Copyright (C) 2006-2008 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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 <gtk/gtk.h>
+#include <cairo.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "eom-image.h"
+#include "eom-print-preview.h"
+
+#define EOM_PRINT_PREVIEW_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PRINT_PREVIEW, EomPrintPreviewPrivate))
+
+G_DEFINE_TYPE (EomPrintPreview, eom_print_preview, GTK_TYPE_ASPECT_FRAME)
+
+struct _EomPrintPreviewPrivate {
+ GtkWidget *area;
+ GdkPixbuf *image;
+ GdkPixbuf *image_scaled;
+
+ /* The surface to set to the cairo context, created from the image */
+ cairo_surface_t *surface;
+
+ /* Flag whether we have to create surface */
+ gboolean flag_create_surface;
+
+ /* the alignment of the image in the page */
+ gfloat image_x_align, image_y_align;
+
+ /* real paper size, in inches */
+ gfloat p_width, p_height;
+
+ /* page margins, in inches */
+ gfloat l_margin, r_margin, t_margin, b_margin;
+
+ /* page margins, relatives to the widget size */
+ gint l_rmargin, r_rmargin, t_rmargin, b_rmargin;
+
+ /* image width, relative to the widget size */
+ gint r_width, r_height;
+
+ /* scale of the image, as defined by the user */
+ gfloat i_scale;
+
+ /* scale of the page, relative to the widget size */
+ gfloat p_scale;
+
+ /* whether we are currently grabbing the image */
+ gboolean grabbed;
+
+ /* the last cursor position */
+ gdouble cursorx, cursory;
+
+ /* if we reject to move the image,
+ store the delta here */
+ gdouble r_dx, r_dy;
+};
+
+/* Signal IDs */
+enum {
+ SIGNAL_IMAGE_MOVED,
+ SIGNAL_LAST
+};
+static gint preview_signals [SIGNAL_LAST];
+
+enum {
+ PROP_IMAGE = 1,
+ PROP_IMAGE_X_ALIGN,
+ PROP_IMAGE_Y_ALIGN,
+ PROP_IMAGE_SCALE,
+ PROP_PAPER_WIDTH,
+ PROP_PAPER_HEIGHT,
+ PROP_PAGE_LEFT_MARGIN,
+ PROP_PAGE_RIGHT_MARGIN,
+ PROP_PAGE_TOP_MARGIN,
+ PROP_PAGE_BOTTOM_MARGIN
+};
+
+static void eom_print_preview_draw (EomPrintPreview *preview, cairo_t *cr);
+static void eom_print_preview_finalize (GObject *object);
+static void update_relative_sizes (EomPrintPreview *preview);
+static void create_surface (EomPrintPreview *preview);
+static void create_image_scaled (EomPrintPreview *preview);
+static gboolean create_surface_when_idle (EomPrintPreview *preview);
+
+static void
+eom_print_preview_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomPrintPreviewPrivate *priv = EOM_PRINT_PREVIEW (object)->priv;
+
+ switch (prop_id) {
+ case PROP_IMAGE:
+ g_value_set_object (value, priv->image);
+ break;
+ case PROP_IMAGE_X_ALIGN:
+ g_value_set_float (value, priv->image_x_align);
+ break;
+ case PROP_IMAGE_Y_ALIGN:
+ g_value_set_float (value, priv->image_y_align);
+ break;
+ case PROP_IMAGE_SCALE:
+ g_value_set_float (value, priv->i_scale);
+ break;
+ case PROP_PAPER_WIDTH:
+ g_value_set_float (value, priv->p_width);
+ break;
+ case PROP_PAPER_HEIGHT:
+ g_value_set_float (value, priv->p_height);
+ break;
+ case PROP_PAGE_LEFT_MARGIN:
+ g_value_set_float (value, priv->l_margin);
+ break;
+ case PROP_PAGE_RIGHT_MARGIN:
+ g_value_set_float (value, priv->r_margin);
+ break;
+ case PROP_PAGE_TOP_MARGIN:
+ g_value_set_float (value, priv->t_margin);
+ break;
+ case PROP_PAGE_BOTTOM_MARGIN:
+ g_value_set_float (value, priv->b_margin);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+eom_print_preview_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomPrintPreviewPrivate *priv = EOM_PRINT_PREVIEW (object)->priv;
+ gboolean paper_size_changed = FALSE;
+
+ switch (prop_id) {
+ case PROP_IMAGE:
+ if (priv->image) {
+ g_object_unref (priv->image);
+ }
+ priv->image = GDK_PIXBUF (g_value_dup_object (value));
+
+ if (priv->image_scaled) {
+ g_object_unref (priv->image_scaled);
+ priv->image_scaled = NULL;
+ }
+
+ priv->flag_create_surface = TRUE;
+ break;
+ case PROP_IMAGE_X_ALIGN:
+ priv->image_x_align = g_value_get_float (value);
+ break;
+ case PROP_IMAGE_Y_ALIGN:
+ priv->image_y_align = g_value_get_float (value);
+ break;
+ case PROP_IMAGE_SCALE:
+ priv->i_scale = g_value_get_float (value);
+ priv->flag_create_surface = TRUE;
+ break;
+ case PROP_PAPER_WIDTH:
+ priv->p_width = g_value_get_float (value);
+ paper_size_changed = TRUE;
+ break;
+ case PROP_PAPER_HEIGHT:
+ priv->p_height = g_value_get_float (value);
+ paper_size_changed = TRUE;
+ break;
+ case PROP_PAGE_LEFT_MARGIN:
+ priv->l_margin = g_value_get_float (value);
+ break;
+ case PROP_PAGE_RIGHT_MARGIN:
+ priv->r_margin = g_value_get_float (value);
+ break;
+ case PROP_PAGE_TOP_MARGIN:
+ priv->t_margin = g_value_get_float (value);
+ break;
+ case PROP_PAGE_BOTTOM_MARGIN:
+ priv->b_margin = g_value_get_float (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+
+ if (paper_size_changed) {
+ g_object_set (object,
+ "ratio", priv->p_width/priv->p_height,
+ NULL);
+ }
+
+ update_relative_sizes (EOM_PRINT_PREVIEW (object));
+ gtk_widget_queue_draw (priv->area);
+}
+
+static void
+eom_print_preview_class_init (EomPrintPreviewClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass*) klass;
+
+ gobject_class->get_property = eom_print_preview_get_property;
+ gobject_class->set_property = eom_print_preview_set_property;
+ gobject_class->finalize = eom_print_preview_finalize;
+
+/**
+ * EomPrintPreview:image:
+ *
+ * The "image" property defines the image that is previewed
+ * in the widget.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_IMAGE,
+ g_param_spec_object ("image",
+ "Image to show in the preview",
+ "",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:image-x-align:
+ *
+ * The "image-x-align" property defines the horizontal alignment
+ * of the image in the widget.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_IMAGE_X_ALIGN,
+ g_param_spec_float ("image-x-align",
+ "Horizontal alignment for the image",
+ "",
+ 0,
+ 1,
+ 0.5,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:image-y-align:
+ *
+ * The "image-y-align" property defines the horizontal alignment
+ * of the image in the widget.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_IMAGE_Y_ALIGN,
+ g_param_spec_float ("image-y-align",
+ "Vertical alignment for the image",
+ "",
+ 0,
+ 1,
+ 0.5,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:image-scale:
+ *
+ * The "image-scale" property defines the scaling of the image
+ * that the user wants for the printing.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_IMAGE_SCALE,
+ g_param_spec_float ("image-scale",
+ "The scale for the image",
+ "",
+ 0,
+ 1,
+ 1,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:paper-width:
+ *
+ * The width of the previewed paper, in inches.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PAPER_WIDTH,
+ g_param_spec_float ("paper-width",
+ "Real paper width in inches",
+ "",
+ 0,
+ 100,
+ 8.5,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:paper-height:
+ *
+ * The height of the previewed paper, in inches.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PAPER_HEIGHT,
+ g_param_spec_float ("paper-height",
+ "Real paper height in inches",
+ "",
+ 0,
+ 200,
+ 11,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:page-left-margin:
+ *
+ * The size of the page's left margin, in inches.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PAGE_LEFT_MARGIN,
+ g_param_spec_float ("page-left-margin",
+ "Left margin of the page in inches",
+ "",
+ 0,
+ 100,
+ 0.25,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:page-right-margin:
+ *
+ * The size of the page's right margin, in inches.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PAGE_RIGHT_MARGIN,
+ g_param_spec_float ("page-right-margin",
+ "Right margin of the page in inches",
+ "",
+ 0,
+ 200,
+ 0.25,
+ G_PARAM_READWRITE));
+/**
+ * EomPrintPreview:page-top-margin:
+ *
+ * The size of the page's top margin, in inches.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PAGE_TOP_MARGIN,
+ g_param_spec_float ("page-top-margin",
+ "Top margin of the page in inches",
+ "",
+ 0,
+ 100,
+ 0.25,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview:page-bottom-margin:
+ *
+ * The size of the page's bottom margin, in inches.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PAGE_BOTTOM_MARGIN,
+ g_param_spec_float ("page-bottom-margin",
+ "Bottom margin of the page in inches",
+ "",
+ 0,
+ 200,
+ 0.56,
+ G_PARAM_READWRITE));
+
+/**
+ * EomPrintPreview::image-moved:
+ * @preview: the object which received the signal
+ *
+ * The #EomPrintPreview::image-moved signal is emitted when the position
+ * of the image is changed.
+ */
+ preview_signals [SIGNAL_IMAGE_MOVED] =
+ g_signal_new ("image_moved",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+ 0, NULL);
+
+ g_type_class_add_private (klass, sizeof (EomPrintPreviewPrivate));
+}
+
+static void
+eom_print_preview_finalize (GObject *object)
+{
+ EomPrintPreviewPrivate *priv;
+
+ priv = EOM_PRINT_PREVIEW (object)->priv;
+
+ if (priv->image) {
+ g_object_unref (priv->image);
+ priv->image = NULL;
+ }
+
+ if (priv->image_scaled) {
+ g_object_unref (priv->image_scaled);
+ priv->image_scaled = NULL;
+ }
+
+ if (priv->surface) {
+ cairo_surface_destroy (priv->surface);
+ priv->surface = NULL;
+ }
+
+ G_OBJECT_CLASS (eom_print_preview_parent_class)->finalize (object);
+}
+
+static void
+eom_print_preview_init (EomPrintPreview *preview)
+{
+ EomPrintPreviewPrivate *priv;
+ gfloat ratio;
+
+ priv = preview->priv = EOM_PRINT_PREVIEW_GET_PRIVATE (preview);
+
+ priv->area = GTK_WIDGET (gtk_drawing_area_new ());
+
+ gtk_container_add (GTK_CONTAINER (preview), priv->area);
+
+ priv->p_width = 8.5;
+ priv->p_height = 11.0;
+
+ ratio = priv->p_width/priv->p_height;
+
+ gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview),
+ 0.5, 0.5, ratio, FALSE);
+
+ priv->image = NULL;
+ priv->image_scaled = NULL;
+ priv->image_x_align = 0.5;
+ priv->image_y_align = 0.5;
+ priv->i_scale = 1;
+
+ priv->surface = NULL;
+ priv->flag_create_surface = TRUE;
+
+ priv->p_scale = 0;
+
+ priv->l_margin = 0.25;
+ priv->r_margin = 0.25;
+ priv->t_margin = 0.25;
+ priv->b_margin = 0.56;
+
+ priv->grabbed = FALSE;
+ priv->cursorx = 0;
+ priv->cursory = 0;
+ priv->r_dx = 0;
+ priv->r_dy = 0;
+}
+
+static gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *bev, gpointer user_data);
+static gboolean button_release_event_cb (GtkWidget *widget, GdkEventButton *bev, gpointer user_data);
+static gboolean motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *mev, gpointer user_data);
+static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
+
+static void expose_event_cb (GtkDrawingArea *drawing_area, GdkEventExpose *eev, gpointer user_data);
+static void size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data);
+
+/**
+ * eom_print_preview_new_with_pixbuf:
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Creates a new #EomPrintPreview widget, and sets the #GdkPixbuf to preview
+ * on it.
+ *
+ * Returns: A new #EomPrintPreview widget.
+ **/
+GtkWidget *
+eom_print_preview_new_with_pixbuf (GdkPixbuf *pixbuf)
+{
+ EomPrintPreview *preview;
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ preview = EOM_PRINT_PREVIEW (eom_print_preview_new ());
+
+ preview->priv->image = g_object_ref (pixbuf);
+
+ update_relative_sizes (preview);
+
+ return GTK_WIDGET (preview);
+}
+
+/**
+ * eom_print_preview_new:
+ *
+ * Creates a new #EomPrintPreview widget, setting it to the default values,
+ * and leaving the page empty. You still need to set the #EomPrintPreview:image
+ * property to make it useful.
+ *
+ * Returns: A new and empty #EomPrintPreview widget.
+ **/
+GtkWidget *
+eom_print_preview_new (void)
+{
+ EomPrintPreview *preview;
+ GtkWidget *area;
+
+ preview = g_object_new (EOM_TYPE_PRINT_PREVIEW, NULL);
+
+ area = preview->priv->area;
+
+ gtk_widget_set_events (area,
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK);
+
+ g_object_set (G_OBJECT (area),
+ "can-focus", TRUE,
+ NULL);
+
+/* update_relative_sizes (preview); */
+
+ g_signal_connect (G_OBJECT (area), "expose-event",
+ G_CALLBACK (expose_event_cb), preview);
+
+ g_signal_connect (G_OBJECT (area), "motion-notify-event",
+ G_CALLBACK (motion_notify_event_cb), preview);
+
+ g_signal_connect (G_OBJECT (area), "button-press-event",
+ G_CALLBACK (button_press_event_cb), preview);
+
+ g_signal_connect (G_OBJECT (area), "button-release-event",
+ G_CALLBACK (button_release_event_cb), preview);
+
+ g_signal_connect (G_OBJECT (area), "key-press-event",
+ G_CALLBACK (key_press_event_cb), preview);
+
+ g_signal_connect (area, "size-allocate",
+ G_CALLBACK (size_allocate_cb), preview);
+
+ return GTK_WIDGET (preview);
+}
+
+static void
+expose_event_cb (GtkDrawingArea *drawing_area,
+ GdkEventExpose *eev,
+ gpointer user_data)
+{
+ GtkWidget *widget;
+ EomPrintPreviewPrivate *priv;
+ cairo_t *cr;
+
+ widget = GTK_WIDGET (drawing_area);
+ priv = EOM_PRINT_PREVIEW (user_data)->priv;
+
+ update_relative_sizes (EOM_PRINT_PREVIEW (user_data));
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ eom_print_preview_draw (EOM_PRINT_PREVIEW (user_data), cr);
+
+ if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
+ fprintf (stderr, "Cairo is unhappy: %s\n",
+ cairo_status_to_string (cairo_status (cr)));
+ }
+
+ cairo_destroy (cr);
+
+ gdk_window_get_pointer (gtk_widget_get_window (widget), NULL, NULL, NULL);
+}
+
+/**
+ * get_current_image_coordinates:
+ * @preview: an #EomPrintPreview
+ * @x0: A pointer where to store the x coordinate.
+ * @y0: A pointer where to store the y coordinate.
+ *
+ * This function returns the current image coordinates, according
+ * with the properties of the given @preview widget.
+ **/
+static void
+get_current_image_coordinates (EomPrintPreview *preview,
+ gint *x0, gint *y0)
+{
+ EomPrintPreviewPrivate *priv;
+ GtkAllocation allocation;
+
+ priv = preview->priv;
+ gtk_widget_get_allocation (GTK_WIDGET (priv->area), &allocation);
+
+ *x0 = (gint)((1 - priv->image_x_align)*priv->l_rmargin + priv->image_x_align*(allocation.width - priv->r_rmargin - priv->r_width));
+ *y0 = (gint)((1 - priv->image_y_align)*priv->t_rmargin + priv->image_y_align*(allocation.height - priv->b_rmargin - priv->r_height));
+}
+
+/**
+ * press_inside_image_area:
+ * @preview: an #EomPrintPreview
+ * @x: the points x coordinate
+ * @y: the points y coordinate
+ *
+ * Returns whether the given point is inside the image area.
+ *
+ * Returns: %TRUE if the given point is inside of the image area,
+ * %FALSE otherwise.
+ **/
+static gboolean
+press_inside_image_area (EomPrintPreview *preview,
+ guint x,
+ guint y)
+{
+ EomPrintPreviewPrivate *priv;
+ gint x0, y0;
+
+ priv = preview->priv;
+ get_current_image_coordinates (preview, &x0, &y0);
+
+ if (x >= x0 && y >= y0 &&
+ x <= x0 + priv->r_width && y <= y0 + priv->r_height)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+create_image_scaled (EomPrintPreview *preview)
+{
+ EomPrintPreviewPrivate *priv = preview->priv;
+
+ if (!priv->image_scaled) {
+ gint i_width, i_height;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (priv->area, &allocation);
+ i_width = gdk_pixbuf_get_width (priv->image);
+ i_height = gdk_pixbuf_get_height (priv->image);
+
+ if ((i_width > allocation.width) ||
+ (i_height > allocation.height)) {
+ gdouble scale;
+ scale = MIN ((gdouble) allocation.width/i_width,
+ (gdouble) allocation.height/i_height);
+ priv->image_scaled = gdk_pixbuf_scale_simple (priv->image,
+ i_width*scale,
+ i_height*scale,
+ GDK_INTERP_TILES);
+ } else {
+ priv->image_scaled = priv->image;
+ g_object_ref (priv->image_scaled);
+ }
+ }
+}
+
+static GdkPixbuf *
+create_preview_buffer (EomPrintPreview *preview)
+{
+ GdkPixbuf *pixbuf;
+ gint width, height;
+ GdkInterpType type = GDK_INTERP_TILES;
+
+ if (preview->priv->image == NULL) {
+ return NULL;
+ }
+
+ create_image_scaled (preview);
+
+ width = gdk_pixbuf_get_width (preview->priv->image);
+ height = gdk_pixbuf_get_height (preview->priv->image);
+
+ width *= preview->priv->i_scale * preview->priv->p_scale;
+ height *= preview->priv->i_scale * preview->priv->p_scale;
+
+ if (width < 1 || height < 1)
+ return NULL;
+
+ /* to use GDK_INTERP_TILES for small pixbufs is expensive and unnecessary */
+ if (width < 25 || height < 25)
+ type = GDK_INTERP_NEAREST;
+
+ if (preview->priv->image_scaled) {
+ pixbuf = gdk_pixbuf_scale_simple (preview->priv->image_scaled,
+ width, height, type);
+ } else {
+ pixbuf = gdk_pixbuf_scale_simple (preview->priv->image,
+ width, height, type);
+ }
+
+ return pixbuf;
+}
+
+/*
+ Function inspired from gdk_cairo_set_source_pixbuf (). The main reason is
+ that I want to save the cairo_surface_t created from the scaled buffer to
+ improve performance.
+*/
+static cairo_surface_t *
+create_surface_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ gint width = gdk_pixbuf_get_width (pixbuf);
+ gint height = gdk_pixbuf_get_height (pixbuf);
+ guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+ int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+ int cairo_stride;
+ guchar *cairo_pixels;
+ cairo_format_t format;
+ cairo_surface_t *surface;
+ static const cairo_user_data_key_t key;
+ int j;
+
+ if (n_channels == 3)
+ format = CAIRO_FORMAT_RGB24;
+ else
+ format = CAIRO_FORMAT_ARGB32;
+
+ cairo_stride = cairo_format_stride_for_width (format, width);
+ cairo_pixels = g_malloc (height * cairo_stride);
+ surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels,
+ format,
+ width, height, cairo_stride);
+ cairo_surface_set_user_data (surface, &key,
+ cairo_pixels, (cairo_destroy_func_t)g_free);
+
+ for (j = height; j; j--)
+ {
+ guchar *p = gdk_pixels;
+ guchar *q = cairo_pixels;
+
+ if (n_channels == 3)
+ {
+ guchar *end = p + 3 * width;
+
+ while (p < end)
+ {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ q[0] = p[2];
+ q[1] = p[1];
+ q[2] = p[0];
+#else
+ q[1] = p[0];
+ q[2] = p[1];
+ q[3] = p[2];
+#endif
+ p += 3;
+ q += 4;
+ }
+ }
+ else
+ {
+ guchar *end = p + 4 * width;
+ guint t1,t2,t3;
+
+#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+
+ while (p < end)
+ {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ MULT(q[0], p[2], p[3], t1);
+ MULT(q[1], p[1], p[3], t2);
+ MULT(q[2], p[0], p[3], t3);
+ q[3] = p[3];
+#else
+ q[0] = p[3];
+ MULT(q[1], p[0], p[3], t1);
+ MULT(q[2], p[1], p[3], t2);
+ MULT(q[3], p[2], p[3], t3);
+#endif
+
+ p += 4;
+ q += 4;
+ }
+
+#undef MULT
+ }
+
+ gdk_pixels += gdk_rowstride;
+ cairo_pixels += cairo_stride;
+ }
+
+ return surface;
+}
+
+static void
+create_surface (EomPrintPreview *preview)
+{
+ EomPrintPreviewPrivate *priv = preview->priv;
+ GdkPixbuf *pixbuf;
+
+ if (priv->surface) {
+ cairo_surface_destroy (priv->surface);
+ priv->surface = NULL;
+ }
+
+ pixbuf = create_preview_buffer (preview);
+ if (pixbuf) {
+ priv->surface = create_surface_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ }
+ priv->flag_create_surface = FALSE;
+}
+
+static gboolean
+create_surface_when_idle (EomPrintPreview *preview)
+{
+ create_surface (preview);
+
+ return FALSE;
+}
+
+static gboolean
+button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ EomPrintPreview *preview = EOM_PRINT_PREVIEW (user_data);
+
+ preview->priv->cursorx = event->x;
+ preview->priv->cursory = event->y;
+
+ switch (event->button) {
+ case 1:
+ preview->priv->grabbed = press_inside_image_area (preview, event->x, event->y);
+ break;
+ }
+
+ if (preview->priv->grabbed) {
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+ }
+
+ gtk_widget_grab_focus (preview->priv->area);
+
+ return FALSE;
+}
+
+static gboolean
+button_release_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ EomPrintPreview *preview = EOM_PRINT_PREVIEW (user_data);
+
+ switch (event->button) {
+ case 1:
+ preview->priv->grabbed = FALSE;
+ preview->priv->r_dx = 0;
+ preview->priv->r_dy = 0;
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+
+ }
+ return FALSE;
+}
+
+static gboolean
+key_press_event_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ EomPrintPreviewPrivate *priv;
+ gfloat delta, align;
+ gboolean stop_emission = FALSE;
+ const gchar *property;
+
+ priv = EOM_PRINT_PREVIEW (user_data)->priv;
+
+ delta = 0;
+
+ switch (event->keyval) {
+ case GDK_Left:
+ property = "image-x-align";
+ delta = -0.01;
+ break;
+ case GDK_Right:
+ property = "image-x-align";
+ delta = 0.01;
+ break;
+ case GDK_Up:
+ property = "image-y-align";
+ delta = -0.01;
+ break;
+ case GDK_Down:
+ property = "image-y-align";
+ delta = 0.01;
+ break;
+ }
+
+ if (delta != 0) {
+ g_object_get (G_OBJECT (user_data),
+ property, &align,
+ NULL);
+
+ align += delta;
+ align = CLAMP (align, 0, 1);
+ g_object_set (G_OBJECT (user_data),
+ property, align,
+ NULL);
+
+ stop_emission = TRUE;
+ g_signal_emit (G_OBJECT (user_data),
+ preview_signals
+ [SIGNAL_IMAGE_MOVED], 0);
+ }
+
+ return stop_emission;
+}
+
+static gboolean
+motion_notify_event_cb (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer user_data)
+{
+ EomPrintPreviewPrivate *priv = EOM_PRINT_PREVIEW (user_data)->priv;
+ gdouble dx, dy;
+ GtkAllocation allocation;
+
+ if (priv->grabbed) {
+ dx = event->x - priv->cursorx;
+ dy = event->y - priv->cursory;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ /* Make sure the image stays inside the margins */
+
+ priv->image_x_align += (dx + priv->r_dx)/(allocation.width - priv->r_width - priv->l_rmargin - priv->r_rmargin);
+ if (priv->image_x_align < 0. || priv->image_x_align > 1.) {
+ priv->image_x_align = CLAMP (priv->image_x_align, 0., 1.);
+ priv->r_dx += dx;
+ }
+ else
+ priv->r_dx = 0;
+
+ priv->image_y_align += (dy + priv->r_dy)/(allocation.height - priv->r_height - priv->t_rmargin - priv->b_rmargin);
+ if (priv->image_y_align < 0. || priv->image_y_align > 1.) {
+ priv->image_y_align = CLAMP (priv->image_y_align, 0., 1.);
+ priv->r_dy += dy;
+ } else
+ priv->r_dy = 0;
+
+ /* we do this to correctly change the property values */
+ g_object_set (EOM_PRINT_PREVIEW (user_data),
+ "image-x-align", priv->image_x_align,
+ "image-y-align", priv->image_y_align,
+ NULL);
+
+ priv->cursorx = event->x;
+ priv->cursory = event->y;
+
+ g_signal_emit (G_OBJECT (user_data),
+ preview_signals
+ [SIGNAL_IMAGE_MOVED], 0);
+ } else {
+ if (press_inside_image_area (EOM_PRINT_PREVIEW (user_data), event->x, event->y)) {
+ GdkCursor *cursor;
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_FLEUR);
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ cursor);
+ gdk_cursor_unref (cursor);
+ } else {
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ NULL);
+ }
+ }
+ return FALSE;
+}
+
+static void
+size_allocate_cb (GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer user_data)
+{
+ EomPrintPreview *preview;
+
+ preview = EOM_PRINT_PREVIEW (user_data);
+ update_relative_sizes (preview);
+
+ preview->priv->flag_create_surface = TRUE;
+
+ if (preview->priv->image_scaled) {
+ g_object_unref (preview->priv->image_scaled);
+ preview->priv->image_scaled = NULL;
+ }
+
+ g_idle_add ((GSourceFunc) create_surface_when_idle, preview);
+}
+
+static void
+eom_print_preview_draw (EomPrintPreview *preview, cairo_t *cr)
+{
+ EomPrintPreviewPrivate *priv;
+ GtkWidget *area;
+ GtkAllocation allocation;
+ gint x0, y0;
+ GtkStyle *style;
+ gboolean has_focus;
+
+ priv = preview->priv;
+ area = priv->area;
+
+ has_focus = gtk_widget_has_focus (area);
+
+ style = gtk_widget_get_style (area);
+
+ gtk_widget_get_allocation (area, &allocation);
+
+ /* draw the page */
+ gdk_cairo_set_source_color (cr, &style->white);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ /* draw the page margins */
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_set_line_width (cr, 0.1);
+ cairo_rectangle (cr,
+ priv->l_rmargin, priv->t_rmargin,
+ allocation.width - priv->l_rmargin - priv->r_rmargin,
+ allocation.height - priv->t_rmargin - priv->b_rmargin);
+ cairo_stroke (cr);
+
+ get_current_image_coordinates (preview, &x0, &y0);
+
+ if (priv->flag_create_surface) {
+ create_surface (preview);
+ }
+
+ if (priv->surface) {
+ cairo_set_source_surface (cr, priv->surface, x0, y0);
+ cairo_paint (cr);
+ } else if (priv->image_scaled) {
+ /* just in the remote case we don't have the surface */
+
+ /* adjust (x0, y0) to the new scale */
+ gdouble scale = priv->i_scale * priv->p_scale *
+ gdk_pixbuf_get_width (priv->image) / gdk_pixbuf_get_width (priv->image_scaled);
+ x0 /= scale;
+ y0 /= scale;
+
+ cairo_scale (cr, scale, scale);
+ gdk_cairo_set_source_pixbuf (cr, priv->image_scaled, x0, y0);
+ cairo_paint (cr);
+ } else if (priv->image) {
+ /* just in the remote case we don't have the surface */
+
+ /* adjust (x0, y0) to the new scale */
+ x0 /= priv->i_scale * priv->p_scale;
+ y0 /= priv->i_scale * priv->p_scale;
+
+ cairo_scale (cr, priv->i_scale*priv->p_scale, priv->i_scale*priv->p_scale);
+ gdk_cairo_set_source_pixbuf (cr, priv->image, x0, y0);
+ cairo_paint (cr);
+ }
+
+ if (has_focus) {
+ gtk_paint_focus (style, gtk_widget_get_window (area),
+ GTK_STATE_NORMAL, NULL, NULL, NULL,
+ 0, 0, allocation.width, allocation.height);
+ }
+}
+
+static void
+update_relative_sizes (EomPrintPreview *preview)
+{
+ EomPrintPreviewPrivate *priv;
+ GtkAllocation allocation;
+ gint i_width, i_height;
+
+ priv = preview->priv;
+
+ if (priv->image != NULL) {
+ i_width = gdk_pixbuf_get_width (priv->image);
+ i_height = gdk_pixbuf_get_height (priv->image);
+ } else {
+ i_width = i_height = 0;
+ }
+
+ gtk_widget_get_allocation (priv->area, &allocation);
+
+ priv->p_scale = (gfloat) allocation.width / (priv->p_width * 72.0);
+
+ priv->r_width = (gint) i_width * priv->i_scale * priv->p_scale;
+ priv->r_height = (gint) i_height * priv->i_scale * priv->p_scale;
+
+ priv->l_rmargin = (gint) (72. * priv->l_margin * priv->p_scale);
+ priv->r_rmargin = (gint) (72. * priv->r_margin * priv->p_scale);
+ priv->t_rmargin = (gint) (72. * priv->t_margin * priv->p_scale);
+ priv->b_rmargin = (gint) (72. * priv->b_margin * priv->p_scale);
+}
+
+/**
+ * eom_print_preview_set_page_margins:
+ * @preview: a #EomPrintPreview
+ * @l_margin: Left margin.
+ * @r_margin: Right margin.
+ * @t_margin: Top margin.
+ * @b_margin: Bottom margin.
+ *
+ * Manually set the margins, in inches.
+ **/
+void
+eom_print_preview_set_page_margins (EomPrintPreview *preview,
+ gfloat l_margin,
+ gfloat r_margin,
+ gfloat t_margin,
+ gfloat b_margin)
+{
+ g_return_if_fail (EOM_IS_PRINT_PREVIEW (preview));
+
+ g_object_set (G_OBJECT(preview),
+ "page-left-margin", l_margin,
+ "page-right-margin", r_margin,
+ "page-top-margin", t_margin,
+ "page-bottom-margin", r_margin,
+ NULL);
+}
+
+/**
+ * eom_print_preview_set_from_page_setup:
+ * @preview: a #EomPrintPreview
+ * @setup: a #GtkPageSetup to set the properties from
+ *
+ * Sets up the page properties from a #GtkPageSetup. Useful when using the
+ * widget with the GtkPrint API.
+ **/
+void
+eom_print_preview_set_from_page_setup (EomPrintPreview *preview,
+ GtkPageSetup *setup)
+{
+ g_return_if_fail (EOM_IS_PRINT_PREVIEW (preview));
+ g_return_if_fail (GTK_IS_PAGE_SETUP (setup));
+
+ g_object_set (G_OBJECT (preview),
+ "page-left-margin", gtk_page_setup_get_left_margin (setup, GTK_UNIT_INCH),
+ "page-right-margin", gtk_page_setup_get_right_margin (setup, GTK_UNIT_INCH),
+ "page-top-margin", gtk_page_setup_get_top_margin (setup, GTK_UNIT_INCH),
+ "page-bottom-margin", gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_INCH),
+ "paper-width", gtk_page_setup_get_paper_width (setup, GTK_UNIT_INCH),
+ "paper-height", gtk_page_setup_get_paper_height (setup, GTK_UNIT_INCH),
+ NULL);
+
+}
+
+/**
+ * eom_print_preview_get_image_position:
+ * @preview: a #EomPrintPreview
+ * @x: a pointer to a #gdouble, or %NULL to ignore it
+ * @y: a pointer to a #gdouble, or %NULL to ignore it
+ *
+ * Gets current image position in inches, relative to the margins. A
+ * (0, 0) position is the intersection between the left and top margins.
+ **/
+void
+eom_print_preview_get_image_position (EomPrintPreview *preview,
+ gdouble *x,
+ gdouble *y)
+{
+ EomPrintPreviewPrivate *priv;
+ gdouble width, height;
+
+ g_return_if_fail (EOM_IS_PRINT_PREVIEW (preview));
+
+ priv = preview->priv;
+
+ if (x != NULL) {
+ width = gdk_pixbuf_get_width (priv->image) * priv->i_scale / 72.;
+ *x = priv->image_x_align * (priv->p_width - priv->l_margin - priv->r_margin - width);
+ }
+ if (y != NULL) {
+ height = gdk_pixbuf_get_height (priv->image) * priv->i_scale / 72.;
+ *y = priv->image_y_align * (priv->p_height - priv->t_margin - priv->b_margin - height);
+ }
+}
+
+/**
+ * eom_print_preview_set_image_position:
+ * @preview: a #EomPrintPreview
+ * @x: The X coordinate, in inches, or -1 to ignore it.
+ * @y: The Y coordinate, in inches, or -1 to ignore it.
+ *
+ * Sets the image position. You can pass -1 to one of the coordinates if you
+ * only want to set the other.
+ **/
+void
+eom_print_preview_set_image_position (EomPrintPreview *preview,
+ gdouble x,
+ gdouble y)
+{
+ EomPrintPreviewPrivate *priv;
+ gfloat x_align, y_align;
+ gdouble width, height;
+
+ g_return_if_fail (EOM_IS_PRINT_PREVIEW (preview));
+
+ priv = preview->priv;
+
+ if (x != -1) {
+ width = gdk_pixbuf_get_width (priv->image) * priv->i_scale / 72.;
+ x_align = CLAMP (x/(priv->p_width - priv->l_margin - priv->r_margin - width), 0, 1);
+ g_object_set (preview, "image-x-align", x_align, NULL);
+ }
+
+ if (y != -1) {
+ height = gdk_pixbuf_get_height (priv->image) * priv->i_scale / 72.;
+ y_align = CLAMP (y/(priv->p_height - priv->t_margin - priv->b_margin - height), 0, 1);
+ g_object_set (preview, "image-y-align", y_align, NULL);
+ }
+}
+
+/**
+ * eom_print_preview_set_scale:
+ * @preview: a #EomPrintPreview
+ * @scale: a scale value, between 0 and 1.
+ *
+ * Sets the scale for the image.
+ **/
+void
+eom_print_preview_set_scale (EomPrintPreview *preview,
+ gfloat scale)
+{
+ g_return_if_fail (EOM_IS_PRINT_PREVIEW (preview));
+
+ g_object_set (preview,
+ "image-scale", scale,
+ NULL);
+}
diff --git a/src/eom-print-preview.h b/src/eom-print-preview.h
new file mode 100644
index 0000000..e245146
--- /dev/null
+++ b/src/eom-print-preview.h
@@ -0,0 +1,84 @@
+/* Eye of MATE -- Print Preview Widget
+ *
+ * Copyright (C) 2006-2007 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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_PRINT_PREVIEW_H_
+#define _EOM_PRINT_PREVIEW_H_
+
+G_BEGIN_DECLS
+
+typedef struct _EomPrintPreview EomPrintPreview;
+typedef struct _EomPrintPreviewClass EomPrintPreviewClass;
+typedef struct _EomPrintPreviewPrivate EomPrintPreviewPrivate;
+
+#define EOM_TYPE_PRINT_PREVIEW (eom_print_preview_get_type ())
+#define EOM_PRINT_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_PRINT_PREVIEW, EomPrintPreview))
+#define EOM_PRINT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_PRINT_PREVIEW, EomPrintPreviewClass))
+#define EOM_IS_PRINT_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_PRINT_PREVIEW))
+#define EOM_IS_PRINT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_PRINT_PREVIEW))
+
+struct _EomPrintPreview {
+ GtkAspectFrame aspect_frame;
+
+ EomPrintPreviewPrivate *priv;
+};
+
+struct _EomPrintPreviewClass {
+ GtkAspectFrameClass parent_class;
+
+};
+
+G_GNUC_INTERNAL
+GType eom_print_preview_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkWidget *eom_print_preview_new (void);
+
+G_GNUC_INTERNAL
+GtkWidget *eom_print_preview_new_with_pixbuf (GdkPixbuf *pixbuf);
+
+G_GNUC_INTERNAL
+void eom_print_preview_set_page_margins (EomPrintPreview *preview,
+ gfloat l_margin,
+ gfloat r_margin,
+ gfloat t_margin,
+ gfloat b_margin);
+
+G_GNUC_INTERNAL
+void eom_print_preview_set_from_page_setup (EomPrintPreview *preview,
+ GtkPageSetup *setup);
+
+G_GNUC_INTERNAL
+void eom_print_preview_get_image_position (EomPrintPreview *preview,
+ gdouble *x,
+ gdouble *y);
+
+G_GNUC_INTERNAL
+void eom_print_preview_set_image_position (EomPrintPreview *preview,
+ gdouble x,
+ gdouble y);
+
+G_GNUC_INTERNAL
+void eom_print_preview_set_scale (EomPrintPreview *preview,
+ gfloat scale);
+
+G_END_DECLS
+
+#endif /* _EOM_PRINT_PREVIEW_H_ */
diff --git a/src/eom-print.c b/src/eom-print.c
new file mode 100644
index 0000000..17c0ed9
--- /dev/null
+++ b/src/eom-print.c
@@ -0,0 +1,369 @@
+/* Eye of Mate - Print Operations
+ *
+ * Copyright (C) 2005-2008 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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 "config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include "eom-image.h"
+#include "eom-print.h"
+#include "eom-print-image-setup.h"
+#include "eom-util.h"
+#include "eom-debug.h"
+
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg-cairo.h>
+#endif
+
+#define EOM_PRINT_SETTINGS_FILE "eom-print-settings.ini"
+#define EOM_PAGE_SETUP_GROUP "Page Setup"
+#define EOM_PRINT_SETTINGS_GROUP "Print Settings"
+
+typedef struct {
+ EomImage *image;
+ gdouble left_margin;
+ gdouble top_margin;
+ gdouble scale_factor;
+ GtkUnit unit;
+} EomPrintData;
+
+static void
+eom_print_draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ gpointer user_data)
+{
+ cairo_t *cr;
+ gdouble dpi_x, dpi_y;
+ gdouble x0, y0;
+ gdouble scale_factor;
+ gdouble p_width, p_height;
+ gint width, height;
+ EomPrintData *data;
+ GtkPageSetup *page_setup;
+
+ eom_debug (DEBUG_PRINTING);
+
+ data = (EomPrintData *) user_data;
+
+ scale_factor = data->scale_factor/100;
+
+ dpi_x = gtk_print_context_get_dpi_x (context);
+ dpi_y = gtk_print_context_get_dpi_y (context);
+
+ switch (data->unit) {
+ case GTK_UNIT_INCH:
+ x0 = data->left_margin * dpi_x;
+ y0 = data->top_margin * dpi_y;
+ break;
+ case GTK_UNIT_MM:
+ x0 = data->left_margin * dpi_x/25.4;
+ y0 = data->top_margin * dpi_y/25.4;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ cr = gtk_print_context_get_cairo_context (context);
+
+ cairo_translate (cr, x0, y0);
+
+ page_setup = gtk_print_context_get_page_setup (context);
+ p_width = gtk_page_setup_get_page_width (page_setup, GTK_UNIT_POINTS);
+ p_height = gtk_page_setup_get_page_height (page_setup, GTK_UNIT_POINTS);
+
+ eom_image_get_size (data->image, &width, &height);
+
+ /* this is both a workaround for a bug in cairo's PDF backend, and
+ a way to ensure we are not printing outside the page margins */
+ cairo_rectangle (cr, 0, 0, MIN (width*scale_factor, p_width), MIN (height*scale_factor, p_height));
+ cairo_clip (cr);
+
+ cairo_scale (cr, scale_factor, scale_factor);
+
+#ifdef HAVE_RSVG
+ if (eom_image_is_svg (data->image))
+ {
+ RsvgHandle *svg = eom_image_get_svg (data->image);
+
+ rsvg_handle_render_cairo (svg, cr);
+ } else
+#endif
+ {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = eom_image_get_pixbuf (data->image);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+ g_object_unref (pixbuf);
+ }
+}
+
+static GObject *
+eom_print_create_custom_widget (GtkPrintOperation *operation,
+ gpointer user_data)
+{
+ GtkPageSetup *page_setup;
+ EomPrintData *data;
+
+ eom_debug (DEBUG_PRINTING);
+
+ data = (EomPrintData *)user_data;
+
+ page_setup = gtk_print_operation_get_default_page_setup (operation);
+
+ if (page_setup == NULL)
+ page_setup = gtk_page_setup_new ();
+
+ return G_OBJECT (eom_print_image_setup_new (data->image, page_setup));
+}
+
+static void
+eom_print_custom_widget_apply (GtkPrintOperation *operation,
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ EomPrintData *data;
+ gdouble left_margin, top_margin, scale_factor;
+ GtkUnit unit;
+
+ eom_debug (DEBUG_PRINTING);
+
+ data = (EomPrintData *)user_data;
+
+ eom_print_image_setup_get_options (EOM_PRINT_IMAGE_SETUP (widget),
+ &left_margin, &top_margin,
+ &scale_factor, &unit);
+
+ data->left_margin = left_margin;
+ data->top_margin = top_margin;
+ data->scale_factor = scale_factor;
+ data->unit = unit;
+}
+
+static void
+eom_print_end_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data)
+{
+ EomPrintData *data = (EomPrintData*) user_data;
+
+ eom_debug (DEBUG_PRINTING);
+
+ g_object_unref (data->image);
+ g_slice_free (EomPrintData, data);
+}
+
+GtkPrintOperation *
+eom_print_operation_new (EomImage *image,
+ GtkPrintSettings *print_settings,
+ GtkPageSetup *page_setup)
+{
+ GtkPrintOperation *print;
+ EomPrintData *data;
+
+ eom_debug (DEBUG_PRINTING);
+
+ print = gtk_print_operation_new ();
+
+ data = g_slice_new0 (EomPrintData);
+
+ data->left_margin = 0;
+ data->top_margin = 0;
+ data->scale_factor = 100;
+ data->image = g_object_ref (image);
+ data->unit = GTK_UNIT_INCH;
+
+ gtk_print_operation_set_print_settings (print, print_settings);
+ gtk_print_operation_set_default_page_setup (print,
+ page_setup);
+ gtk_print_operation_set_n_pages (print, 1);
+ gtk_print_operation_set_job_name (print,
+ eom_image_get_caption (image));
+ gtk_print_operation_set_embed_page_setup (print, TRUE);
+
+ g_signal_connect (print, "draw_page",
+ G_CALLBACK (eom_print_draw_page),
+ data);
+ g_signal_connect (print, "create-custom-widget",
+ G_CALLBACK (eom_print_create_custom_widget),
+ data);
+ g_signal_connect (print, "custom-widget-apply",
+ G_CALLBACK (eom_print_custom_widget_apply),
+ data);
+ g_signal_connect (print, "end-print",
+ G_CALLBACK (eom_print_end_print),
+ data);
+ g_signal_connect (print, "update-custom-widget",
+ G_CALLBACK (eom_print_image_setup_update),
+ data);
+
+ gtk_print_operation_set_custom_tab_label (print, _("Image Settings"));
+
+ return print;
+}
+
+static GKeyFile *
+eom_print_get_key_file (void)
+{
+ GKeyFile *key_file;
+ GError *error = NULL;
+ gchar *filename;
+ GFile *file;
+ const gchar *dot_dir = eom_util_dot_dir ();
+
+ filename = g_build_filename (dot_dir, EOM_PRINT_SETTINGS_FILE, NULL);
+
+ file = g_file_new_for_path (filename);
+ key_file = g_key_file_new ();
+
+ if (g_file_query_exists (file, NULL)) {
+ g_key_file_load_from_file (key_file, filename,
+ G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+ &error);
+ if (error) {
+ g_warning ("Error loading print settings file: %s", error->message);
+ g_error_free (error);
+ g_object_unref (file);
+ g_free (filename);
+ g_key_file_free (key_file);
+ return NULL;
+ }
+ }
+
+ g_object_unref (file);
+ g_free (filename);
+
+ return key_file;
+}
+
+GtkPageSetup *
+eom_print_get_page_setup (void)
+{
+ GtkPageSetup *page_setup;
+ GKeyFile *key_file;
+ GError *error = NULL;
+
+ key_file = eom_print_get_key_file ();
+
+ if (key_file && g_key_file_has_group (key_file, EOM_PAGE_SETUP_GROUP)) {
+ page_setup = gtk_page_setup_new_from_key_file (key_file, EOM_PAGE_SETUP_GROUP, &error);
+ } else {
+ page_setup = gtk_page_setup_new ();
+ }
+
+ if (error) {
+ page_setup = gtk_page_setup_new ();
+
+ g_warning ("Error loading print settings file: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (key_file)
+ g_key_file_free (key_file);
+
+ return page_setup;
+}
+
+static void
+eom_print_save_key_file (GKeyFile *key_file)
+{
+ gchar *filename;
+ gchar *data;
+ GError *error = NULL;
+ const gchar *dot_dir = eom_util_dot_dir ();
+
+ filename = g_build_filename (dot_dir, EOM_PRINT_SETTINGS_FILE, NULL);
+
+ data = g_key_file_to_data (key_file, NULL, NULL);
+
+ g_file_set_contents (filename, data, -1, &error);
+
+ if (error) {
+ g_warning ("Error saving print settings file: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_free (filename);
+ g_free (data);
+}
+
+void
+eom_print_set_page_setup (GtkPageSetup *page_setup)
+{
+ GKeyFile *key_file;
+
+ key_file = eom_print_get_key_file ();
+
+ if (key_file == NULL) {
+ key_file = g_key_file_new ();
+ }
+
+ gtk_page_setup_to_key_file (page_setup, key_file, EOM_PAGE_SETUP_GROUP);
+ eom_print_save_key_file (key_file);
+
+ g_key_file_free (key_file);
+}
+
+GtkPrintSettings *
+eom_print_get_print_settings (void)
+{
+ GtkPrintSettings *print_settings;
+ GError *error = NULL;
+ GKeyFile *key_file;
+
+ key_file = eom_print_get_key_file ();
+
+ if (key_file && g_key_file_has_group (key_file, EOM_PRINT_SETTINGS_GROUP)) {
+ print_settings = gtk_print_settings_new_from_key_file (key_file, EOM_PRINT_SETTINGS_GROUP, &error);
+ } else {
+ print_settings = gtk_print_settings_new ();
+ }
+
+ if (error) {
+ print_settings = gtk_print_settings_new ();
+
+ g_warning ("Error loading print settings file: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (key_file)
+ g_key_file_free (key_file);
+
+ return print_settings;
+}
+
+void
+eom_print_set_print_settings (GtkPrintSettings *print_settings)
+{
+ GKeyFile *key_file;
+
+ key_file = eom_print_get_key_file ();
+
+ if (key_file == NULL) {
+ key_file = g_key_file_new ();
+ }
+
+ gtk_print_settings_to_key_file (print_settings, key_file, EOM_PRINT_SETTINGS_GROUP);
+ eom_print_save_key_file (key_file);
+
+ g_key_file_free (key_file);
+}
diff --git a/src/eom-print.h b/src/eom-print.h
new file mode 100644
index 0000000..ab3e857
--- /dev/null
+++ b/src/eom-print.h
@@ -0,0 +1,49 @@
+/* Eye of Mate - Print Operations
+ *
+ * Copyright (C) 2005-2008 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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_PRINT_H__
+#define __EOM_PRINT_H__
+
+#include "eom-image.h"
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+GtkPrintOperation* eom_print_operation_new (EomImage *image,
+ GtkPrintSettings *print_settings,
+ GtkPageSetup *page_setup);
+
+G_GNUC_INTERNAL
+GtkPageSetup* eom_print_get_page_setup (void);
+
+G_GNUC_INTERNAL
+void eom_print_set_page_setup (GtkPageSetup *page_setup);
+
+G_GNUC_INTERNAL
+GtkPrintSettings * eom_print_get_print_settings (void);
+
+G_GNUC_INTERNAL
+void eom_print_set_print_settings (GtkPrintSettings *print_settings);
+
+G_END_DECLS
+
+#endif /* __EOM_PRINT_H__ */
diff --git a/src/eom-properties-dialog.c b/src/eom-properties-dialog.c
new file mode 100644
index 0000000..21a346f
--- /dev/null
+++ b/src/eom-properties-dialog.c
@@ -0,0 +1,834 @@
+/* Eye Of Mate - Image Properties Dialog
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ * Hubert Figuiere <[email protected]> (XMP support)
+ *
+ * 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-properties-dialog.h"
+#include "eom-image.h"
+#include "eom-util.h"
+#include "eom-thumb-view.h"
+
+#if HAVE_EXIF
+#include "eom-exif-util.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#if HAVE_EXEMPI
+#include <exempi/xmp.h>
+#include <exempi/xmpconsts.h>
+#endif
+#if HAVE_EXIF || HAVE_EXEMPI
+#define HAVE_METADATA 1
+#endif
+
+#if HAVE_METADATA
+#include "eom-exif-details.h"
+#endif
+
+#define EOM_PROPERTIES_DIALOG_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PROPERTIES_DIALOG, EomPropertiesDialogPrivate))
+
+G_DEFINE_TYPE (EomPropertiesDialog, eom_properties_dialog, EOM_TYPE_DIALOG);
+
+enum {
+ PROP_0,
+ PROP_THUMBVIEW,
+ PROP_NETBOOK_MODE
+};
+
+struct _EomPropertiesDialogPrivate {
+ EomThumbView *thumbview;
+
+ gboolean update_page;
+ EomPropertiesDialogPage current_page;
+
+ GtkWidget *notebook;
+ GtkWidget *close_button;
+ GtkWidget *next_button;
+ GtkWidget *previous_button;
+
+ GtkWidget *general_box;
+ GtkWidget *thumbnail_image;
+ GtkWidget *name_label;
+ GtkWidget *width_label;
+ GtkWidget *height_label;
+ GtkWidget *type_label;
+ GtkWidget *bytes_label;
+ GtkWidget *location_label;
+ GtkWidget *created_label;
+ GtkWidget *modified_label;
+#ifdef HAVE_EXIF
+ GtkWidget *exif_aperture_label;
+ GtkWidget *exif_exposure_label;
+ GtkWidget *exif_focal_label;
+ GtkWidget *exif_flash_label;
+ GtkWidget *exif_iso_label;
+ GtkWidget *exif_metering_label;
+ GtkWidget *exif_model_label;
+ GtkWidget *exif_date_label;
+#endif
+#ifdef HAVE_EXEMPI
+ GtkWidget *xmp_location_label;
+ GtkWidget *xmp_description_label;
+ GtkWidget *xmp_keywords_label;
+ GtkWidget *xmp_creator_label;
+ GtkWidget *xmp_rights_label;
+#endif
+#if HAVE_METADATA
+ GtkWidget *exif_box;
+ GtkWidget *exif_details_expander;
+ GtkWidget *exif_details;
+ GtkWidget *metadata_details_box;
+ GtkWidget *metadata_details_sw;
+#endif
+
+ gboolean netbook_mode;
+};
+
+static void
+pd_update_general_tab (EomPropertiesDialog *prop_dlg,
+ EomImage *image)
+{
+ gchar *bytes_str, *dir_str, *uri_str;
+ gchar *width_str, *height_str;
+ GFile *file;
+ GFileInfo *file_info;
+ const char *mime_str;
+ char *type_str;
+ gint width, height;
+ goffset bytes;
+
+ g_object_set (G_OBJECT (prop_dlg->priv->thumbnail_image),
+ "pixbuf", eom_image_get_thumbnail (image),
+ NULL);
+
+ gtk_label_set_text (GTK_LABEL (prop_dlg->priv->name_label),
+ eom_image_get_caption (image));
+
+ eom_image_get_size (image, &width, &height);
+
+ width_str = g_strdup_printf ("%d %s", width,
+ ngettext ("pixel", "pixels", width));
+ height_str = g_strdup_printf ("%d %s", height,
+ ngettext ("pixel", "pixels", height));
+
+ gtk_label_set_text (GTK_LABEL (prop_dlg->priv->width_label), width_str);
+
+ gtk_label_set_text (GTK_LABEL (prop_dlg->priv->height_label),
+ height_str);
+
+ g_free (height_str);
+ g_free (width_str);
+
+ file = eom_image_get_file (image);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ if (file_info == NULL) {
+ type_str = g_strdup (_("Unknown"));
+ } else {
+ mime_str = g_file_info_get_content_type (file_info);
+ type_str = g_content_type_get_description (mime_str);
+ g_object_unref (file_info);
+ }
+
+ gtk_label_set_text (GTK_LABEL (prop_dlg->priv->type_label), type_str);
+
+ bytes = eom_image_get_bytes (image);
+ bytes_str = g_format_size_for_display (bytes);
+
+ gtk_label_set_text (GTK_LABEL (prop_dlg->priv->bytes_label), bytes_str);
+
+ uri_str = eom_image_get_uri_for_display (image);
+ dir_str = g_path_get_dirname (uri_str);
+ gtk_label_set_text (GTK_LABEL (prop_dlg->priv->location_label),
+ dir_str);
+
+ g_free (type_str);
+ g_free (bytes_str);
+ g_free (dir_str);
+ g_free (uri_str);
+}
+
+#if HAVE_EXIF
+static void
+eom_exif_set_label (GtkWidget *w, ExifData *exif_data, gint tag_id)
+{
+ gchar exif_buffer[512];
+ const gchar *buf_ptr;
+ gchar *label_text = NULL;
+
+ if (exif_data) {
+ buf_ptr = eom_exif_util_get_value (exif_data, tag_id,
+ exif_buffer, 512);
+
+ if (tag_id == EXIF_TAG_DATE_TIME_ORIGINAL && buf_ptr)
+ label_text = eom_exif_util_format_date (buf_ptr);
+ else
+ label_text = eom_util_make_valid_utf8 (buf_ptr);
+ }
+
+ gtk_label_set_text (GTK_LABEL (w), label_text);
+ g_free (label_text);
+}
+
+static void
+eom_exif_set_focal_length_label (GtkWidget *w, ExifData *exif_data)
+{
+ ExifEntry *entry = NULL, *entry35mm = NULL;
+ ExifByteOrder byte_order;
+ gfloat f_val = 0.0;
+ gchar *fl_text = NULL,*fl35_text = NULL;
+
+ /* If no ExifData is supplied the label will be
+ * cleared later as fl35_text is NULL. */
+ if (exif_data != NULL) {
+ entry = exif_data_get_entry (exif_data, EXIF_TAG_FOCAL_LENGTH);
+ entry35mm = exif_data_get_entry (exif_data,
+ EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM);
+ byte_order = exif_data_get_byte_order (exif_data);
+ }
+
+ if (entry && G_LIKELY (entry->format == EXIF_FORMAT_RATIONAL)) {
+ ExifRational value;
+
+ /* Decode value by hand as libexif is not necessarily returning
+ * it in the format we want it to be.
+ */
+ value = exif_get_rational (entry->data, byte_order);
+ /* Guard against div by zero */
+ if (G_LIKELY(value.denominator != 0))
+ f_val = (gfloat)value.numerator/
+ (gfloat)value.denominator;
+
+ /* TRANSLATORS: This is the actual focal length used when
+ the image was taken.*/
+ fl_text = g_strdup_printf (_("%.1f (lens)"), f_val);
+
+ }
+ if (entry35mm && G_LIKELY (entry35mm->format == EXIF_FORMAT_SHORT)) {
+ ExifShort s_val;
+
+ s_val = exif_get_short (entry35mm->data, byte_order);
+
+ /* Print as float to get a similar look as above. */
+ /* TRANSLATORS: This is the equivalent focal length assuming
+ a 35mm film camera. */
+ fl35_text = g_strdup_printf(_("%.1f (35mm film)"),(float)s_val);
+ }
+
+ if (fl_text) {
+ if (fl35_text) {
+ gchar *merged_txt;
+
+ merged_txt = g_strconcat (fl35_text,", ", fl_text, NULL);
+ gtk_label_set_text (GTK_LABEL (w), merged_txt);
+ g_free (merged_txt);
+ } else {
+ gtk_label_set_text (GTK_LABEL (w), fl_text);
+ }
+ } else {
+ /* This will also clear the label if no ExifData was supplied */
+ gtk_label_set_text (GTK_LABEL (w), fl35_text);
+ }
+
+ g_free (fl35_text);
+ g_free (fl_text);
+
+}
+#endif
+
+#if HAVE_EXEMPI
+static void
+eom_xmp_set_label (XmpPtr xmp,
+ const char *ns,
+ const char *propname,
+ GtkWidget *w)
+{
+ uint32_t options;
+
+ XmpStringPtr value = xmp_string_new ();
+
+ if (xmp_get_property (xmp, ns, propname, value, &options)) {
+ if (XMP_IS_PROP_SIMPLE (options)) {
+ gtk_label_set_text (GTK_LABEL (w), xmp_string_cstr (value));
+ } else if (XMP_IS_PROP_ARRAY (options)) {
+ XmpIteratorPtr iter = xmp_iterator_new (xmp,
+ ns,
+ propname,
+ XMP_ITER_JUSTLEAFNODES);
+
+ GString *string = g_string_new ("");
+
+ if (iter) {
+ gboolean first = TRUE;
+
+ while (xmp_iterator_next (iter, NULL, NULL, value, &options)
+ && !XMP_IS_PROP_QUALIFIER (options)) {
+
+ if (!first) {
+ g_string_append_printf(string, ", ");
+ } else {
+ first = FALSE;
+ }
+
+ g_string_append_printf (string,
+ "%s",
+ xmp_string_cstr (value));
+ }
+
+ xmp_iterator_free (iter);
+ }
+
+ gtk_label_set_text (GTK_LABEL (w), string->str);
+ g_string_free (string, TRUE);
+ }
+ } else {
+ /* Property was not found */
+ /* Clear label so it won't show bogus data */
+ gtk_label_set_text (GTK_LABEL (w), NULL);
+ }
+
+ xmp_string_free (value);
+}
+#endif
+
+#if HAVE_METADATA
+static void
+pd_update_metadata_tab (EomPropertiesDialog *prop_dlg,
+ EomImage *image)
+{
+ EomPropertiesDialogPrivate *priv;
+ GtkNotebook *notebook;
+#if HAVE_EXIF
+ ExifData *exif_data;
+#endif
+#if HAVE_EXEMPI
+ XmpPtr xmp_data;
+#endif
+
+ g_return_if_fail (EOM_IS_PROPERTIES_DIALOG (prop_dlg));
+
+ priv = prop_dlg->priv;
+
+ notebook = GTK_NOTEBOOK (priv->notebook);
+
+ if (TRUE
+#if HAVE_EXIF
+ && !eom_image_has_data (image, EOM_IMAGE_DATA_EXIF)
+#endif
+#if HAVE_EXEMPI
+ && !eom_image_has_data (image, EOM_IMAGE_DATA_XMP)
+#endif
+ ) {
+ if (gtk_notebook_get_current_page (notebook) == EOM_PROPERTIES_DIALOG_PAGE_EXIF) {
+ gtk_notebook_prev_page (notebook);
+ } else if (gtk_notebook_get_current_page (notebook) == EOM_PROPERTIES_DIALOG_PAGE_DETAILS) {
+ gtk_notebook_set_current_page (notebook, EOM_PROPERTIES_DIALOG_PAGE_GENERAL);
+ }
+
+ if (gtk_widget_get_visible (priv->exif_box)) {
+ gtk_widget_hide_all (priv->exif_box);
+ }
+ if (gtk_widget_get_visible (priv->metadata_details_box)) {
+ gtk_widget_hide_all (priv->metadata_details_box);
+ }
+
+ return;
+ } else {
+ if (!gtk_widget_get_visible (priv->exif_box))
+ gtk_widget_show_all (priv->exif_box);
+ if (priv->netbook_mode &&
+ !gtk_widget_get_visible (priv->metadata_details_box)) {
+ gtk_widget_show_all (priv->metadata_details_box);
+ gtk_widget_hide_all (priv->exif_details_expander);
+ }
+ }
+
+#if HAVE_EXIF
+ exif_data = (ExifData *) eom_image_get_exif_info (image);
+
+ eom_exif_set_label (priv->exif_aperture_label,
+ exif_data, EXIF_TAG_FNUMBER);
+
+ eom_exif_set_label (priv->exif_exposure_label,
+ exif_data, EXIF_TAG_EXPOSURE_TIME);
+
+ eom_exif_set_focal_length_label (priv->exif_focal_label, exif_data);
+
+ eom_exif_set_label (priv->exif_flash_label,
+ exif_data, EXIF_TAG_FLASH);
+
+ eom_exif_set_label (priv->exif_iso_label,
+ exif_data, EXIF_TAG_ISO_SPEED_RATINGS);
+
+
+ eom_exif_set_label (priv->exif_metering_label,
+ exif_data, EXIF_TAG_METERING_MODE);
+
+ eom_exif_set_label (priv->exif_model_label,
+ exif_data, EXIF_TAG_MODEL);
+
+ eom_exif_set_label (priv->exif_date_label,
+ exif_data, EXIF_TAG_DATE_TIME_ORIGINAL);
+
+ eom_exif_details_update (EOM_EXIF_DETAILS (priv->exif_details),
+ exif_data);
+
+ /* exif_data_unref can handle NULL-values */
+ exif_data_unref(exif_data);
+#endif
+
+#if HAVE_EXEMPI
+ xmp_data = (XmpPtr) eom_image_get_xmp_info (image);
+
+ if (xmp_data != NULL) {
+ eom_xmp_set_label (xmp_data,
+ NS_IPTC4XMP,
+ "Location",
+ priv->xmp_location_label);
+
+ eom_xmp_set_label (xmp_data,
+ NS_DC,
+ "description",
+ priv->xmp_description_label);
+
+ eom_xmp_set_label (xmp_data,
+ NS_DC,
+ "subject",
+ priv->xmp_keywords_label);
+
+ eom_xmp_set_label (xmp_data,
+ NS_DC,
+ "creator",
+ priv->xmp_creator_label);
+
+ eom_xmp_set_label (xmp_data,
+ NS_DC,
+ "rights",
+ priv->xmp_rights_label);
+
+ eom_exif_details_xmp_update (EOM_EXIF_DETAILS (priv->exif_details), xmp_data);
+
+ xmp_free (xmp_data);
+ } else {
+ /* Image has no XMP data */
+
+ /* Clear the labels so they won't display foreign data.*/
+
+ gtk_label_set_text (GTK_LABEL (priv->xmp_location_label), NULL);
+ gtk_label_set_text (GTK_LABEL (priv->xmp_description_label),
+ NULL);
+ gtk_label_set_text (GTK_LABEL (priv->xmp_keywords_label), NULL);
+ gtk_label_set_text (GTK_LABEL (priv->xmp_creator_label), NULL);
+ gtk_label_set_text (GTK_LABEL (priv->xmp_rights_label), NULL);
+ }
+#endif
+}
+
+static gboolean
+pd_resize_dialog (gpointer user_data)
+{
+ gint width, height;
+
+ gtk_window_get_size (GTK_WINDOW (user_data),
+ &width,
+ &height);
+
+ gtk_window_resize (GTK_WINDOW (user_data), width, 1);
+
+ return FALSE;
+}
+
+static void
+pd_exif_details_activated_cb (GtkExpander *expander,
+ GParamSpec *param_spec,
+ GtkWidget *dialog)
+{
+ gboolean expanded;
+
+ expanded = gtk_expander_get_expanded (expander);
+
+ /*FIXME: this is depending on the expander animation
+ * duration. Need to find a safer way for doing that. */
+ if (!expanded)
+ g_timeout_add (150, pd_resize_dialog, dialog);
+}
+#endif
+
+static void
+pd_close_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ eom_dialog_hide (EOM_DIALOG (user_data));
+}
+
+static gboolean
+eom_properties_dialog_page_switch (GtkNotebook *notebook,
+ GtkNotebookPage *page,
+ gint page_index,
+ EomPropertiesDialog *prop_dlg)
+{
+
+ if (prop_dlg->priv->update_page)
+ prop_dlg->priv->current_page = page_index;
+
+ return TRUE;
+}
+
+static gint
+eom_properties_dialog_delete (GtkWidget *widget,
+ GdkEventAny *event,
+ gpointer user_data)
+{
+ g_return_val_if_fail (EOM_IS_PROPERTIES_DIALOG (user_data), FALSE);
+
+ eom_dialog_hide (EOM_DIALOG (user_data));
+
+ return TRUE;
+}
+
+void
+eom_properties_dialog_set_netbook_mode (EomPropertiesDialog *dlg,
+ gboolean enable)
+{
+ EomPropertiesDialogPrivate *priv;
+
+ g_return_if_fail (EOM_IS_PROPERTIES_DIALOG (dlg));
+
+ priv = dlg->priv;
+
+ if (priv->netbook_mode == enable)
+ return;
+
+ priv->netbook_mode = enable;
+
+#ifdef HAVE_METADATA
+ if (enable) {
+ gtk_widget_reparent (priv->metadata_details_sw,
+ priv->metadata_details_box);
+ // Only show details box if metadata is being displayed
+ if (gtk_widget_get_visible (priv->exif_box))
+ gtk_widget_show_all (priv->metadata_details_box);
+
+ gtk_widget_hide_all (priv->exif_details_expander);
+ } else {
+ gtk_widget_reparent (priv->metadata_details_sw,
+ priv->exif_details_expander);
+ gtk_widget_show_all (priv->exif_details_expander);
+
+ if (gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)) == EOM_PROPERTIES_DIALOG_PAGE_DETAILS)
+ gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
+ gtk_widget_hide_all (priv->metadata_details_box);
+ }
+#endif
+}
+
+static void
+eom_properties_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomPropertiesDialog *prop_dlg = EOM_PROPERTIES_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_THUMBVIEW:
+ prop_dlg->priv->thumbview = g_value_get_object (value);
+ break;
+ case PROP_NETBOOK_MODE:
+ eom_properties_dialog_set_netbook_mode (prop_dlg,
+ g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
+ pspec);
+ break;
+ }
+}
+
+static void
+eom_properties_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomPropertiesDialog *prop_dlg = EOM_PROPERTIES_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_THUMBVIEW:
+ g_value_set_object (value, prop_dlg->priv->thumbview);
+ break;
+ case PROP_NETBOOK_MODE:
+ g_value_set_boolean (value,
+ prop_dlg->priv->netbook_mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
+ pspec);
+ break;
+ }
+}
+
+static void
+eom_properties_dialog_dispose (GObject *object)
+{
+ EomPropertiesDialog *prop_dlg;
+ EomPropertiesDialogPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (EOM_IS_PROPERTIES_DIALOG (object));
+
+ prop_dlg = EOM_PROPERTIES_DIALOG (object);
+ priv = prop_dlg->priv;
+
+ if (priv->thumbview) {
+ g_object_unref (priv->thumbview);
+ priv->thumbview = NULL;
+ }
+
+ G_OBJECT_CLASS (eom_properties_dialog_parent_class)->dispose (object);
+}
+
+static void
+eom_properties_dialog_class_init (EomPropertiesDialogClass *class)
+{
+ GObjectClass *g_object_class = (GObjectClass *) class;
+
+ g_object_class->dispose = eom_properties_dialog_dispose;
+ g_object_class->set_property = eom_properties_dialog_set_property;
+ g_object_class->get_property = eom_properties_dialog_get_property;
+
+ g_object_class_install_property (g_object_class,
+ PROP_THUMBVIEW,
+ g_param_spec_object ("thumbview",
+ "Thumbview",
+ "Thumbview",
+ EOM_TYPE_THUMB_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+ g_object_class_install_property (g_object_class, PROP_NETBOOK_MODE,
+ g_param_spec_boolean ("netbook-mode",
+ "Netbook Mode",
+ "Netbook Mode",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (g_object_class, sizeof (EomPropertiesDialogPrivate));
+}
+
+static void
+eom_properties_dialog_init (EomPropertiesDialog *prop_dlg)
+{
+ EomPropertiesDialogPrivate *priv;
+ GtkWidget *dlg;
+#ifndef HAVE_EXEMPI
+ GtkWidget *xmp_box, *xmp_box_label;
+#endif
+#if HAVE_METADATA
+ GtkWidget *sw;
+#endif
+
+ prop_dlg->priv = EOM_PROPERTIES_DIALOG_GET_PRIVATE (prop_dlg);
+
+ priv = prop_dlg->priv;
+
+ priv->update_page = FALSE;
+
+ eom_dialog_construct (EOM_DIALOG (prop_dlg),
+ "eom-image-properties-dialog.ui",
+ "eom_image_properties_dialog");
+
+ eom_dialog_get_controls (EOM_DIALOG (prop_dlg),
+ "eom_image_properties_dialog", &dlg,
+ "notebook", &priv->notebook,
+ "previous_button", &priv->previous_button,
+ "next_button", &priv->next_button,
+ "close_button", &priv->close_button,
+ "thumbnail_image", &priv->thumbnail_image,
+ "general_box", &priv->general_box,
+ "name_label", &priv->name_label,
+ "width_label", &priv->width_label,
+ "height_label", &priv->height_label,
+ "type_label", &priv->type_label,
+ "bytes_label", &priv->bytes_label,
+ "location_label", &priv->location_label,
+ "created_label", &priv->created_label,
+ "modified_label", &priv->modified_label,
+#ifdef HAVE_EXIF
+ "exif_aperture_label", &priv->exif_aperture_label,
+ "exif_exposure_label", &priv->exif_exposure_label,
+ "exif_focal_label", &priv->exif_focal_label,
+ "exif_flash_label", &priv->exif_flash_label,
+ "exif_iso_label", &priv->exif_iso_label,
+ "exif_metering_label", &priv->exif_metering_label,
+ "exif_model_label", &priv->exif_model_label,
+ "exif_date_label", &priv->exif_date_label,
+#endif
+#ifdef HAVE_EXEMPI
+ "xmp_location_label", &priv->xmp_location_label,
+ "xmp_description_label", &priv->xmp_description_label,
+ "xmp_keywords_label", &priv->xmp_keywords_label,
+ "xmp_creator_label", &priv->xmp_creator_label,
+ "xmp_rights_label", &priv->xmp_rights_label,
+#else
+ "xmp_box", &xmp_box,
+ "xmp_box_label", &xmp_box_label,
+#endif
+#ifdef HAVE_METADATA
+ "exif_box", &priv->exif_box,
+ "exif_details_expander", &priv->exif_details_expander,
+ "metadata_details_box", &priv->metadata_details_box,
+#endif
+ NULL);
+
+ g_signal_connect (dlg,
+ "delete-event",
+ G_CALLBACK (eom_properties_dialog_delete),
+ prop_dlg);
+
+ g_signal_connect (priv->notebook,
+ "switch-page",
+ G_CALLBACK (eom_properties_dialog_page_switch),
+ prop_dlg);
+
+ g_signal_connect (priv->close_button,
+ "clicked",
+ G_CALLBACK (pd_close_button_clicked_cb),
+ prop_dlg);
+
+ gtk_widget_set_size_request (priv->thumbnail_image, 100, 100);
+
+#ifdef HAVE_METADATA
+ sw = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+ GTK_SHADOW_IN);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ priv->exif_details = eom_exif_details_new ();
+ gtk_widget_set_size_request (priv->exif_details, -1, 170);
+ gtk_container_set_border_width (GTK_CONTAINER (sw), 6);
+
+ gtk_container_add (GTK_CONTAINER (sw), priv->exif_details);
+ gtk_widget_show_all (sw);
+
+ priv->metadata_details_sw = sw;
+
+ if (priv->netbook_mode) {
+ gtk_widget_hide_all (priv->exif_details_expander);
+ gtk_box_pack_start (GTK_BOX (priv->metadata_details_box),
+ sw, TRUE, TRUE, 6);
+ } else {
+ gtk_container_add (GTK_CONTAINER (priv->exif_details_expander),
+ sw);
+ }
+
+ g_signal_connect_after (G_OBJECT (priv->exif_details_expander),
+ "notify::expanded",
+ G_CALLBACK (pd_exif_details_activated_cb),
+ dlg);
+
+#ifndef HAVE_EXEMPI
+ gtk_widget_hide_all (xmp_box);
+ gtk_widget_hide_all (xmp_box_label);
+#endif
+
+#else
+ /* Remove pages from back to front. Otherwise the page index
+ * needs to be adjusted when deleting the next page. */
+ gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook),
+ EOM_PROPERTIES_DIALOG_PAGE_DETAILS);
+ gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook),
+ EOM_PROPERTIES_DIALOG_PAGE_EXIF);
+#endif
+}
+
+GObject *
+eom_properties_dialog_new (GtkWindow *parent,
+ EomThumbView *thumbview,
+ GtkAction *next_image_action,
+ GtkAction *previous_image_action)
+{
+ GObject *prop_dlg;
+
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+ g_return_val_if_fail (EOM_IS_THUMB_VIEW (thumbview), NULL);
+ g_return_val_if_fail (GTK_IS_ACTION (next_image_action), NULL);
+ g_return_val_if_fail (GTK_IS_ACTION (previous_image_action), NULL);
+
+ prop_dlg = g_object_new (EOM_TYPE_PROPERTIES_DIALOG,
+ "parent-window", parent,
+ "thumbview", thumbview,
+ NULL);
+
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (EOM_PROPERTIES_DIALOG (prop_dlg)->priv->next_button), next_image_action);
+
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (EOM_PROPERTIES_DIALOG (prop_dlg)->priv->previous_button), previous_image_action);
+
+ return prop_dlg;
+}
+
+void
+eom_properties_dialog_update (EomPropertiesDialog *prop_dlg,
+ EomImage *image)
+{
+ g_return_if_fail (EOM_IS_PROPERTIES_DIALOG (prop_dlg));
+
+ prop_dlg->priv->update_page = FALSE;
+
+ pd_update_general_tab (prop_dlg, image);
+
+#ifdef HAVE_METADATA
+ pd_update_metadata_tab (prop_dlg, image);
+#endif
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (prop_dlg->priv->notebook),
+ prop_dlg->priv->current_page);
+
+ prop_dlg->priv->update_page = TRUE;
+}
+
+void
+eom_properties_dialog_set_page (EomPropertiesDialog *prop_dlg,
+ EomPropertiesDialogPage page)
+{
+ g_return_if_fail (EOM_IS_PROPERTIES_DIALOG (prop_dlg));
+
+ prop_dlg->priv->current_page = page;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (prop_dlg->priv->notebook),
+ page);
+}
diff --git a/src/eom-properties-dialog.h b/src/eom-properties-dialog.h
new file mode 100644
index 0000000..c95a2dc
--- /dev/null
+++ b/src/eom-properties-dialog.h
@@ -0,0 +1,80 @@
+/* Eye Of Mate - Image Properties 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_PROPERTIES_DIALOG_H__
+#define __EOM_PROPERTIES_DIALOG_H__
+
+#include "eom-dialog.h"
+#include "eom-image.h"
+#include "eom-thumb-view.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EomPropertiesDialog EomPropertiesDialog;
+typedef struct _EomPropertiesDialogClass EomPropertiesDialogClass;
+typedef struct _EomPropertiesDialogPrivate EomPropertiesDialogPrivate;
+
+#define EOM_TYPE_PROPERTIES_DIALOG (eom_properties_dialog_get_type ())
+#define EOM_PROPERTIES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PROPERTIES_DIALOG, EomPropertiesDialog))
+#define EOM_PROPERTIES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_PROPERTIES_DIALOG, EomPropertiesDialogClass))
+#define EOM_IS_PROPERTIES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_PROPERTIES_DIALOG))
+#define EOM_IS_PROPERTIES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_PROPERTIES_DIALOG))
+#define EOM_PROPERTIES_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_PROPERTIES_DIALOG, EomPropertiesDialogClass))
+
+typedef enum {
+ EOM_PROPERTIES_DIALOG_PAGE_GENERAL = 0,
+ EOM_PROPERTIES_DIALOG_PAGE_EXIF,
+ EOM_PROPERTIES_DIALOG_PAGE_DETAILS,
+ EOM_PROPERTIES_DIALOG_N_PAGES
+} EomPropertiesDialogPage;
+
+struct _EomPropertiesDialog {
+ EomDialog dialog;
+
+ EomPropertiesDialogPrivate *priv;
+};
+
+struct _EomPropertiesDialogClass {
+ EomDialogClass parent_class;
+};
+
+GType eom_properties_dialog_get_type (void) G_GNUC_CONST;
+
+GObject *eom_properties_dialog_new (GtkWindow *parent,
+ EomThumbView *thumbview,
+ GtkAction *next_image_action,
+ GtkAction *previous_image_action);
+
+void eom_properties_dialog_update (EomPropertiesDialog *prop,
+ EomImage *image);
+
+void eom_properties_dialog_set_page (EomPropertiesDialog *prop,
+ EomPropertiesDialogPage page);
+
+void eom_properties_dialog_set_netbook_mode (EomPropertiesDialog *dlg,
+ gboolean enable);
+G_END_DECLS
+
+#endif /* __EOM_PROPERTIES_DIALOG_H__ */
diff --git a/src/eom-python-module.c b/src/eom-python-module.c
new file mode 100644
index 0000000..d0e05a5
--- /dev/null
+++ b/src/eom-python-module.c
@@ -0,0 +1,527 @@
+/*
+ * eom-python-module.c
+ * This file is part of eom
+ *
+ * Copyright (C) 2005 Raphael Slinckx
+ *
+ * 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
+
+/* This needs to be included before any standard header
+ * see http://docs.python.org/c-api/intro.html#include-files */
+#include <Python.h>
+
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+
+#include <signal.h>
+
+#include <gmodule.h>
+
+#include "eom-python-module.h"
+#include "eom-python-plugin.h"
+#include "eom-debug.h"
+
+#if PY_VERSION_HEX < 0x02050000
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+#define EOM_PYTHON_MODULE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PYTHON_MODULE, EomPythonModulePrivate))
+
+struct _EomPythonModulePrivate {
+ gchar *module;
+ gchar *path;
+ GType type;
+};
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_MODULE
+};
+
+void pyeom_register_classes (PyObject *d);
+void pyeom_add_constants (PyObject *module, const gchar *strip_prefix);
+extern PyMethodDef pyeom_functions[];
+
+static PyTypeObject *PyEomPlugin_Type;
+
+G_DEFINE_TYPE (EomPythonModule, eom_python_module, G_TYPE_TYPE_MODULE)
+
+static gboolean
+eom_python_module_load (GTypeModule *gmodule)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (gmodule);
+ PyObject *main_module, *main_locals, *locals, *key, *value;
+ PyObject *module, *fromlist;
+ Py_ssize_t pos = 0;
+
+ g_return_val_if_fail (Py_IsInitialized (), FALSE);
+
+ main_module = PyImport_AddModule ("__main__");
+
+ if (main_module == NULL) {
+ g_warning ("Could not get __main__.");
+ return FALSE;
+ }
+
+ /* If we have a special path, we register it */
+ if (priv->path != NULL) {
+ PyObject *sys_path = PySys_GetObject ("path");
+ PyObject *path = PyString_FromString (priv->path);
+
+ if (PySequence_Contains(sys_path, path) == 0)
+ PyList_Insert (sys_path, 0, path);
+
+ Py_DECREF(path);
+ }
+
+ main_locals = PyModule_GetDict (main_module);
+
+ /* We need a fromlist to be able to import modules with
+ * a '.' in the name. */
+ fromlist = PyTuple_New(0);
+
+ module = PyImport_ImportModuleEx (priv->module, main_locals, main_locals, fromlist);
+
+ Py_DECREF(fromlist);
+
+ if (!module) {
+ PyErr_Print ();
+ return FALSE;
+ }
+
+ locals = PyModule_GetDict (module);
+
+ while (PyDict_Next (locals, &pos, &key, &value)) {
+ if (!PyType_Check(value))
+ continue;
+
+ if (PyObject_IsSubclass (value, (PyObject*) PyEomPlugin_Type)) {
+ priv->type = eom_python_plugin_get_type (gmodule, value);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+eom_python_module_unload (GTypeModule *module)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (module);
+
+ eom_debug_message (DEBUG_PLUGINS, "Unloading Python module");
+
+ priv->type = 0;
+}
+
+GObject *
+eom_python_module_new_object (EomPythonModule *module)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (module);
+
+ eom_debug_message (DEBUG_PLUGINS, "Creating object of type %s", g_type_name (priv->type));
+
+ if (priv->type == 0)
+ return NULL;
+
+ return g_object_new (priv->type, NULL);
+}
+
+static void
+eom_python_module_init (EomPythonModule *module)
+{
+ eom_debug_message (DEBUG_PLUGINS, "Init of Python module");
+}
+
+static void
+eom_python_module_finalize (GObject *object)
+{
+ EomPythonModulePrivate *priv = EOM_PYTHON_MODULE_GET_PRIVATE (object);
+
+ eom_debug_message (DEBUG_PLUGINS, "Finalizing Python module %s", g_type_name (priv->type));
+
+ g_free (priv->module);
+ g_free (priv->path);
+
+ G_OBJECT_CLASS (eom_python_module_parent_class)->finalize (object);
+}
+
+static void
+eom_python_module_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ g_return_if_reached ();
+}
+
+static void
+eom_python_module_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomPythonModule *mod = EOM_PYTHON_MODULE (object);
+
+ switch (prop_id) {
+ case PROP_MODULE:
+ EOM_PYTHON_MODULE_GET_PRIVATE (mod)->module = g_value_dup_string (value);
+ break;
+
+ case PROP_PATH:
+ EOM_PYTHON_MODULE_GET_PRIVATE (mod)->path = g_value_dup_string (value);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+}
+
+static void
+eom_python_module_class_init (EomPythonModuleClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
+
+ object_class->finalize = eom_python_module_finalize;
+ object_class->get_property = eom_python_module_get_property;
+ object_class->set_property = eom_python_module_set_property;
+
+ g_object_class_install_property
+ (object_class,
+ PROP_MODULE,
+ g_param_spec_string ("module",
+ "Module Name",
+ "The Python module to load for this plugin",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path",
+ "Path",
+ "The Python path to use when loading this module",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (EomPythonModulePrivate));
+
+ module_class->load = eom_python_module_load;
+ module_class->unload = eom_python_module_unload;
+}
+
+EomPythonModule *
+eom_python_module_new (const gchar *path,
+ const gchar *module)
+{
+ EomPythonModule *result;
+
+ if (module == NULL || module[0] == '\0')
+ return NULL;
+
+ result = g_object_new (EOM_TYPE_PYTHON_MODULE,
+ "module", module,
+ "path", path,
+ NULL);
+
+ g_type_module_set_name (G_TYPE_MODULE (result), module);
+
+ return result;
+}
+
+
+static gint idle_garbage_collect_id = 0;
+
+/* C equivalent of
+ * import pygtk
+ * pygtk.require ("2.0")
+ */
+static gboolean
+check_pygtk2 (void)
+{
+ PyObject *pygtk, *mdict, *require;
+
+ /* pygtk.require("2.0") */
+ pygtk = PyImport_ImportModule ("pygtk");
+
+ if (pygtk == NULL) {
+ g_warning ("Error initializing Python interpreter: could not import pygtk.");
+ return FALSE;
+ }
+
+ mdict = PyModule_GetDict (pygtk);
+
+ require = PyDict_GetItemString (mdict, "require");
+
+ PyObject_CallObject (require,
+ Py_BuildValue ("(S)", PyString_FromString ("2.0")));
+
+ if (PyErr_Occurred()) {
+ g_warning ("Error initializing Python interpreter: pygtk 2 is required.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Note: the following two functions are needed because
+ * init_pyobject and init_pygtk which are *macros* which in case
+ * case of error set the PyErr and then make the calling
+ * function return behind our back.
+ * It's up to the caller to check the result with PyErr_Occurred()
+ */
+static void
+eom_init_pygobject (void)
+{
+ init_pygobject_check (2, 11, 5); /* FIXME: get from config */
+}
+
+static void
+eom_init_pygtk (void)
+{
+ PyObject *gtk, *mdict, *version, *required_version;
+
+ init_pygtk ();
+
+ /* There isn't init_pygtk_check(), do the version
+ * check ourselves */
+ gtk = PyImport_ImportModule("gtk");
+
+ mdict = PyModule_GetDict(gtk);
+
+ version = PyDict_GetItemString (mdict, "pygtk_version");
+
+ if (!version) {
+ PyErr_SetString (PyExc_ImportError,
+ "PyGObject version too old");
+ return;
+ }
+
+ required_version = Py_BuildValue ("(iii)", 2, 4, 0); /* FIXME */
+
+ if (PyObject_Compare (version, required_version) == -1) {
+ PyErr_SetString (PyExc_ImportError,
+ "PyGObject version too old");
+
+ Py_DECREF (required_version);
+ return;
+ }
+
+ Py_DECREF (required_version);
+}
+
+gboolean
+eom_python_init (void)
+{
+ PyObject *mdict, *path, *tuple;
+ PyObject *sys_path, *eom;
+ PyObject *gettext, *install, *gettext_args;
+ struct sigaction old_sigint;
+ gint res;
+ /* Workaround for python bug. See #569228. */
+ char *argv[] = { "/dev/null/python/is/buggy/eom", NULL };
+
+ static gboolean init_failed = FALSE;
+
+ if (init_failed) {
+ /* We already failed to initialized Python, don't need to
+ * retry again */
+ return FALSE;
+ }
+
+ if (Py_IsInitialized ()) {
+ /* Python has already been successfully initialized */
+ return TRUE;
+ }
+
+ /* We are trying to initialize Python for the first time,
+ set init_failed to FALSE only if the entire initialization process
+ ends with success */
+ init_failed = TRUE;
+
+ /* Hack to make python not overwrite SIGINT: this is needed to avoid
+ * the crash reported on bug #326191 */
+
+ /* CHECK: can't we use Py_InitializeEx instead of Py_Initialize in order
+ to avoid to manage signal handlers ? - Paolo (Dec. 31, 2006) */
+
+ /* Save old handler */
+ res = sigaction (SIGINT, NULL, &old_sigint);
+
+ if (res != 0) {
+ g_warning ("Error initializing Python interpreter: cannot get "
+ "handler to SIGINT signal (%s)", strerror (errno));
+
+ return FALSE;
+ }
+
+ /* Python initialization */
+ Py_Initialize ();
+
+ /* Restore old handler */
+ res = sigaction (SIGINT, &old_sigint, NULL);
+
+ if (res != 0) {
+ g_warning ("Error initializing Python interpreter: cannot restore "
+ "handler to SIGINT signal (%s).", strerror (errno));
+
+ goto python_init_error;
+ }
+
+ PySys_SetArgv (1, argv);
+
+ /* Sanitize sys.path */
+ PyRun_SimpleString("import sys; sys.path = filter(None, sys.path)");
+
+ if (!check_pygtk2 ()) {
+ /* Warning message already printed in check_pygtk2 */
+ goto python_init_error;
+ }
+
+ /* import gobject */
+ eom_init_pygobject ();
+
+ if (PyErr_Occurred ()) {
+ g_warning ("Error initializing Python interpreter: could not import pygobject.");
+
+ goto python_init_error;
+ }
+
+ /* import gtk */
+ eom_init_pygtk ();
+
+ if (PyErr_Occurred ()) {
+ g_warning ("Error initializing Python interpreter: could not import pygtk.");
+
+ goto python_init_error;
+ }
+
+ /* sys.path.insert(0, ...) for system-wide plugins */
+ sys_path = PySys_GetObject ("path");
+ path = PyString_FromString (EOM_PLUGIN_DIR "/");
+ PyList_Insert (sys_path, 0, path);
+ Py_DECREF(path);
+
+ /* import eom */
+ eom = Py_InitModule ("eom", pyeom_functions);
+ mdict = PyModule_GetDict (eom);
+
+ pyeom_register_classes (mdict);
+ pyeom_add_constants (eom, "EOM_");
+
+ /* eom version */
+ tuple = Py_BuildValue("(iii)",
+ EOM_MAJOR_VERSION,
+ EOM_MINOR_VERSION,
+ EOM_MICRO_VERSION);
+ PyDict_SetItemString(mdict, "version", tuple);
+ Py_DECREF(tuple);
+
+ /* Retrieve the Python type for eom.Plugin */
+ PyEomPlugin_Type = (PyTypeObject *) PyDict_GetItemString (mdict, "Plugin");
+
+ if (PyEomPlugin_Type == NULL) {
+ PyErr_Print ();
+
+ goto python_init_error;
+ }
+
+ /* i18n support */
+ gettext = PyImport_ImportModule ("gettext");
+
+ if (gettext == NULL) {
+ g_warning ("Error initializing Python interpreter: could not import gettext.");
+
+ goto python_init_error;
+ }
+
+ mdict = PyModule_GetDict (gettext);
+ install = PyDict_GetItemString (mdict, "install");
+ gettext_args = Py_BuildValue ("ss", GETTEXT_PACKAGE, EOM_LOCALE_DIR);
+ PyObject_CallObject (install, gettext_args);
+ Py_DECREF (gettext_args);
+
+ /* Python has been successfully initialized */
+ init_failed = FALSE;
+
+ return TRUE;
+
+python_init_error:
+
+ g_warning ("Please check the installation of all the Python related packages required "
+ "by eom and try again.");
+
+ PyErr_Clear ();
+
+ eom_python_shutdown ();
+
+ return FALSE;
+}
+
+void
+eom_python_shutdown (void)
+{
+ if (Py_IsInitialized ()) {
+ if (idle_garbage_collect_id != 0) {
+ g_source_remove (idle_garbage_collect_id);
+ idle_garbage_collect_id = 0;
+ }
+
+ while (PyGC_Collect ())
+ ;
+
+ Py_Finalize ();
+ }
+}
+
+static gboolean
+run_gc (gpointer data)
+{
+ while (PyGC_Collect ())
+ ;
+
+ idle_garbage_collect_id = 0;
+
+ return FALSE;
+}
+
+void
+eom_python_garbage_collect (void)
+{
+ if (Py_IsInitialized()) {
+ /*
+ * We both run the GC right now and we schedule
+ * a further collection in the main loop.
+ */
+
+ while (PyGC_Collect ())
+ ;
+
+ if (idle_garbage_collect_id == 0)
+ idle_garbage_collect_id = g_idle_add (run_gc, NULL);
+ }
+}
+
diff --git a/src/eom-python-module.h b/src/eom-python-module.h
new file mode 100644
index 0000000..594884d
--- /dev/null
+++ b/src/eom-python-module.h
@@ -0,0 +1,72 @@
+/* Eye Of Mate - Python Module
+ *
+ * Copyright (C) 2007 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on gedit code (gedit/gedit-python-module.h) by:
+ * - Raphael Slinckx <[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_PYTHON_MODULE_H__
+#define __EOM_PYTHON_MODULE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EOM_TYPE_PYTHON_MODULE (eom_python_module_get_type ())
+#define EOM_PYTHON_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_PYTHON_MODULE, EomPythonModule))
+#define EOM_PYTHON_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_PYTHON_MODULE, EomPythonModuleClass))
+#define EOM_IS_PYTHON_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_PYTHON_MODULE))
+#define EOM_IS_PYTHON_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EOM_TYPE_PYTHON_MODULE))
+#define EOM_PYTHON_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_PYTHON_MODULE, EomPythonModuleClass))
+
+typedef struct _EomPythonModule EomPythonModule;
+typedef struct _EomPythonModuleClass EomPythonModuleClass;
+typedef struct _EomPythonModulePrivate EomPythonModulePrivate;
+
+struct _EomPythonModuleClass {
+ GTypeModuleClass parent_class;
+};
+
+struct _EomPythonModule {
+ GTypeModule parent_instance;
+};
+
+G_GNUC_INTERNAL
+GType eom_python_module_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+EomPythonModule *eom_python_module_new (const gchar* path,
+ const gchar *module);
+
+G_GNUC_INTERNAL
+GObject *eom_python_module_new_object (EomPythonModule *module);
+
+G_GNUC_INTERNAL
+gboolean eom_python_init (void);
+
+G_GNUC_INTERNAL
+void eom_python_shutdown (void);
+
+G_GNUC_INTERNAL
+void eom_python_garbage_collect (void);
+
+G_END_DECLS
+
+#endif /* __EOM_PYTHON_MODULE_H__ */
diff --git a/src/eom-python-plugin.c b/src/eom-python-plugin.c
new file mode 100644
index 0000000..e693b54
--- /dev/null
+++ b/src/eom-python-plugin.c
@@ -0,0 +1,282 @@
+/* Eye Of Mate - Python Plugin
+ *
+ * Copyright (C) 2007 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on gedit code (gedit/gedit-python-module.h) by:
+ * - Raphael Slinckx <[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 <config.h>
+
+#include "eom-python-plugin.h"
+#include "eom-plugin.h"
+#include "eom-debug.h"
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+static PyObject *
+call_python_method (EomPythonPlugin *plugin,
+ EomWindow *window,
+ gchar *method)
+{
+ PyObject *py_ret = NULL;
+
+ g_return_val_if_fail (PyObject_HasAttrString (plugin->instance, method), NULL);
+
+ if (window == NULL) {
+ py_ret = PyObject_CallMethod (plugin->instance,
+ method,
+ NULL);
+ } else {
+ py_ret = PyObject_CallMethod (plugin->instance,
+ method,
+ "(N)",
+ pygobject_new (G_OBJECT (window)));
+ }
+
+ if (!py_ret)
+ PyErr_Print ();
+
+ return py_ret;
+}
+
+static gboolean
+check_py_object_is_gtk_widget (PyObject *py_obj)
+{
+ static PyTypeObject *_PyGtkWidget_Type = NULL;
+
+ if (_PyGtkWidget_Type == NULL) {
+ PyObject *module;
+
+ if ((module = PyImport_ImportModule ("gtk"))) {
+ PyObject *moddict = PyModule_GetDict (module);
+ _PyGtkWidget_Type = (PyTypeObject *) PyDict_GetItemString (moddict, "Widget");
+ }
+
+ if (_PyGtkWidget_Type == NULL) {
+ PyErr_SetString(PyExc_TypeError, "could not find Python gtk widget type");
+ PyErr_Print();
+
+ return FALSE;
+ }
+ }
+
+ return PyObject_TypeCheck (py_obj, _PyGtkWidget_Type) ? TRUE : FALSE;
+}
+
+static void
+impl_update_ui (EomPlugin *plugin,
+ EomWindow *window)
+{
+ PyGILState_STATE state = pyg_gil_state_ensure ();
+
+ EomPythonPlugin *pyplugin = (EomPythonPlugin *) plugin;
+
+ if (PyObject_HasAttrString (pyplugin->instance, "update_ui")) {
+ PyObject *py_ret = call_python_method (pyplugin, window, "update_ui");
+
+ if (py_ret)
+ {
+ Py_XDECREF (py_ret);
+ }
+ } else {
+ EOM_PLUGIN_CLASS (parent_class)->update_ui (plugin, window);
+ }
+
+ pyg_gil_state_release (state);
+}
+
+static void
+impl_deactivate (EomPlugin *plugin,
+ EomWindow *window)
+{
+ PyGILState_STATE state = pyg_gil_state_ensure ();
+
+ EomPythonPlugin *pyplugin = (EomPythonPlugin *) plugin;
+
+ if (PyObject_HasAttrString (pyplugin->instance, "deactivate")) {
+ PyObject *py_ret = call_python_method (pyplugin, window, "deactivate");
+
+ if (py_ret) {
+ Py_XDECREF (py_ret);
+ }
+ } else {
+ EOM_PLUGIN_CLASS (parent_class)->deactivate (plugin, window);
+ }
+
+ pyg_gil_state_release (state);
+}
+
+static void
+impl_activate (EomPlugin *plugin,
+ EomWindow *window)
+{
+ PyGILState_STATE state = pyg_gil_state_ensure ();
+
+ EomPythonPlugin *pyplugin = (EomPythonPlugin *) plugin;
+
+ if (PyObject_HasAttrString (pyplugin->instance, "activate")) {
+ PyObject *py_ret = call_python_method (pyplugin, window, "activate");
+
+ if (py_ret) {
+ Py_XDECREF (py_ret);
+ }
+ } else {
+ EOM_PLUGIN_CLASS (parent_class)->activate (plugin, window);
+ }
+
+ pyg_gil_state_release (state);
+}
+
+static GtkWidget *
+impl_create_configure_dialog (EomPlugin *plugin)
+{
+ PyGILState_STATE state = pyg_gil_state_ensure ();
+ EomPythonPlugin *pyplugin = (EomPythonPlugin *) plugin;
+ GtkWidget *ret = NULL;
+
+ if (PyObject_HasAttrString (pyplugin->instance, "create_configure_dialog")) {
+ PyObject *py_ret = call_python_method (pyplugin, NULL, "create_configure_dialog");
+
+ if (py_ret) {
+ if (check_py_object_is_gtk_widget (py_ret)) {
+ ret = GTK_WIDGET (pygobject_get (py_ret));
+ g_object_ref (ret);
+ } else {
+ PyErr_SetString(PyExc_TypeError, "Return value for create_configure_dialog is not a GtkWidget");
+ PyErr_Print();
+ }
+
+ Py_DECREF (py_ret);
+ }
+ } else {
+ ret = EOM_PLUGIN_CLASS (parent_class)->create_configure_dialog (plugin);
+ }
+
+ pyg_gil_state_release (state);
+
+ return ret;
+}
+
+static gboolean
+impl_is_configurable (EomPlugin *plugin)
+{
+ PyGILState_STATE state = pyg_gil_state_ensure ();
+
+ EomPythonPlugin *pyplugin = (EomPythonPlugin *) plugin;
+
+ PyObject *dict = pyplugin->instance->ob_type->tp_dict;
+
+ gboolean result;
+
+ if (dict == NULL)
+ result = FALSE;
+ else if (!PyDict_Check(dict))
+ result = FALSE;
+ else
+ result = PyDict_GetItemString(dict, "create_configure_dialog") != NULL;
+
+ pyg_gil_state_release (state);
+
+ return result;
+}
+
+static void
+eom_python_plugin_init (EomPythonPlugin *plugin)
+{
+ EomPythonPluginClass *class;
+
+ eom_debug_message (DEBUG_PLUGINS, "Creating Python plugin instance");
+
+ class = (EomPythonPluginClass*) (((GTypeInstance*) plugin)->g_class);
+
+ plugin->instance = PyObject_CallObject (class->type, NULL);
+
+ if (plugin->instance == NULL)
+ PyErr_Print();
+}
+
+static void
+eom_python_plugin_finalize (GObject *plugin)
+{
+ eom_debug_message (DEBUG_PLUGINS, "Finalizing Python plugin instance");
+
+ Py_DECREF (((EomPythonPlugin *) plugin)->instance);
+
+ G_OBJECT_CLASS (parent_class)->finalize (plugin);
+}
+
+static void
+eom_python_plugin_class_init (EomPythonPluginClass *klass,
+ gpointer class_data)
+{
+ EomPluginClass *plugin_class = EOM_PLUGIN_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ klass->type = (PyObject*) class_data;
+
+ G_OBJECT_CLASS (klass)->finalize = eom_python_plugin_finalize;
+
+ plugin_class->activate = impl_activate;
+ plugin_class->deactivate = impl_deactivate;
+ plugin_class->update_ui = impl_update_ui;
+ plugin_class->create_configure_dialog = impl_create_configure_dialog;
+ plugin_class->is_configurable = impl_is_configurable;
+}
+
+GType
+eom_python_plugin_get_type (GTypeModule *module,
+ PyObject *type)
+{
+ GType gtype;
+ gchar *type_name;
+
+ GTypeInfo info = {
+ sizeof (EomPythonPluginClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) eom_python_plugin_class_init,
+ NULL, /* class_finalize */
+ type, /* class_data */
+ sizeof (EomPythonPlugin),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) eom_python_plugin_init,
+ };
+
+ Py_INCREF (type);
+
+ type_name = g_strdup_printf ("%s+EomPythonPlugin",
+ PyString_AsString (PyObject_GetAttrString (type, "__name__")));
+
+ eom_debug_message (DEBUG_PLUGINS, "Registering Python plugin instance: %s", type_name);
+
+ gtype = g_type_module_register_type (module,
+ EOM_TYPE_PLUGIN,
+ type_name,
+ &info, 0);
+
+ g_free (type_name);
+
+ return gtype;
+}
diff --git a/src/eom-python-plugin.h b/src/eom-python-plugin.h
new file mode 100644
index 0000000..eda7959
--- /dev/null
+++ b/src/eom-python-plugin.h
@@ -0,0 +1,55 @@
+/* Eye Of Mate - Python Plugin
+ *
+ * Copyright (C) 2007 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on gedit code (gedit/gedit-python-module.h) by:
+ * - Raphael Slinckx <[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_PYTHON_PLUGIN_H__
+#define __EOM_PYTHON_PLUGIN_H__
+
+#include <Python.h>
+#include <glib-object.h>
+
+#include "eom-plugin.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EomPythonPlugin EomPythonPlugin;
+typedef struct _EomPythonPluginClass EomPythonPluginClass;
+
+struct _EomPythonPlugin {
+ EomPlugin plugin;
+
+ PyObject *instance;
+};
+
+struct _EomPythonPluginClass {
+ EomPluginClass parent_class;
+
+ PyObject *type;
+};
+
+G_GNUC_INTERNAL
+GType eom_python_plugin_get_type (GTypeModule *module, PyObject *type);
+
+G_END_DECLS
+
+#endif
diff --git a/src/eom-save-as-dialog-helper.c b/src/eom-save-as-dialog-helper.c
new file mode 100644
index 0000000..e2ff12e
--- /dev/null
+++ b/src/eom-save-as-dialog-helper.c
@@ -0,0 +1,311 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "eom-save-as-dialog-helper.h"
+#include "eom-pixbuf-util.h"
+#include "eom-file-chooser.h"
+
+typedef struct {
+ GtkWidget *dir_chooser;
+ GtkWidget *token_entry;
+ GtkWidget *replace_spaces_check;
+ GtkWidget *counter_spin;
+ GtkWidget *preview_label;
+ GtkWidget *format_combobox;
+
+ guint idle_id;
+ gint n_images;
+ EomImage *image;
+ gint nth_image;
+} SaveAsData;
+
+static GdkPixbufFormat *
+get_selected_format (GtkComboBox *combobox)
+{
+ GdkPixbufFormat *format;
+ GtkTreeModel *store;
+ GtkTreeIter iter;
+
+ gtk_combo_box_get_active_iter (combobox, &iter);
+ store = gtk_combo_box_get_model (combobox);
+ gtk_tree_model_get (store, &iter, 1, &format, -1);
+
+ return format;
+}
+
+static gboolean
+update_preview (gpointer user_data)
+{
+ SaveAsData *data;
+ char *preview_str = NULL;
+ const char *token_str;
+ gboolean convert_spaces;
+ gulong counter_start;
+ GdkPixbufFormat *format;
+
+ data = g_object_get_data (G_OBJECT (user_data), "data");
+ g_assert (data != NULL);
+
+ if (data->image == NULL) return FALSE;
+
+ /* obtain required dialog data */
+ token_str = gtk_entry_get_text (GTK_ENTRY (data->token_entry));
+ convert_spaces = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (data->replace_spaces_check));
+ counter_start = gtk_spin_button_get_value_as_int
+ (GTK_SPIN_BUTTON (data->counter_spin));
+
+ format = get_selected_format (GTK_COMBO_BOX (data->format_combobox));
+
+ if (token_str != NULL) {
+ /* generate preview filename */
+ preview_str = eom_uri_converter_preview (token_str, data->image, format,
+ (counter_start + data->nth_image),
+ data->n_images,
+ convert_spaces, '_' /* FIXME: make this editable */);
+ }
+
+ gtk_label_set_text (GTK_LABEL (data->preview_label), preview_str);
+
+ g_free (preview_str);
+
+ data->idle_id = 0;
+
+ return FALSE;
+}
+
+static void
+request_preview_update (GtkWidget *dlg)
+{
+ SaveAsData *data;
+
+ data = g_object_get_data (G_OBJECT (dlg), "data");
+ g_assert (data != NULL);
+
+ if (data->idle_id != 0)
+ return;
+
+ data->idle_id = g_idle_add (update_preview, dlg);
+}
+
+static void
+on_format_combobox_changed (GtkComboBox *widget, gpointer data)
+{
+ request_preview_update (GTK_WIDGET (data));
+}
+
+static void
+on_token_entry_changed (GtkWidget *widget, gpointer user_data)
+{
+ SaveAsData *data;
+ gboolean enable_save;
+
+ data = g_object_get_data (G_OBJECT (user_data), "data");
+ g_assert (data != NULL);
+
+ request_preview_update (GTK_WIDGET (user_data));
+
+ enable_save = (strlen (gtk_entry_get_text (GTK_ENTRY (data->token_entry))) > 0);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (user_data), GTK_RESPONSE_OK,
+ enable_save);
+}
+
+static void
+on_replace_spaces_check_clicked (GtkWidget *widget, gpointer data)
+{
+ request_preview_update (GTK_WIDGET (data));
+}
+
+static void
+on_counter_spin_changed (GtkWidget *widget, gpointer data)
+{
+ request_preview_update (GTK_WIDGET (data));
+}
+
+static void
+prepare_format_combobox (SaveAsData *data)
+{
+ GtkComboBox *combobox;
+ GtkCellRenderer *cell;
+ GSList *formats;
+ GtkListStore *store;
+ GSList *it;
+ GtkTreeIter iter;
+
+ combobox = GTK_COMBO_BOX (data->format_combobox);
+
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+ gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (store));
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), cell, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox), cell,
+ "text", 0);
+
+ formats = eom_pixbuf_get_savable_formats ();
+ for (it = formats; it != NULL; it = it->next) {
+ GdkPixbufFormat *f;
+
+ f = (GdkPixbufFormat*) it->data;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, gdk_pixbuf_format_get_name (f), 1, f, -1);
+ }
+ g_slist_free (formats);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, _("as is"), 1, NULL, -1);
+ gtk_combo_box_set_active_iter (combobox, &iter);
+ gtk_widget_show_all (GTK_WIDGET (combobox));
+}
+
+static void
+destroy_data_cb (gpointer data)
+{
+ SaveAsData *sd;
+
+ sd = (SaveAsData*) data;
+
+ if (sd->image != NULL)
+ g_object_unref (sd->image);
+
+ if (sd->idle_id != 0)
+ g_source_remove (sd->idle_id);
+
+ g_slice_free (SaveAsData, sd);
+}
+
+static void
+set_default_values (GtkWidget *dlg, GFile *base_file)
+{
+ SaveAsData *sd;
+
+ sd = (SaveAsData*) g_object_get_data (G_OBJECT (dlg), "data");
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (sd->counter_spin), 0.0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sd->replace_spaces_check),
+ FALSE);
+ if (base_file != NULL) {
+ gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (sd->dir_chooser), base_file, NULL);
+ }
+
+ /*gtk_dialog_set_response_sensitive (GTK_DIALOG (dlg), GTK_RESPONSE_OK, FALSE);*/
+
+ request_preview_update (dlg);
+}
+
+GtkWidget*
+eom_save_as_dialog_new (GtkWindow *main, GList *images, GFile *base_file)
+{
+ char *filepath;
+ GtkBuilder *xml;
+ GtkWidget *dlg;
+ SaveAsData *data;
+ GtkWidget *label;
+
+ filepath = g_build_filename (EOM_DATA_DIR,
+ "eom-multiple-save-as-dialog.ui",
+ NULL);
+
+ xml = gtk_builder_new ();
+ gtk_builder_set_translation_domain (xml, GETTEXT_PACKAGE);
+ g_assert (gtk_builder_add_from_file (xml, filepath, NULL));
+
+ g_free (filepath);
+
+ dlg = GTK_WIDGET (g_object_ref (gtk_builder_get_object (xml, "eom_multiple_save_as_dialog")));
+ gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (main));
+ gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER_ON_PARENT);
+
+ data = g_slice_new0 (SaveAsData);
+ /* init widget references */
+ data->dir_chooser = GTK_WIDGET (gtk_builder_get_object (xml,
+ "dir_chooser"));
+ data->token_entry = GTK_WIDGET (gtk_builder_get_object (xml,
+ "token_entry"));
+ data->replace_spaces_check = GTK_WIDGET (gtk_builder_get_object (xml,
+ "replace_spaces_check"));
+ data->counter_spin = GTK_WIDGET (gtk_builder_get_object (xml,
+ "counter_spin"));
+ data->preview_label = GTK_WIDGET (gtk_builder_get_object (xml,
+ "preview_label"));
+ data->format_combobox = GTK_WIDGET (gtk_builder_get_object (xml,
+ "format_combobox"));
+
+ /* init preview information */
+ data->idle_id = 0;
+ data->n_images = g_list_length (images);
+ data->nth_image = (int) ((float) data->n_images * rand() / (float) (RAND_MAX+1.0));
+ g_assert (data->nth_image >= 0 && data->nth_image < data->n_images);
+ data->image = g_object_ref (G_OBJECT (g_list_nth_data (images, data->nth_image)));
+ g_object_set_data_full (G_OBJECT (dlg), "data", data, destroy_data_cb);
+
+ g_signal_connect (G_OBJECT (data->format_combobox), "changed",
+ (GCallback) on_format_combobox_changed, dlg);
+
+ g_signal_connect (G_OBJECT (data->token_entry), "changed",
+ (GCallback) on_token_entry_changed, dlg);
+
+ g_signal_connect (G_OBJECT (data->replace_spaces_check), "toggled",
+ (GCallback) on_replace_spaces_check_clicked, dlg);
+
+ g_signal_connect (G_OBJECT (data->counter_spin), "changed",
+ (GCallback) on_counter_spin_changed, dlg);
+
+ label = GTK_WIDGET (gtk_builder_get_object (xml, "preview_label_from"));
+ gtk_label_set_text (GTK_LABEL (label), eom_image_get_caption (data->image));
+
+ prepare_format_combobox (data);
+
+ set_default_values (dlg, base_file);
+ g_object_unref (xml);
+ return dlg;
+}
+
+EomURIConverter*
+eom_save_as_dialog_get_converter (GtkWidget *dlg)
+{
+ EomURIConverter *conv;
+
+ SaveAsData *data;
+ const char *format_str;
+ gboolean convert_spaces;
+ gulong counter_start;
+ GdkPixbufFormat *format;
+ GFile *base_file;
+
+ data = g_object_get_data (G_OBJECT (dlg), "data");
+ g_assert (data != NULL);
+
+ /* obtain required dialog data */
+ format_str = gtk_entry_get_text (GTK_ENTRY (data->token_entry));
+
+ convert_spaces = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON (data->replace_spaces_check));
+
+ counter_start = gtk_spin_button_get_value_as_int
+ (GTK_SPIN_BUTTON (data->counter_spin));
+
+ format = get_selected_format (GTK_COMBO_BOX (data->format_combobox));
+
+ base_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (data->dir_chooser));
+
+ /* create converter object */
+ conv = eom_uri_converter_new (base_file, format, format_str);
+
+ /* set other properties */
+ g_object_set (G_OBJECT (conv),
+ "convert-spaces", convert_spaces,
+ "space-character", '_',
+ "counter-start", counter_start,
+ "n-images", data->n_images,
+ NULL);
+
+ g_object_unref (base_file);
+
+ return conv;
+}
diff --git a/src/eom-save-as-dialog-helper.h b/src/eom-save-as-dialog-helper.h
new file mode 100644
index 0000000..dd72fa9
--- /dev/null
+++ b/src/eom-save-as-dialog-helper.h
@@ -0,0 +1,20 @@
+#ifndef _EOM_SAVE_AS_DIALOG_HELPER_H_
+#define _EOM_SAVE_AS_DIALOG_HELPER_H_
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include "eom-uri-converter.h"
+
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+GtkWidget* eom_save_as_dialog_new (GtkWindow *main, GList *images, GFile *base_file);
+
+G_GNUC_INTERNAL
+EomURIConverter* eom_save_as_dialog_get_converter (GtkWidget *dlg);
+
+
+G_END_DECLS
+
+#endif /* _EOM_SAVE_DIALOG_HELPER_H_ */
diff --git a/src/eom-scroll-view.c b/src/eom-scroll-view.c
new file mode 100644
index 0000000..f37561c
--- /dev/null
+++ b/src/eom-scroll-view.c
@@ -0,0 +1,2633 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdkkeysyms.h>
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#endif
+
+#include "eom-marshal.h"
+#include "eom-scroll-view.h"
+#include "eom-debug.h"
+#include "uta.h"
+#include "zoom.h"
+
+/* Maximum size of delayed repaint rectangles */
+#define PAINT_RECT_WIDTH 128
+#define PAINT_RECT_HEIGHT 128
+
+/* Scroll step increment */
+#define SCROLL_STEP_SIZE 32
+
+/* Maximum zoom factor */
+#define MAX_ZOOM_FACTOR 20
+#define MIN_ZOOM_FACTOR 0.02
+
+#define CHECK_MEDIUM 8
+#define CHECK_BLACK 0x00000000
+#define CHECK_DARK 0x00555555
+#define CHECK_GRAY 0x00808080
+#define CHECK_LIGHT 0x00cccccc
+#define CHECK_WHITE 0x00ffffff
+
+/* Default increment for zooming. The current zoom factor is multiplied or
+ * divided by this amount on every zooming step. For consistency, you should
+ * use the same value elsewhere in the program.
+ */
+#define IMAGE_VIEW_ZOOM_MULTIPLIER 1.05
+
+/* States for automatically adjusting the zoom factor */
+typedef enum {
+ ZOOM_MODE_FIT, /* Image is fitted to scroll view even if the latter changes size */
+ ZOOM_MODE_FREE /* The image remains at its current zoom factor even if the scrollview changes size */
+} ZoomMode;
+
+/* Progressive loading state */
+typedef enum {
+ PROGRESSIVE_NONE, /* We are not loading an image or it is already loaded */
+ PROGRESSIVE_LOADING, /* An image is being loaded */
+ PROGRESSIVE_POLISHING /* We have finished loading an image but have not scaled it with interpolation */
+} ProgressiveState;
+
+/* Signal IDs */
+enum {
+ SIGNAL_ZOOM_CHANGED,
+ SIGNAL_LAST
+};
+static gint view_signals [SIGNAL_LAST];
+
+typedef enum {
+ EOM_SCROLL_VIEW_CURSOR_NORMAL,
+ EOM_SCROLL_VIEW_CURSOR_HIDDEN,
+ EOM_SCROLL_VIEW_CURSOR_DRAG
+} EomScrollViewCursor;
+
+/* Drag 'n Drop */
+static GtkTargetEntry target_table[] = {
+ { "text/uri-list", 0, 0},
+};
+
+enum {
+ PROP_0,
+ PROP_USE_BG_COLOR,
+ PROP_BACKGROUND_COLOR
+};
+
+/* Private part of the EomScrollView structure */
+struct _EomScrollViewPrivate {
+ /* some widgets we rely on */
+ GtkWidget *display;
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+ GtkWidget *hbar;
+ GtkWidget *vbar;
+ GtkWidget *menu;
+
+ /* actual image */
+ EomImage *image;
+ guint image_changed_id;
+ guint frame_changed_id;
+ GdkPixbuf *pixbuf;
+
+ /* zoom mode, either ZOOM_MODE_FIT or ZOOM_MODE_FREE */
+ ZoomMode zoom_mode;
+
+ /* whether to allow zoom > 1.0 on zoom fit */
+ gboolean upscale;
+
+ /* the actual zoom factor */
+ double zoom;
+
+ /* the minimum possible (reasonable) zoom factor */
+ double min_zoom;
+
+ /* Current scrolling offsets */
+ int xofs, yofs;
+
+ /* Microtile arrays for dirty region. This represents the dirty region
+ * for interpolated drawing.
+ */
+ EomUta *uta;
+
+ /* handler ID for paint idle callback */
+ guint idle_id;
+
+ /* Interpolation type when zoomed in*/
+ GdkInterpType interp_type_in;
+
+ /* Interpolation type when zoomed out*/
+ GdkInterpType interp_type_out;
+
+ /* Scroll wheel zoom */
+ gboolean scroll_wheel_zoom;
+
+ /* Scroll wheel zoom */
+ gdouble zoom_multiplier;
+
+ /* dragging stuff */
+ int drag_anchor_x, drag_anchor_y;
+ int drag_ofs_x, drag_ofs_y;
+ guint dragging : 1;
+
+ /* status of progressive loading */
+ ProgressiveState progressive_state;
+
+ /* how to indicate transparency in images */
+ EomTransparencyStyle transp_style;
+ guint32 transp_color;
+
+ /* the type of the cursor we are currently showing */
+ EomScrollViewCursor cursor;
+
+ gboolean use_bg_color;
+ GdkColor *background_color;
+ GdkColor *override_bg_color;
+
+ cairo_surface_t *background_surface;
+};
+
+static void scroll_by (EomScrollView *view, int xofs, int yofs);
+static void set_zoom_fit (EomScrollView *view);
+static void request_paint_area (EomScrollView *view, GdkRectangle *area);
+static void set_minimum_zoom_factor (EomScrollView *view);
+
+#define EOM_SCROLL_VIEW_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_SCROLL_VIEW, EomScrollViewPrivate))
+
+G_DEFINE_TYPE (EomScrollView, eom_scroll_view, GTK_TYPE_TABLE)
+
+
+/*===================================
+ widget size changing handler &
+ util functions
+ ---------------------------------*/
+
+/* Disconnects from the EomImage and removes references to it */
+static void
+free_image_resources (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+
+ if (priv->image_changed_id > 0) {
+ g_signal_handler_disconnect (G_OBJECT (priv->image), priv->image_changed_id);
+ priv->image_changed_id = 0;
+ }
+
+ if (priv->frame_changed_id > 0) {
+ g_signal_handler_disconnect (G_OBJECT (priv->image), priv->frame_changed_id);
+ priv->frame_changed_id = 0;
+ }
+
+ if (priv->image != NULL) {
+ eom_image_data_unref (priv->image);
+ priv->image = NULL;
+ }
+
+ if (priv->pixbuf != NULL) {
+ g_object_unref (priv->pixbuf);
+ priv->pixbuf = NULL;
+ }
+}
+
+/* Computes the size in pixels of the scaled image */
+static void
+compute_scaled_size (EomScrollView *view, double zoom, int *width, int *height)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+
+ if (priv->pixbuf) {
+ *width = floor (gdk_pixbuf_get_width (priv->pixbuf) * zoom + 0.5);
+ *height = floor (gdk_pixbuf_get_height (priv->pixbuf) * zoom + 0.5);
+ } else
+ *width = *height = 0;
+}
+
+/* Computes the offsets for the new zoom value so that they keep the image
+ * centered on the view.
+ */
+static void
+compute_center_zoom_offsets (EomScrollView *view,
+ double old_zoom, double new_zoom,
+ int width, int height,
+ double zoom_x_anchor, double zoom_y_anchor,
+ int *xofs, int *yofs)
+{
+ EomScrollViewPrivate *priv;
+ int old_scaled_width, old_scaled_height;
+ int new_scaled_width, new_scaled_height;
+ double view_cx, view_cy;
+
+ priv = view->priv;
+
+ compute_scaled_size (view, old_zoom,
+ &old_scaled_width, &old_scaled_height);
+
+ if (old_scaled_width < width)
+ view_cx = (zoom_x_anchor * old_scaled_width) / old_zoom;
+ else
+ view_cx = (priv->xofs + zoom_x_anchor * width) / old_zoom;
+
+ if (old_scaled_height < height)
+ view_cy = (zoom_y_anchor * old_scaled_height) / old_zoom;
+ else
+ view_cy = (priv->yofs + zoom_y_anchor * height) / old_zoom;
+
+ compute_scaled_size (view, new_zoom,
+ &new_scaled_width, &new_scaled_height);
+
+ if (new_scaled_width < width)
+ *xofs = 0;
+ else {
+ *xofs = floor (view_cx * new_zoom - zoom_x_anchor * width + 0.5);
+ if (*xofs < 0)
+ *xofs = 0;
+ }
+
+ if (new_scaled_height < height)
+ *yofs = 0;
+ else {
+ *yofs = floor (view_cy * new_zoom - zoom_y_anchor * height + 0.5);
+ if (*yofs < 0)
+ *yofs = 0;
+ }
+}
+
+/* Sets the scrollbar values based on the current scrolling offset */
+static void
+update_scrollbar_values (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+ int scaled_width, scaled_height;
+ int xofs, yofs;
+ gdouble page_size,page_increment,step_increment;
+ gdouble lower, upper, value;
+ GtkAllocation allocation;
+
+ priv = view->priv;
+
+ if (!gtk_widget_get_visible (GTK_WIDGET (priv->hbar))
+ && !gtk_widget_get_visible (GTK_WIDGET (priv->vbar)))
+ return;
+
+ compute_scaled_size (view, priv->zoom, &scaled_width, &scaled_height);
+ gtk_widget_get_allocation (GTK_WIDGET (priv->display), &allocation);
+
+ if (gtk_widget_get_visible (GTK_WIDGET (priv->hbar))) {
+ /* Set scroll increments */
+ page_size = MIN (scaled_width, allocation.width);
+ page_increment = allocation.width / 2;
+ step_increment = SCROLL_STEP_SIZE;
+
+ /* Set scroll bounds and new offsets */
+ lower = 0;
+ upper = scaled_width;
+ xofs = CLAMP (priv->xofs, 0, upper - page_size);
+ if (gtk_adjustment_get_value (priv->hadj) != xofs) {
+ value = xofs;
+ priv->xofs = xofs;
+
+ g_signal_handlers_block_matched (
+ priv->hadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+
+ gtk_adjustment_configure (priv->hadj, value, lower,
+ upper, step_increment,
+ page_increment, page_size);
+
+ g_signal_handlers_unblock_matched (
+ priv->hadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+ }
+ }
+
+ if (gtk_widget_get_visible (GTK_WIDGET (priv->vbar))) {
+ page_size = MIN (scaled_height, allocation.height);
+ page_increment = allocation.height / 2;
+ step_increment = SCROLL_STEP_SIZE;
+
+ lower = 0;
+ upper = scaled_height;
+ yofs = CLAMP (priv->yofs, 0, upper - page_size);
+
+ if (gtk_adjustment_get_value (priv->vadj) != yofs) {
+ value = yofs;
+ priv->yofs = yofs;
+
+ g_signal_handlers_block_matched (
+ priv->vadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+
+ gtk_adjustment_configure (priv->vadj, value, lower,
+ upper, step_increment,
+ page_increment, page_size);
+
+ g_signal_handlers_unblock_matched (
+ priv->vadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+ }
+ }
+}
+
+static void
+eom_scroll_view_set_cursor (EomScrollView *view, EomScrollViewCursor new_cursor)
+{
+ GdkCursor *cursor = NULL;
+ GdkDisplay *display;
+ GtkWidget *widget;
+
+ if (view->priv->cursor == new_cursor) {
+ return;
+ }
+
+ widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ display = gtk_widget_get_display (widget);
+ view->priv->cursor = new_cursor;
+
+ switch (new_cursor) {
+ case EOM_SCROLL_VIEW_CURSOR_NORMAL:
+ gdk_window_set_cursor (gtk_widget_get_window (widget), NULL);
+ break;
+ case EOM_SCROLL_VIEW_CURSOR_HIDDEN:
+ cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
+ break;
+ case EOM_SCROLL_VIEW_CURSOR_DRAG:
+ cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
+ break;
+ }
+
+ if (cursor) {
+ gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+ gdk_cursor_unref (cursor);
+ gdk_flush();
+ }
+}
+
+/* Changes visibility of the scrollbars based on the zoom factor and the
+ * specified allocation, or the current allocation if NULL is specified.
+ */
+static void
+check_scrollbar_visibility (EomScrollView *view, GtkAllocation *alloc)
+{
+ EomScrollViewPrivate *priv;
+ int bar_height;
+ int bar_width;
+ int img_width;
+ int img_height;
+ GtkRequisition req;
+ int width, height;
+ gboolean hbar_visible, vbar_visible;
+
+ priv = view->priv;
+
+ if (alloc) {
+ width = alloc->width;
+ height = alloc->height;
+ } else {
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
+ width = allocation.width;
+ height = allocation.height;
+ }
+
+ compute_scaled_size (view, priv->zoom, &img_width, &img_height);
+
+ /* this should work fairly well in this special case for scrollbars */
+ gtk_widget_size_request (priv->hbar, &req);
+ bar_height = req.height;
+ gtk_widget_size_request (priv->vbar, &req);
+ bar_width = req.width;
+
+ eom_debug_message (DEBUG_WINDOW, "Widget Size allocate: %i, %i Bar: %i, %i\n",
+ width, height, bar_width, bar_height);
+
+ hbar_visible = vbar_visible = FALSE;
+ if (priv->zoom_mode == ZOOM_MODE_FIT)
+ hbar_visible = vbar_visible = FALSE;
+ else if (img_width <= width && img_height <= height)
+ hbar_visible = vbar_visible = FALSE;
+ else if (img_width > width && img_height > height)
+ hbar_visible = vbar_visible = TRUE;
+ else if (img_width > width) {
+ hbar_visible = TRUE;
+ if (img_height <= (height - bar_height))
+ vbar_visible = FALSE;
+ else
+ vbar_visible = TRUE;
+ }
+ else if (img_height > height) {
+ vbar_visible = TRUE;
+ if (img_width <= (width - bar_width))
+ hbar_visible = FALSE;
+ else
+ hbar_visible = TRUE;
+ }
+
+ if (hbar_visible != gtk_widget_get_visible (GTK_WIDGET (priv->hbar)))
+ g_object_set (G_OBJECT (priv->hbar), "visible", hbar_visible, NULL);
+
+ if (vbar_visible != gtk_widget_get_visible (GTK_WIDGET (priv->vbar)))
+ g_object_set (G_OBJECT (priv->vbar), "visible", vbar_visible, NULL);
+}
+
+#define DOUBLE_EQUAL_MAX_DIFF 1e-6
+#define DOUBLE_EQUAL(a,b) (fabs (a - b) < DOUBLE_EQUAL_MAX_DIFF)
+
+/* Returns whether the zoom factor is 1.0 */
+static gboolean
+is_unity_zoom (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+ return DOUBLE_EQUAL (priv->zoom, 1.0);
+}
+
+/* Returns whether the image is zoomed in */
+static gboolean
+is_zoomed_in (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+ return priv->zoom - 1.0 > DOUBLE_EQUAL_MAX_DIFF;
+}
+
+/* Returns whether the image is zoomed out */
+static gboolean
+is_zoomed_out (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+ return DOUBLE_EQUAL_MAX_DIFF + priv->zoom - 1.0 < 0.0;
+}
+
+/* Returns wether the image is movable, that means if it is larger then
+ * the actual visible area.
+ */
+static gboolean
+is_image_movable (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+
+ return (gtk_widget_get_visible (priv->hbar) || gtk_widget_get_visible (priv->vbar));
+}
+
+
+/* Computes the image offsets with respect to the window */
+/*
+static void
+get_image_offsets (EomScrollView *view, int *xofs, int *yofs)
+{
+ EomScrollViewPrivate *priv;
+ int scaled_width, scaled_height;
+ int width, height;
+
+ priv = view->priv;
+
+ compute_scaled_size (view, priv->zoom, &scaled_width, &scaled_height);
+
+ width = GTK_WIDGET (priv->display)->allocation.width;
+ height = GTK_WIDGET (priv->display)->allocation.height;
+
+ // Compute image offsets with respect to the window
+ if (scaled_width <= width)
+ *xofs = (width - scaled_width) / 2;
+ else
+ *xofs = -priv->xofs;
+
+ if (scaled_height <= height)
+ *yofs = (height - scaled_height) / 2;
+ else
+ *yofs = -priv->yofs;
+}
+*/
+
+/*===================================
+ drawing core
+ ---------------------------------*/
+
+
+/* Pulls a rectangle from the specified microtile array. The rectangle is the
+ * first one that would be glommed together by art_rect_list_from_uta(), and its
+ * size is bounded by max_width and max_height. The rectangle is also removed
+ * from the microtile array.
+ */
+static void
+pull_rectangle (EomUta *uta, EomIRect *rect, int max_width, int max_height)
+{
+ uta_find_first_glom_rect (uta, rect, max_width, max_height);
+ uta_remove_rect (uta, rect->x0, rect->y0, rect->x1, rect->y1);
+}
+
+/* Paints a rectangle with the background color if the specified rectangle
+ * intersects the dirty rectangle.
+ */
+static void
+paint_background (EomScrollView *view, EomIRect *r, EomIRect *rect)
+{
+ EomScrollViewPrivate *priv;
+ EomIRect d;
+
+ priv = view->priv;
+
+ eom_irect_intersect (&d, r, rect);
+ if (!eom_irect_empty (&d)) {
+ gdk_window_clear_area (gtk_widget_get_window (priv->display),
+ d.x0, d.y0,
+ d.x1 - d.x0, d.y1 - d.y0);
+ }
+}
+
+static void
+get_transparency_params (EomScrollView *view, int *size, guint32 *color1, guint32 *color2)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+
+ /* Compute transparency parameters */
+ switch (priv->transp_style) {
+ case EOM_TRANSP_BACKGROUND: {
+ GdkColor color = gtk_widget_get_style (GTK_WIDGET (priv->display))->bg[GTK_STATE_NORMAL];
+
+ *color1 = *color2 = (((color.red & 0xff00) << 8)
+ | (color.green & 0xff00)
+ | ((color.blue & 0xff00) >> 8));
+ break; }
+
+ case EOM_TRANSP_CHECKED:
+ *color1 = CHECK_GRAY;
+ *color2 = CHECK_LIGHT;
+ break;
+
+ case EOM_TRANSP_COLOR:
+ *color1 = *color2 = priv->transp_color;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ };
+
+ *size = CHECK_MEDIUM;
+}
+
+#ifdef HAVE_RSVG
+static cairo_surface_t *
+create_background_surface (EomScrollView *view)
+{
+ int check_size;
+ guint32 check_1 = 0;
+ guint32 check_2 = 0;
+ cairo_surface_t *surface;
+ cairo_t *check_cr;
+
+ get_transparency_params (view, &check_size, &check_1, &check_2);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, check_size * 2, check_size * 2);
+ check_cr = cairo_create (surface);
+ cairo_set_source_rgba (check_cr,
+ ((check_1 & 0xff0000) >> 16) / 255.,
+ ((check_1 & 0x00ff00) >> 8) / 255.,
+ (check_1 & 0x0000ff) / 255.,
+ 1.);
+ cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+ cairo_fill (check_cr);
+ cairo_translate (check_cr, check_size, check_size);
+ cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+ cairo_fill (check_cr);
+
+ cairo_set_source_rgba (check_cr,
+ ((check_2 & 0xff0000) >> 16) / 255.,
+ ((check_2 & 0x00ff00) >> 8) / 255.,
+ (check_2 & 0x0000ff) / 255.,
+ 1.);
+ cairo_translate (check_cr, -check_size, 0);
+ cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+ cairo_fill (check_cr);
+ cairo_translate (check_cr, check_size, -check_size);
+ cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+ cairo_fill (check_cr);
+ cairo_destroy (check_cr);
+
+ return surface;
+}
+
+static void
+draw_svg_background (EomScrollView *view, cairo_t *cr, EomIRect *render_rect, EomIRect *image_rect)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+
+ if (priv->background_surface == NULL)
+ priv->background_surface = create_background_surface (view);
+
+ cairo_set_source_surface (cr, priv->background_surface,
+ - (render_rect->x0 - image_rect->x0) % (CHECK_MEDIUM * 2),
+ - (render_rect->y0 - image_rect->y0) % (CHECK_MEDIUM * 2));
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr,
+ 0,
+ 0,
+ render_rect->x1 - render_rect->x0,
+ render_rect->y1 - render_rect->y0);
+ cairo_fill (cr);
+}
+
+static cairo_surface_t *
+draw_svg_on_image_surface (EomScrollView *view, EomIRect *render_rect, EomIRect *image_rect)
+{
+ EomScrollViewPrivate *priv;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ cairo_matrix_t matrix, translate, scale;
+ EomTransform *transform;
+
+ priv = view->priv;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ render_rect->x1 - render_rect->x0,
+ render_rect->y1 - render_rect->y0);
+ cr = cairo_create (surface);
+
+ cairo_save (cr);
+ draw_svg_background (view, cr, render_rect, image_rect);
+ cairo_restore (cr);
+
+ cairo_matrix_init_identity (&matrix);
+ transform = eom_image_get_transform (priv->image);
+ if (transform) {
+ cairo_matrix_t affine;
+ double image_offset_x = 0., image_offset_y = 0.;
+
+ eom_transform_get_affine (transform, &affine);
+ cairo_matrix_multiply (&matrix, &affine, &matrix);
+
+ switch (eom_transform_get_transform_type (transform)) {
+ case EOM_TRANSFORM_ROT_90:
+ case EOM_TRANSFORM_FLIP_HORIZONTAL:
+ image_offset_x = (double) gdk_pixbuf_get_width (priv->pixbuf);
+ break;
+ case EOM_TRANSFORM_ROT_270:
+ case EOM_TRANSFORM_FLIP_VERTICAL:
+ image_offset_y = (double) gdk_pixbuf_get_height (priv->pixbuf);
+ break;
+ case EOM_TRANSFORM_ROT_180:
+ case EOM_TRANSFORM_TRANSPOSE:
+ case EOM_TRANSFORM_TRANSVERSE:
+ image_offset_x = (double) gdk_pixbuf_get_width (priv->pixbuf);
+ image_offset_y = (double) gdk_pixbuf_get_height (priv->pixbuf);
+ break;
+ case EOM_TRANSFORM_NONE:
+ default:
+ break;
+ }
+
+ cairo_matrix_init_translate (&translate, image_offset_x, image_offset_y);
+ cairo_matrix_multiply (&matrix, &matrix, &translate);
+ }
+
+ cairo_matrix_init_scale (&scale, priv->zoom, priv->zoom);
+ cairo_matrix_multiply (&matrix, &matrix, &scale);
+ cairo_matrix_init_translate (&translate, image_rect->x0, image_rect->y0);
+ cairo_matrix_multiply (&matrix, &matrix, &translate);
+ cairo_matrix_init_translate (&translate, -render_rect->x0, -render_rect->y0);
+ cairo_matrix_multiply (&matrix, &matrix, &translate);
+
+ cairo_set_matrix (cr, &matrix);
+
+ rsvg_handle_render_cairo (eom_image_get_svg (priv->image), cr);
+ cairo_destroy (cr);
+
+ return surface;
+}
+
+static void
+draw_svg (EomScrollView *view, EomIRect *render_rect, EomIRect *image_rect)
+{
+ EomScrollViewPrivate *priv;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ GdkWindow *window;
+
+ priv = view->priv;
+
+ window = gtk_widget_get_window (GTK_WIDGET (priv->display));
+ surface = draw_svg_on_image_surface (view, render_rect, image_rect);
+
+ cr = gdk_cairo_create (window);
+ cairo_set_source_surface (cr, surface, render_rect->x0, render_rect->y0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+}
+#endif
+
+/* Paints a rectangle of the dirty region */
+static void
+paint_rectangle (EomScrollView *view, EomIRect *rect, GdkInterpType interp_type)
+{
+ EomScrollViewPrivate *priv;
+ GdkPixbuf *tmp;
+ char *str;
+ GtkAllocation allocation;
+ int scaled_width, scaled_height;
+ int xofs, yofs;
+ EomIRect r, d;
+ int check_size;
+ guint32 check_1 = 0;
+ guint32 check_2 = 0;
+
+ priv = view->priv;
+
+ if (!gtk_widget_is_drawable (priv->display))
+ return;
+
+ compute_scaled_size (view, priv->zoom, &scaled_width, &scaled_height);
+
+ gtk_widget_get_allocation (GTK_WIDGET (priv->display), &allocation);
+
+ if (scaled_width < 1 || scaled_height < 1)
+ {
+ r.x0 = 0;
+ r.y0 = 0;
+ r.x1 = allocation.width;
+ r.y1 = allocation.height;
+ paint_background (view, &r, rect);
+ return;
+ }
+
+ /* Compute image offsets with respect to the window */
+
+ if (scaled_width <= allocation.width)
+ xofs = (allocation.width - scaled_width) / 2;
+ else
+ xofs = -priv->xofs;
+
+ if (scaled_height <= allocation.height)
+ yofs = (allocation.height - scaled_height) / 2;
+ else
+ yofs = -priv->yofs;
+
+ eom_debug_message (DEBUG_WINDOW, "zoom %.2f, xofs: %i, yofs: %i scaled w: %i h: %i\n",
+ priv->zoom, xofs, yofs, scaled_width, scaled_height);
+
+ /* Draw background if necessary, in four steps */
+
+ /* Top */
+ if (yofs > 0) {
+ r.x0 = 0;
+ r.y0 = 0;
+ r.x1 = allocation.width;
+ r.y1 = yofs;
+ paint_background (view, &r, rect);
+ }
+
+ /* Left */
+ if (xofs > 0) {
+ r.x0 = 0;
+ r.y0 = yofs;
+ r.x1 = xofs;
+ r.y1 = yofs + scaled_height;
+ paint_background (view, &r, rect);
+ }
+
+ /* Right */
+ if (xofs >= 0) {
+ r.x0 = xofs + scaled_width;
+ r.y0 = yofs;
+ r.x1 = allocation.width;
+ r.y1 = yofs + scaled_height;
+ if (r.x0 < r.x1)
+ paint_background (view, &r, rect);
+ }
+
+ /* Bottom */
+ if (yofs >= 0) {
+ r.x0 = 0;
+ r.y0 = yofs + scaled_height;
+ r.x1 = allocation.width;
+ r.y1 = allocation.height;
+ if (r.y0 < r.y1)
+ paint_background (view, &r, rect);
+ }
+
+
+ /* Draw the scaled image
+ *
+ * FIXME: this is not using the color correction tables!
+ */
+
+ if (!priv->pixbuf)
+ return;
+
+ r.x0 = xofs;
+ r.y0 = yofs;
+ r.x1 = xofs + scaled_width;
+ r.y1 = yofs + scaled_height;
+
+ eom_irect_intersect (&d, &r, rect);
+ if (eom_irect_empty (&d))
+ return;
+
+ switch (interp_type) {
+ case GDK_INTERP_NEAREST:
+ str = "NEAREST";
+ break;
+ default:
+ str = "ALIASED";
+ }
+
+ eom_debug_message (DEBUG_WINDOW, "%s: x0: %i,\t y0: %i,\t x1: %i,\t y1: %i\n",
+ str, d.x0, d.y0, d.x1, d.y1);
+
+#ifdef HAVE_RSVG
+ if (eom_image_is_svg (view->priv->image) && interp_type != GDK_INTERP_NEAREST) {
+ draw_svg (view, &d, &r);
+ return;
+ }
+#endif
+ /* Short-circuit the fast case to avoid a memcpy() */
+
+ if (is_unity_zoom (view)
+ && gdk_pixbuf_get_colorspace (priv->pixbuf) == GDK_COLORSPACE_RGB
+ && !gdk_pixbuf_get_has_alpha (priv->pixbuf)
+ && gdk_pixbuf_get_bits_per_sample (priv->pixbuf) == 8) {
+ guchar *pixels;
+ int rowstride;
+
+ rowstride = gdk_pixbuf_get_rowstride (priv->pixbuf);
+
+ pixels = (gdk_pixbuf_get_pixels (priv->pixbuf)
+ + (d.y0 - yofs) * rowstride
+ + 3 * (d.x0 - xofs));
+
+ gdk_draw_rgb_image_dithalign (gtk_widget_get_window (GTK_WIDGET (priv->display)),
+ gtk_widget_get_style (GTK_WIDGET (priv->display))->black_gc,
+ d.x0, d.y0,
+ d.x1 - d.x0, d.y1 - d.y0,
+ GDK_RGB_DITHER_MAX,
+ pixels,
+ rowstride,
+ d.x0 - xofs, d.y0 - yofs);
+ return;
+ }
+
+ /* For all other cases, create a temporary pixbuf */
+
+ tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, d.x1 - d.x0, d.y1 - d.y0);
+
+ if (!tmp) {
+ g_message ("paint_rectangle(): Could not allocate temporary pixbuf of "
+ "size (%d, %d); skipping", d.x1 - d.x0, d.y1 - d.y0);
+ return;
+ }
+
+ /* Compute transparency parameters */
+ get_transparency_params (view, &check_size, &check_1, &check_2);
+
+ /* Draw! */
+ gdk_pixbuf_composite_color (priv->pixbuf,
+ tmp,
+ 0, 0,
+ d.x1 - d.x0, d.y1 - d.y0,
+ -(d.x0 - xofs), -(d.y0 - yofs),
+ priv->zoom, priv->zoom,
+ is_unity_zoom (view) ? GDK_INTERP_NEAREST : interp_type,
+ 255,
+ d.x0 - xofs, d.y0 - yofs,
+ check_size,
+ check_1, check_2);
+
+ gdk_draw_rgb_image_dithalign (gtk_widget_get_window (priv->display),
+ gtk_widget_get_style (priv->display)->black_gc,
+ d.x0, d.y0,
+ d.x1 - d.x0, d.y1 - d.y0,
+ GDK_RGB_DITHER_MAX,
+ gdk_pixbuf_get_pixels (tmp),
+ gdk_pixbuf_get_rowstride (tmp),
+ d.x0 - xofs, d.y0 - yofs);
+
+ g_object_unref (tmp);
+}
+
+
+/* Idle handler for the drawing process. We pull a rectangle from the dirty
+ * region microtile array, paint it, and leave the rest to the next idle
+ * iteration.
+ */
+static gboolean
+paint_iteration_idle (gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+ EomIRect rect;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ g_assert (priv->uta != NULL);
+
+ pull_rectangle (priv->uta, &rect, PAINT_RECT_WIDTH, PAINT_RECT_HEIGHT);
+
+ if (eom_irect_empty (&rect)) {
+ eom_uta_free (priv->uta);
+ priv->uta = NULL;
+ } else {
+ if (is_zoomed_in (view))
+ paint_rectangle (view, &rect, priv->interp_type_in);
+ else if (is_zoomed_out (view))
+ paint_rectangle (view, &rect, priv->interp_type_out);
+ else
+ paint_rectangle (view, &rect, GDK_INTERP_NEAREST);
+ }
+
+ if (!priv->uta) {
+ priv->idle_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Paints the requested area in non-interpolated mode. Then, if we are
+ * configured to use interpolation, we queue an idle handler to redraw the area
+ * with interpolation. The area is in window coordinates.
+ */
+static void
+request_paint_area (EomScrollView *view, GdkRectangle *area)
+{
+ EomScrollViewPrivate *priv;
+ EomIRect r;
+ GtkAllocation allocation;
+
+ priv = view->priv;
+
+ eom_debug_message (DEBUG_WINDOW, "x: %i, y: %i, width: %i, height: %i\n",
+ area->x, area->y, area->width, area->height);
+
+ if (!gtk_widget_is_drawable (priv->display))
+ return;
+
+ gtk_widget_get_allocation (GTK_WIDGET (priv->display), &allocation);
+ r.x0 = MAX (0, area->x);
+ r.y0 = MAX (0, area->y);
+ r.x1 = MIN (allocation.width, area->x + area->width);
+ r.y1 = MIN (allocation.height, area->y + area->height);
+
+ eom_debug_message (DEBUG_WINDOW, "r: %i, %i, %i, %i\n", r.x0, r.y0, r.x1, r.y1);
+
+ if (r.x0 >= r.x1 || r.y0 >= r.y1)
+ return;
+
+ /* Do nearest neighbor, 1:1 zoom or active progressive loading synchronously for speed. */
+ if ((is_zoomed_in (view) && priv->interp_type_in == GDK_INTERP_NEAREST) ||
+ (is_zoomed_out (view) && priv->interp_type_out == GDK_INTERP_NEAREST) ||
+ is_unity_zoom (view) ||
+ priv->progressive_state == PROGRESSIVE_LOADING) {
+ paint_rectangle (view, &r, GDK_INTERP_NEAREST);
+ return;
+ }
+
+ if (priv->progressive_state == PROGRESSIVE_POLISHING)
+ /* We have already a complete image with nearest neighbor mode.
+ * It's sufficient to add only a antitaliased idle update
+ */
+ priv->progressive_state = PROGRESSIVE_NONE;
+ else if (!priv->image || !eom_image_is_animation (priv->image))
+ /* do nearest neigbor before anti aliased version,
+ except for animations to avoid a "blinking" effect. */
+ paint_rectangle (view, &r, GDK_INTERP_NEAREST);
+
+ /* All other interpolation types are delayed. */
+ if (priv->uta)
+ g_assert (priv->idle_id != 0);
+ else {
+ g_assert (priv->idle_id == 0);
+ priv->idle_id = g_idle_add (paint_iteration_idle, view);
+ }
+
+ priv->uta = uta_add_rect (priv->uta, r.x0, r.y0, r.x1, r.y1);
+}
+
+
+/* =======================================
+
+ scrolling stuff
+
+ --------------------------------------*/
+
+
+/* Scrolls the view to the specified offsets. */
+static void
+scroll_to (EomScrollView *view, int x, int y, gboolean change_adjustments)
+{
+ EomScrollViewPrivate *priv;
+ GtkAllocation allocation;
+ int xofs, yofs;
+ GdkWindow *window;
+ int src_x, src_y;
+ int dest_x, dest_y;
+ int twidth, theight;
+
+ priv = view->priv;
+
+ /* Check bounds & Compute offsets */
+ if (gtk_widget_get_visible (priv->hbar)) {
+ x = CLAMP (x, 0, gtk_adjustment_get_upper (priv->hadj)
+ - gtk_adjustment_get_page_size (priv->hadj));
+ xofs = x - priv->xofs;
+ } else
+ xofs = 0;
+
+ if (gtk_widget_get_visible (priv->vbar)) {
+ y = CLAMP (y, 0, gtk_adjustment_get_upper (priv->vadj)
+ - gtk_adjustment_get_page_size (priv->vadj));
+ yofs = y - priv->yofs;
+ } else
+ yofs = 0;
+
+ if (xofs == 0 && yofs == 0)
+ return;
+
+ priv->xofs = x;
+ priv->yofs = y;
+
+ if (!gtk_widget_is_drawable (priv->display))
+ goto out;
+
+ gtk_widget_get_allocation (GTK_WIDGET (priv->display), &allocation);
+
+ if (abs (xofs) >= allocation.width || abs (yofs) >= allocation.height) {
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ goto out;
+ }
+
+ window = gtk_widget_get_window (GTK_WIDGET (priv->display));
+
+ /* Ensure that the uta has the full size */
+
+ twidth = (allocation.width + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+ theight = (allocation.height + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+
+ if (priv->uta)
+ g_assert (priv->idle_id != 0);
+ else
+ priv->idle_id = g_idle_add (paint_iteration_idle, view);
+
+ priv->uta = uta_ensure_size (priv->uta, 0, 0, twidth, theight);
+
+ /* Copy the uta area. Our synchronous handling of expose events, below,
+ * will queue the new scrolled-in areas.
+ */
+ src_x = xofs < 0 ? 0 : xofs;
+ src_y = yofs < 0 ? 0 : yofs;
+ dest_x = xofs < 0 ? -xofs : 0;
+ dest_y = yofs < 0 ? -yofs : 0;
+
+ uta_copy_area (priv->uta,
+ src_x, src_y,
+ dest_x, dest_y,
+ allocation.width - abs (xofs),
+ allocation.height - abs (yofs));
+
+ /* Scroll the window area and process exposure synchronously. */
+
+ gdk_window_scroll (window, -xofs, -yofs);
+ gdk_window_process_updates (window, TRUE);
+
+ out:
+ if (!change_adjustments)
+ return;
+
+ g_signal_handlers_block_matched (
+ priv->hadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+ g_signal_handlers_block_matched (
+ priv->vadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+
+ gtk_adjustment_set_value (priv->hadj, x);
+ gtk_adjustment_set_value (priv->vadj, y);
+
+ g_signal_handlers_unblock_matched (
+ priv->hadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+ g_signal_handlers_unblock_matched (
+ priv->vadj, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, view);
+}
+
+/* Scrolls the image view by the specified offsets. Notifies the adjustments
+ * about their new values.
+ */
+static void
+scroll_by (EomScrollView *view, int xofs, int yofs)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv;
+
+ scroll_to (view, priv->xofs + xofs, priv->yofs + yofs, TRUE);
+}
+
+
+/* Callback used when an adjustment is changed */
+static void
+adjustment_changed_cb (GtkAdjustment *adj, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ scroll_to (view, gtk_adjustment_get_value (priv->hadj),
+ gtk_adjustment_get_value (priv->vadj), FALSE);
+}
+
+
+/* Drags the image to the specified position */
+static void
+drag_to (EomScrollView *view, int x, int y)
+{
+ EomScrollViewPrivate *priv;
+ int dx, dy;
+
+ priv = view->priv;
+
+ dx = priv->drag_anchor_x - x;
+ dy = priv->drag_anchor_y - y;
+
+ x = priv->drag_ofs_x + dx;
+ y = priv->drag_ofs_y + dy;
+
+ scroll_to (view, x, y, TRUE);
+}
+
+static void
+set_minimum_zoom_factor (EomScrollView *view)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ view->priv->min_zoom = MAX (1.0 / gdk_pixbuf_get_width (view->priv->pixbuf),
+ MAX(1.0 / gdk_pixbuf_get_height (view->priv->pixbuf),
+ MIN_ZOOM_FACTOR) );
+ return;
+}
+
+/**
+ * set_zoom:
+ * @view: A scroll view.
+ * @zoom: Zoom factor.
+ * @have_anchor: Whether the anchor point specified by (@anchorx, @anchory)
+ * should be used.
+ * @anchorx: Horizontal anchor point in pixels.
+ * @anchory: Vertical anchor point in pixels.
+ *
+ * Sets the zoom factor for an image view. The anchor point can be used to
+ * specify the point that stays fixed when the image is zoomed. If @have_anchor
+ * is %TRUE, then (@anchorx, @anchory) specify the point relative to the image
+ * view widget's allocation that will stay fixed when zooming. If @have_anchor
+ * is %FALSE, then the center point of the image view will be used.
+ **/
+static void
+set_zoom (EomScrollView *view, double zoom,
+ gboolean have_anchor, int anchorx, int anchory)
+{
+ EomScrollViewPrivate *priv;
+ GtkAllocation allocation;
+ int xofs, yofs;
+ double x_rel, y_rel;
+
+ g_assert (zoom > 0.0);
+
+ priv = view->priv;
+
+ if (priv->pixbuf == NULL)
+ return;
+
+ if (zoom > MAX_ZOOM_FACTOR)
+ zoom = MAX_ZOOM_FACTOR;
+ else if (zoom < MIN_ZOOM_FACTOR)
+ zoom = MIN_ZOOM_FACTOR;
+
+ if (DOUBLE_EQUAL (priv->zoom, zoom))
+ return;
+ if (DOUBLE_EQUAL (priv->zoom, priv->min_zoom) && zoom < priv->zoom)
+ return;
+
+ priv->zoom_mode = ZOOM_MODE_FREE;
+
+ gtk_widget_get_allocation (GTK_WIDGET (priv->display), &allocation);
+
+ /* compute new xofs/yofs values */
+ if (have_anchor) {
+ x_rel = (double) anchorx / allocation.width;
+ y_rel = (double) anchory / allocation.height;
+ } else {
+ x_rel = 0.5;
+ y_rel = 0.5;
+ }
+
+ compute_center_zoom_offsets (view, priv->zoom, zoom,
+ allocation.width, allocation.height,
+ x_rel, y_rel,
+ &xofs, &yofs);
+
+ /* set new values */
+ priv->xofs = xofs; /* (img_width * x_rel * zoom) - anchorx; */
+ priv->yofs = yofs; /* (img_height * y_rel * zoom) - anchory; */
+#if 0
+ g_print ("xofs: %i yofs: %i\n", priv->xofs, priv->yofs);
+#endif
+ if (zoom <= priv->min_zoom)
+ priv->zoom = priv->min_zoom;
+ else
+ priv->zoom = zoom;
+
+ /* we make use of the new values here */
+ check_scrollbar_visibility (view, NULL);
+ update_scrollbar_values (view);
+
+ /* repaint the whole image */
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+
+ g_signal_emit (view, view_signals [SIGNAL_ZOOM_CHANGED], 0, priv->zoom);
+}
+
+/* Zooms the image to fit the available allocation */
+static void
+set_zoom_fit (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+ GtkAllocation allocation;
+ double new_zoom;
+
+ priv = view->priv;
+
+ priv->zoom_mode = ZOOM_MODE_FIT;
+
+ if (!gtk_widget_get_mapped (GTK_WIDGET (view)))
+ return;
+
+ if (priv->pixbuf == NULL)
+ return;
+
+ gtk_widget_get_allocation (GTK_WIDGET(priv->display), &allocation);
+
+ new_zoom = zoom_fit_scale (allocation.width, allocation.height,
+ gdk_pixbuf_get_width (priv->pixbuf),
+ gdk_pixbuf_get_height (priv->pixbuf),
+ priv->upscale);
+
+ if (new_zoom > MAX_ZOOM_FACTOR)
+ new_zoom = MAX_ZOOM_FACTOR;
+ else if (new_zoom < MIN_ZOOM_FACTOR)
+ new_zoom = MIN_ZOOM_FACTOR;
+
+ priv->zoom = new_zoom;
+ priv->xofs = 0;
+ priv->yofs = 0;
+
+ g_signal_emit (view, view_signals [SIGNAL_ZOOM_CHANGED], 0, priv->zoom);
+}
+
+/*===================================
+
+ internal signal callbacks
+
+ ---------------------------------*/
+
+/* Key press event handler for the image view */
+static gboolean
+display_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+ GtkAllocation allocation;
+ gboolean do_zoom;
+ double zoom;
+ gboolean do_scroll;
+ int xofs, yofs;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ do_zoom = FALSE;
+ do_scroll = FALSE;
+ xofs = yofs = 0;
+ zoom = 1.0;
+
+ gtk_widget_get_allocation (GTK_WIDGET (priv->display), &allocation);
+
+ /* EomScrollView doesn't handle/have any Alt+Key combos */
+ if (event->state & GDK_MOD1_MASK) {
+ return FALSE;
+ }
+
+ switch (event->keyval) {
+ case GDK_Up:
+ do_scroll = TRUE;
+ xofs = 0;
+ yofs = -SCROLL_STEP_SIZE;
+ break;
+
+ case GDK_Page_Up:
+ do_scroll = TRUE;
+ if (event->state & GDK_CONTROL_MASK) {
+ xofs = -(allocation.width * 3) / 4;
+ yofs = 0;
+ } else {
+ xofs = 0;
+ yofs = -(allocation.height * 3) / 4;
+ }
+ break;
+
+ case GDK_Down:
+ do_scroll = TRUE;
+ xofs = 0;
+ yofs = SCROLL_STEP_SIZE;
+ break;
+
+ case GDK_Page_Down:
+ do_scroll = TRUE;
+ if (event->state & GDK_CONTROL_MASK) {
+ xofs = (allocation.width * 3) / 4;
+ yofs = 0;
+ } else {
+ xofs = 0;
+ yofs = (allocation.height * 3) / 4;
+ }
+ break;
+
+ case GDK_Left:
+ do_scroll = TRUE;
+ xofs = -SCROLL_STEP_SIZE;
+ yofs = 0;
+ break;
+
+ case GDK_Right:
+ do_scroll = TRUE;
+ xofs = SCROLL_STEP_SIZE;
+ yofs = 0;
+ break;
+
+ case GDK_plus:
+ case GDK_equal:
+ case GDK_KP_Add:
+ do_zoom = TRUE;
+ zoom = priv->zoom * priv->zoom_multiplier;
+ break;
+
+ case GDK_minus:
+ case GDK_KP_Subtract:
+ do_zoom = TRUE;
+ zoom = priv->zoom / priv->zoom_multiplier;
+ break;
+
+ case GDK_1:
+ do_zoom = TRUE;
+ zoom = 1.0;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ if (do_zoom) {
+ gint x, y;
+
+ gdk_window_get_pointer (gtk_widget_get_window (widget),
+ &x, &y, NULL);
+ set_zoom (view, zoom, TRUE, x, y);
+ }
+
+ if (do_scroll)
+ scroll_by (view, xofs, yofs);
+
+ return TRUE;
+}
+
+
+/* Button press event handler for the image view */
+static gboolean
+eom_scroll_view_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ if (!gtk_widget_has_focus (priv->display))
+ gtk_widget_grab_focus (GTK_WIDGET (priv->display));
+
+ if (priv->dragging)
+ return FALSE;
+
+ switch (event->button) {
+ case 1:
+ case 2:
+ if (event->button == 1 && !priv->scroll_wheel_zoom &&
+ !(event->state & GDK_CONTROL_MASK))
+ break;
+
+ if (is_image_movable (view)) {
+ eom_scroll_view_set_cursor (view, EOM_SCROLL_VIEW_CURSOR_DRAG);
+
+ priv->dragging = TRUE;
+ priv->drag_anchor_x = event->x;
+ priv->drag_anchor_y = event->y;
+
+ priv->drag_ofs_x = priv->xofs;
+ priv->drag_ofs_y = priv->yofs;
+
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+eom_scroll_view_style_set (GtkWidget *widget, GtkStyle *old_style)
+{
+ GtkStyle *style;
+ EomScrollViewPrivate *priv;
+
+ style = gtk_widget_get_style (widget);
+ priv = EOM_SCROLL_VIEW (widget)->priv;
+
+ gtk_widget_set_style (priv->display, style);
+}
+
+
+/* Button release event handler for the image view */
+static gboolean
+eom_scroll_view_button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ if (!priv->dragging)
+ return FALSE;
+
+ switch (event->button) {
+ case 1:
+ case 2:
+ drag_to (view, event->x, event->y);
+ priv->dragging = FALSE;
+
+ eom_scroll_view_set_cursor (view, EOM_SCROLL_VIEW_CURSOR_NORMAL);
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+/* Scroll event handler for the image view. We zoom with an event without
+ * modifiers rather than scroll; we use the Shift modifier to scroll.
+ * Rationale: images are not primarily vertical, and in EOM you scan scroll by
+ * dragging the image with button 1 anyways.
+ */
+static gboolean
+eom_scroll_view_scroll_event (GtkWidget *widget, GdkEventScroll *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+ double zoom_factor;
+ int xofs, yofs;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ /* Compute zoom factor and scrolling offsets; we'll only use either of them */
+ /* same as in gtkscrolledwindow.c */
+ xofs = gtk_adjustment_get_page_increment (priv->hadj) / 2;
+ yofs = gtk_adjustment_get_page_increment (priv->vadj) / 2;
+
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ zoom_factor = priv->zoom_multiplier;
+ xofs = 0;
+ yofs = -yofs;
+ break;
+
+ case GDK_SCROLL_LEFT:
+ zoom_factor = 1.0 / priv->zoom_multiplier;
+ xofs = -xofs;
+ yofs = 0;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ zoom_factor = 1.0 / priv->zoom_multiplier;
+ xofs = 0;
+ yofs = yofs;
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ zoom_factor = priv->zoom_multiplier;
+ xofs = xofs;
+ yofs = 0;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ if (priv->scroll_wheel_zoom) {
+ if (event->state & GDK_SHIFT_MASK)
+ scroll_by (view, yofs, xofs);
+ else if (event->state & GDK_CONTROL_MASK)
+ scroll_by (view, xofs, yofs);
+ else
+ set_zoom (view, priv->zoom * zoom_factor,
+ TRUE, event->x, event->y);
+ } else {
+ if (event->state & GDK_SHIFT_MASK)
+ scroll_by (view, yofs, xofs);
+ else if (event->state & GDK_CONTROL_MASK)
+ set_zoom (view, priv->zoom * zoom_factor,
+ TRUE, event->x, event->y);
+ else
+ scroll_by (view, xofs, yofs);
+ }
+
+ return TRUE;
+}
+
+/* Motion event handler for the image view */
+static gboolean
+eom_scroll_view_motion_event (GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+ gint x, y;
+ GdkModifierType mods;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ if (!priv->dragging)
+ return FALSE;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (gtk_widget_get_window (GTK_WIDGET (priv->display)), &x, &y, &mods);
+ else {
+ x = event->x;
+ y = event->y;
+ }
+
+ drag_to (view, x, y);
+ return TRUE;
+}
+
+static void
+display_map_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ eom_debug (DEBUG_WINDOW);
+
+ set_zoom_fit (view);
+ check_scrollbar_visibility (view, NULL);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+}
+
+static void
+eom_scroll_view_size_allocate (GtkWidget *widget, GtkAllocation *alloc)
+{
+ EomScrollView *view;
+
+ view = EOM_SCROLL_VIEW (widget);
+ check_scrollbar_visibility (view, alloc);
+
+ GTK_WIDGET_CLASS (eom_scroll_view_parent_class)->size_allocate (widget
+ ,alloc);
+}
+
+static void
+display_size_change (GtkWidget *widget, GdkEventConfigure *event, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ if (priv->zoom_mode == ZOOM_MODE_FIT) {
+ GtkAllocation alloc;
+
+ alloc.width = event->width;
+ alloc.height = event->height;
+
+ set_zoom_fit (view);
+ check_scrollbar_visibility (view, &alloc);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ } else {
+ int scaled_width, scaled_height;
+ int x_offset = 0;
+ int y_offset = 0;
+
+ compute_scaled_size (view, priv->zoom, &scaled_width, &scaled_height);
+
+ if (priv->xofs + event->width > scaled_width)
+ x_offset = scaled_width - event->width - priv->xofs;
+
+ if (priv->yofs + event->height > scaled_height)
+ y_offset = scaled_height - event->height - priv->yofs;
+
+ scroll_by (view, x_offset, y_offset);
+ }
+
+ update_scrollbar_values (view);
+}
+
+
+static gboolean
+eom_scroll_view_focus_in_event (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer data)
+{
+ g_signal_stop_emission_by_name (G_OBJECT (widget), "focus_in_event");
+ return FALSE;
+}
+
+static gboolean
+eom_scroll_view_focus_out_event (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer data)
+{
+ g_signal_stop_emission_by_name (G_OBJECT (widget), "focus_out_event");
+ return FALSE;
+}
+
+/* Expose event handler for the drawing area. First we process the whole dirty
+ * region by drawing a non-interpolated version, which is "instantaneous", and
+ * we do this synchronously. Then, if we are set to use interpolation, we queue
+ * an idle handler to handle interpolated drawing there.
+ */
+static gboolean
+display_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ EomScrollView *view;
+ GdkRectangle *rects;
+ gint n_rects;
+ int i;
+
+ g_return_val_if_fail (GTK_IS_DRAWING_AREA (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+ g_return_val_if_fail (EOM_IS_SCROLL_VIEW (data), FALSE);
+
+ view = EOM_SCROLL_VIEW (data);
+
+ gdk_region_get_rectangles (event->region, &rects, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ request_paint_area (view, rects + i);
+ }
+
+ g_free (rects);
+
+ return TRUE;
+}
+
+
+/*==================================
+
+ image loading callbacks
+
+ -----------------------------------*/
+/*
+static void
+image_loading_update_cb (EomImage *img, int x, int y, int width, int height, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+ GdkRectangle area;
+ int xofs, yofs;
+ int sx0, sy0, sx1, sy1;
+
+ view = (EomScrollView*) data;
+ priv = view->priv;
+
+ eom_debug_message (DEBUG_IMAGE_LOAD, "x: %i, y: %i, width: %i, height: %i\n",
+ x, y, width, height);
+
+ if (priv->pixbuf == NULL) {
+ priv->pixbuf = eom_image_get_pixbuf (img);
+ set_zoom_fit (view);
+ check_scrollbar_visibility (view, NULL);
+ }
+ priv->progressive_state = PROGRESSIVE_LOADING;
+
+ get_image_offsets (view, &xofs, &yofs);
+
+ sx0 = floor (x * priv->zoom + xofs);
+ sy0 = floor (y * priv->zoom + yofs);
+ sx1 = ceil ((x + width) * priv->zoom + xofs);
+ sy1 = ceil ((y + height) * priv->zoom + yofs);
+
+ area.x = sx0;
+ area.y = sy0;
+ area.width = sx1 - sx0;
+ area.height = sy1 - sy0;
+
+ if (GTK_WIDGET_DRAWABLE (priv->display))
+ gdk_window_invalidate_rect (GTK_WIDGET (priv->display)->window, &area, FALSE);
+}
+
+
+static void
+image_loading_finished_cb (EomImage *img, gpointer data)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ view = (EomScrollView*) data;
+ priv = view->priv;
+
+ if (priv->pixbuf == NULL) {
+ priv->pixbuf = eom_image_get_pixbuf (img);
+ priv->progressive_state = PROGRESSIVE_NONE;
+ set_zoom_fit (view);
+ check_scrollbar_visibility (view, NULL);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+
+ }
+ else if (priv->interp_type != GDK_INTERP_NEAREST &&
+ !is_unity_zoom (view))
+ {
+ // paint antialiased image version
+ priv->progressive_state = PROGRESSIVE_POLISHING;
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ }
+}
+
+static void
+image_loading_failed_cb (EomImage *img, char *msg, gpointer data)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = EOM_SCROLL_VIEW (data)->priv;
+
+ g_print ("loading failed: %s.\n", msg);
+
+ if (priv->pixbuf != 0) {
+ g_object_unref (priv->pixbuf);
+ priv->pixbuf = 0;
+ }
+
+ if (GTK_WIDGET_DRAWABLE (priv->display)) {
+ gdk_window_clear (GTK_WIDGET (priv->display)->window);
+ }
+}
+
+static void
+image_loading_cancelled_cb (EomImage *img, gpointer data)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = EOM_SCROLL_VIEW (data)->priv;
+
+ if (priv->pixbuf != NULL) {
+ g_object_unref (priv->pixbuf);
+ priv->pixbuf = NULL;
+ }
+
+ if (GTK_WIDGET_DRAWABLE (priv->display)) {
+ gdk_window_clear (GTK_WIDGET (priv->display)->window);
+ }
+}
+*/
+static void
+image_changed_cb (EomImage *img, gpointer data)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = EOM_SCROLL_VIEW (data)->priv;
+
+ if (priv->pixbuf != NULL) {
+ g_object_unref (priv->pixbuf);
+ priv->pixbuf = NULL;
+ }
+
+ priv->pixbuf = eom_image_get_pixbuf (img);
+
+ set_zoom_fit (EOM_SCROLL_VIEW (data));
+ check_scrollbar_visibility (EOM_SCROLL_VIEW (data), NULL);
+
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+}
+
+/*===================================
+ public API
+ ---------------------------------*/
+
+void
+eom_scroll_view_hide_cursor (EomScrollView *view)
+{
+ eom_scroll_view_set_cursor (view, EOM_SCROLL_VIEW_CURSOR_HIDDEN);
+}
+
+void
+eom_scroll_view_show_cursor (EomScrollView *view)
+{
+ eom_scroll_view_set_cursor (view, EOM_SCROLL_VIEW_CURSOR_NORMAL);
+}
+
+/* general properties */
+void
+eom_scroll_view_set_zoom_upscale (EomScrollView *view, gboolean upscale)
+{
+ EomScrollViewPrivate *priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ if (priv->upscale != upscale) {
+ priv->upscale = upscale;
+
+ if (priv->zoom_mode == ZOOM_MODE_FIT) {
+ set_zoom_fit (view);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ }
+ }
+}
+
+void
+eom_scroll_view_set_antialiasing_in (EomScrollView *view, gboolean state)
+{
+ EomScrollViewPrivate *priv;
+ GdkInterpType new_interp_type;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ new_interp_type = state ? GDK_INTERP_BILINEAR : GDK_INTERP_NEAREST;
+
+ if (priv->interp_type_in != new_interp_type) {
+ priv->interp_type_in = new_interp_type;
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ }
+}
+
+void
+eom_scroll_view_set_antialiasing_out (EomScrollView *view, gboolean state)
+{
+ EomScrollViewPrivate *priv;
+ GdkInterpType new_interp_type;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ new_interp_type = state ? GDK_INTERP_BILINEAR : GDK_INTERP_NEAREST;
+
+ if (priv->interp_type_out != new_interp_type) {
+ priv->interp_type_out = new_interp_type;
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ }
+}
+
+void
+eom_scroll_view_set_transparency (EomScrollView *view, EomTransparencyStyle style, GdkColor *color)
+{
+ EomScrollViewPrivate *priv;
+ guint32 col = 0;
+ guint32 red, green, blue;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ if (color != NULL) {
+ red = (color->red >> 8) << 16;
+ green = (color->green >> 8) << 8;
+ blue = (color->blue >> 8);
+ col = red + green + blue;
+ }
+
+ if (priv->transp_style != style) {
+ priv->transp_style = style;
+ changed = TRUE;
+ }
+
+ if (priv->transp_style == EOM_TRANSP_COLOR && priv->transp_color != col) {
+ priv->transp_color = col;
+ changed = TRUE;
+ }
+
+ if (changed && priv->pixbuf != NULL && gdk_pixbuf_get_has_alpha (priv->pixbuf)) {
+ if (priv->background_surface) {
+ cairo_surface_destroy (priv->background_surface);
+ /* Will be recreated if needed during redraw */
+ priv->background_surface = NULL;
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ }
+}
+
+/* zoom api */
+
+static double preferred_zoom_levels[] = {
+ 1.0 / 100, 1.0 / 50, 1.0 / 20,
+ 1.0 / 10.0, 1.0 / 5.0, 1.0 / 3.0, 1.0 / 2.0, 1.0 / 1.5,
+ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
+ 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0
+};
+static const gint n_zoom_levels = (sizeof (preferred_zoom_levels) / sizeof (double));
+
+void
+eom_scroll_view_zoom_in (EomScrollView *view, gboolean smooth)
+{
+ EomScrollViewPrivate *priv;
+ double zoom;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ if (smooth) {
+ zoom = priv->zoom * priv->zoom_multiplier;
+ }
+ else {
+ int i;
+ int index = -1;
+
+ for (i = 0; i < n_zoom_levels; i++) {
+ if (preferred_zoom_levels [i] - priv->zoom
+ > DOUBLE_EQUAL_MAX_DIFF) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ zoom = priv->zoom;
+ }
+ else {
+ zoom = preferred_zoom_levels [i];
+ }
+ }
+ set_zoom (view, zoom, FALSE, 0, 0);
+
+}
+
+void
+eom_scroll_view_zoom_out (EomScrollView *view, gboolean smooth)
+{
+ EomScrollViewPrivate *priv;
+ double zoom;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ if (smooth) {
+ zoom = priv->zoom / priv->zoom_multiplier;
+ }
+ else {
+ int i;
+ int index = -1;
+
+ for (i = n_zoom_levels - 1; i >= 0; i--) {
+ if (priv->zoom - preferred_zoom_levels [i]
+ > DOUBLE_EQUAL_MAX_DIFF) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ zoom = priv->zoom;
+ }
+ else {
+ zoom = preferred_zoom_levels [i];
+ }
+ }
+ set_zoom (view, zoom, FALSE, 0, 0);
+}
+
+void
+eom_scroll_view_zoom_fit (EomScrollView *view)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ set_zoom_fit (view);
+ check_scrollbar_visibility (view, NULL);
+ gtk_widget_queue_draw (GTK_WIDGET (view->priv->display));
+}
+
+void
+eom_scroll_view_set_zoom (EomScrollView *view, double zoom)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ set_zoom (view, zoom, FALSE, 0, 0);
+}
+
+double
+eom_scroll_view_get_zoom (EomScrollView *view)
+{
+ g_return_val_if_fail (EOM_IS_SCROLL_VIEW (view), 0.0);
+
+ return view->priv->zoom;
+}
+
+gboolean
+eom_scroll_view_get_zoom_is_min (EomScrollView *view)
+{
+ g_return_val_if_fail (EOM_IS_SCROLL_VIEW (view), FALSE);
+
+ set_minimum_zoom_factor (view);
+
+ return DOUBLE_EQUAL (view->priv->zoom, MIN_ZOOM_FACTOR) ||
+ DOUBLE_EQUAL (view->priv->zoom, view->priv->min_zoom);
+}
+
+gboolean
+eom_scroll_view_get_zoom_is_max (EomScrollView *view)
+{
+ g_return_val_if_fail (EOM_IS_SCROLL_VIEW (view), FALSE);
+
+ return DOUBLE_EQUAL (view->priv->zoom, MAX_ZOOM_FACTOR);
+}
+
+static void
+display_next_frame_cb (EomImage *image, gint delay, gpointer data)
+{
+ EomScrollViewPrivate *priv;
+ EomScrollView *view;
+
+ if (!EOM_IS_SCROLL_VIEW (data))
+ return;
+
+ view = EOM_SCROLL_VIEW (data);
+ priv = view->priv;
+
+ if (priv->pixbuf != NULL) {
+ g_object_unref (priv->pixbuf);
+ priv->pixbuf = NULL;
+ }
+
+ priv->pixbuf = eom_image_get_pixbuf (image);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+}
+
+void
+eom_scroll_view_set_image (EomScrollView *view, EomImage *image)
+{
+ EomScrollViewPrivate *priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ if (priv->image == image) {
+ return;
+ }
+
+ if (priv->image != NULL) {
+ free_image_resources (view);
+ if (gtk_widget_is_drawable (priv->display) && image == NULL) {
+ gdk_window_clear (gtk_widget_get_window (priv->display));
+ }
+ }
+ g_assert (priv->image == NULL);
+ g_assert (priv->pixbuf == NULL);
+
+ priv->progressive_state = PROGRESSIVE_NONE;
+ if (image != NULL) {
+ eom_image_data_ref (image);
+
+ if (priv->pixbuf == NULL) {
+ priv->pixbuf = eom_image_get_pixbuf (image);
+ priv->progressive_state = PROGRESSIVE_NONE;
+ set_zoom_fit (view);
+ check_scrollbar_visibility (view, NULL);
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+
+ }
+ else if ((is_zoomed_in (view) && priv->interp_type_in != GDK_INTERP_NEAREST) ||
+ (is_zoomed_out (view) && priv->interp_type_out != GDK_INTERP_NEAREST))
+ {
+ /* paint antialiased image version */
+ priv->progressive_state = PROGRESSIVE_POLISHING;
+ gtk_widget_queue_draw (GTK_WIDGET (priv->display));
+ }
+
+ priv->image_changed_id = g_signal_connect (image, "changed",
+ (GCallback) image_changed_cb, view);
+ if (eom_image_is_animation (image) == TRUE ) {
+ eom_image_start_animation (image);
+ priv->frame_changed_id = g_signal_connect (image, "next-frame",
+ (GCallback) display_next_frame_cb, view);
+ }
+ }
+
+ priv->image = image;
+}
+
+gboolean
+eom_scroll_view_scrollbars_visible (EomScrollView *view)
+{
+ if (!gtk_widget_get_visible (GTK_WIDGET (view->priv->hbar)) &&
+ !gtk_widget_get_visible (GTK_WIDGET (view->priv->vbar)))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*===================================
+ object creation/freeing
+ ---------------------------------*/
+
+static void
+eom_scroll_view_init (EomScrollView *view)
+{
+ EomScrollViewPrivate *priv;
+
+ priv = view->priv = EOM_SCROLL_VIEW_GET_PRIVATE (view);
+
+ priv->zoom = 1.0;
+ priv->min_zoom = MIN_ZOOM_FACTOR;
+ priv->zoom_mode = ZOOM_MODE_FIT;
+ priv->upscale = FALSE;
+ priv->uta = NULL;
+ priv->interp_type_in = GDK_INTERP_BILINEAR;
+ priv->interp_type_out = GDK_INTERP_BILINEAR;
+ priv->scroll_wheel_zoom = FALSE;
+ priv->zoom_multiplier = IMAGE_VIEW_ZOOM_MULTIPLIER;
+ priv->image = NULL;
+ priv->pixbuf = NULL;
+ priv->progressive_state = PROGRESSIVE_NONE;
+ priv->transp_style = EOM_TRANSP_BACKGROUND;
+ priv->transp_color = 0;
+ priv->cursor = EOM_SCROLL_VIEW_CURSOR_NORMAL;
+ priv->menu = NULL;
+ priv->background_color = NULL;
+ priv->override_bg_color = NULL;
+ priv->background_surface = NULL;
+}
+
+static void
+eom_scroll_view_dispose (GObject *object)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (object));
+
+ view = EOM_SCROLL_VIEW (object);
+ priv = view->priv;
+
+ if (priv->uta != NULL) {
+ eom_uta_free (priv->uta);
+ priv->uta = NULL;
+ }
+
+ if (priv->idle_id != 0) {
+ g_source_remove (priv->idle_id);
+ priv->idle_id = 0;
+ }
+
+ if (priv->background_color != NULL) {
+ gdk_color_free (priv->background_color);
+ priv->background_color = NULL;
+ }
+
+ if (priv->override_bg_color != NULL) {
+ gdk_color_free (priv->override_bg_color);
+ priv->override_bg_color = NULL;
+ }
+
+ if (priv->background_surface != NULL) {
+ cairo_surface_destroy (priv->background_surface);
+ priv->background_surface = NULL;
+ }
+
+ free_image_resources (view);
+
+ G_OBJECT_CLASS (eom_scroll_view_parent_class)->dispose (object);
+}
+
+static void
+eom_scroll_view_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (object));
+
+ view = EOM_SCROLL_VIEW (object);
+ priv = view->priv;
+
+ switch (property_id) {
+ case PROP_USE_BG_COLOR:
+ g_value_set_boolean (value, priv->use_bg_color);
+ break;
+ case PROP_BACKGROUND_COLOR:
+ //FIXME: This doesn't really handle the NULL color.
+ g_value_set_boxed (value, priv->background_color);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+eom_scroll_view_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (object));
+
+ view = EOM_SCROLL_VIEW (object);
+ priv = view->priv;
+
+ switch (property_id) {
+ case PROP_USE_BG_COLOR:
+ eom_scroll_view_set_use_bg_color (view, g_value_get_boolean (value));
+ break;
+ case PROP_BACKGROUND_COLOR:
+ {
+ const GdkColor *color = g_value_get_boxed (value);
+ eom_scroll_view_set_background_color (view, color);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+
+static void
+eom_scroll_view_class_init (EomScrollViewClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = (GObjectClass*) klass;
+ widget_class = (GtkWidgetClass*) klass;
+
+ gobject_class->dispose = eom_scroll_view_dispose;
+ gobject_class->set_property = eom_scroll_view_set_property;
+ gobject_class->get_property = eom_scroll_view_get_property;
+
+ /**
+ * EomScrollView:background-color:
+ *
+ * This is the default background color used for painting the background
+ * of the image view. If set to %NULL the color is determined by the
+ * active GTK theme.
+ */
+ g_object_class_install_property (
+ gobject_class, PROP_BACKGROUND_COLOR,
+ g_param_spec_boxed ("background-color", NULL, NULL,
+ GDK_TYPE_COLOR,
+ G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
+
+ g_object_class_install_property (
+ gobject_class, PROP_USE_BG_COLOR,
+ g_param_spec_boolean ("use-background-color", NULL, NULL, FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_NAME));
+
+ view_signals [SIGNAL_ZOOM_CHANGED] =
+ g_signal_new ("zoom_changed",
+ EOM_TYPE_SCROLL_VIEW,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EomScrollViewClass, zoom_changed),
+ NULL, NULL,
+ eom_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+
+ widget_class->size_allocate = eom_scroll_view_size_allocate;
+ widget_class->style_set = eom_scroll_view_style_set;
+
+ g_type_class_add_private (klass, sizeof (EomScrollViewPrivate));
+}
+
+static void
+view_on_drag_begin_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer user_data)
+{
+ EomScrollView *view;
+ EomImage *image;
+ GdkPixbuf *thumbnail;
+ gint width, height;
+
+ view = EOM_SCROLL_VIEW (user_data);
+ image = view->priv->image;
+
+ thumbnail = eom_image_get_thumbnail (image);
+
+ if (thumbnail) {
+ width = gdk_pixbuf_get_width (thumbnail);
+ height = gdk_pixbuf_get_height (thumbnail);
+ gtk_drag_set_icon_pixbuf (context, thumbnail, width/2, height/2);
+ g_object_unref (thumbnail);
+ }
+}
+
+static void
+view_on_drag_data_get_cb (GtkWidget *widget,
+ GdkDragContext *drag_context,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ EomScrollView *view;
+ EomImage *image;
+ gchar *uris[2];
+ GFile *file;
+
+ view = EOM_SCROLL_VIEW (user_data);
+
+ image = view->priv->image;
+
+ file = eom_image_get_file (image);
+ uris[0] = g_file_get_uri (file);
+ uris[1] = NULL;
+
+ gtk_selection_data_set_uris (data, uris);
+
+ g_free (uris[0]);
+ g_object_unref (file);
+}
+
+GtkWidget*
+eom_scroll_view_new (void)
+{
+ GtkWidget *widget;
+ GtkTable *table;
+ EomScrollView *view;
+ EomScrollViewPrivate *priv;
+
+ widget = g_object_new (EOM_TYPE_SCROLL_VIEW,
+ "can-focus", TRUE,
+ "n_rows", 2,
+ "n_columns", 2,
+ "homogeneous", FALSE,
+ NULL);
+
+ table = GTK_TABLE (widget);
+ view = EOM_SCROLL_VIEW (widget);
+ priv = view->priv;
+
+ priv->hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0, 100, 0, 10, 10, 100));
+ g_signal_connect (priv->hadj, "value_changed",
+ G_CALLBACK (adjustment_changed_cb),
+ view);
+ priv->hbar = gtk_hscrollbar_new (priv->hadj);
+ priv->vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0, 100, 0, 10, 10, 100));
+ g_signal_connect (priv->vadj, "value_changed",
+ G_CALLBACK (adjustment_changed_cb),
+ view);
+ priv->vbar = gtk_vscrollbar_new (priv->vadj);
+ priv->display = g_object_new (GTK_TYPE_DRAWING_AREA,
+ "can-focus", TRUE,
+ NULL);
+ /* We don't want to be double-buffered as we are SuperSmart(tm) */
+ gtk_widget_set_double_buffered (GTK_WIDGET (priv->display), FALSE);
+
+ gtk_widget_add_events (GTK_WIDGET (priv->display),
+ GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ | GDK_SCROLL_MASK
+ | GDK_KEY_PRESS_MASK);
+ g_signal_connect (G_OBJECT (priv->display), "configure_event", G_CALLBACK (display_size_change), view);
+ g_signal_connect (G_OBJECT (priv->display), "expose_event", G_CALLBACK (display_expose_event), view);
+ g_signal_connect (G_OBJECT (priv->display), "map_event", G_CALLBACK (display_map_event), view);
+ g_signal_connect (G_OBJECT (priv->display), "button_press_event", G_CALLBACK (eom_scroll_view_button_press_event), view);
+ g_signal_connect (G_OBJECT (priv->display), "motion_notify_event", G_CALLBACK (eom_scroll_view_motion_event), view);
+ g_signal_connect (G_OBJECT (priv->display), "button_release_event", G_CALLBACK (eom_scroll_view_button_release_event), view);
+ g_signal_connect (G_OBJECT (priv->display), "scroll_event", G_CALLBACK (eom_scroll_view_scroll_event), view);
+ g_signal_connect (G_OBJECT (priv->display), "focus_in_event", G_CALLBACK (eom_scroll_view_focus_in_event), NULL);
+ g_signal_connect (G_OBJECT (priv->display), "focus_out_event", G_CALLBACK (eom_scroll_view_focus_out_event), NULL);
+
+ g_signal_connect (G_OBJECT (widget), "key_press_event", G_CALLBACK (display_key_press_event), view);
+
+ gtk_drag_source_set (priv->display, GDK_BUTTON1_MASK,
+ target_table, G_N_ELEMENTS (target_table),
+ GDK_ACTION_COPY);
+ g_signal_connect (G_OBJECT (priv->display), "drag-data-get",
+ G_CALLBACK (view_on_drag_data_get_cb), widget);
+ g_signal_connect (G_OBJECT (priv->display), "drag-begin",
+ G_CALLBACK (view_on_drag_begin_cb), widget);
+
+ gtk_table_attach (table, priv->display,
+ 0, 1, 0, 1,
+ GTK_EXPAND | GTK_FILL,
+ GTK_EXPAND | GTK_FILL,
+ 0,0);
+ gtk_table_attach (table, priv->hbar,
+ 0, 1, 1, 2,
+ GTK_FILL,
+ GTK_FILL,
+ 0, 0);
+ gtk_table_attach (table, priv->vbar,
+ 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL,
+ 0, 0);
+
+ gtk_widget_show_all (widget);
+
+ return widget;
+}
+
+static void
+eom_scroll_view_popup_menu (EomScrollView *view, GdkEventButton *event)
+{
+ GtkWidget *popup;
+ int button, event_time;
+
+ popup = view->priv->menu;
+
+ if (event) {
+ button = event->button;
+ event_time = event->time;
+ } else {
+ button = 0;
+ event_time = gtk_get_current_event_time ();
+ }
+
+ gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL,
+ button, event_time);
+}
+
+static gboolean
+view_on_button_press_event_cb (GtkWidget *view, GdkEventButton *event,
+ gpointer user_data)
+{
+ /* Ignore double-clicks and triple-clicks */
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ {
+ eom_scroll_view_popup_menu (EOM_SCROLL_VIEW (view), event);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+eom_scroll_view_set_popup (EomScrollView *view,
+ GtkMenu *menu)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+ g_return_if_fail (view->priv->menu == NULL);
+
+ view->priv->menu = g_object_ref (menu);
+
+ gtk_menu_attach_to_widget (GTK_MENU (view->priv->menu),
+ GTK_WIDGET (view),
+ NULL);
+
+ g_signal_connect (G_OBJECT (view), "button_press_event",
+ G_CALLBACK (view_on_button_press_event_cb), NULL);
+}
+
+static gboolean
+_eom_gdk_color_equal0 (const GdkColor *a, const GdkColor *b)
+{
+ if (a == NULL || b == NULL)
+ return (a == b);
+
+ return gdk_color_equal (a, b);
+}
+
+static gboolean
+_eom_replace_gdk_color (GdkColor **dest, const GdkColor *new)
+{
+ GdkColor *old = *dest;
+
+ if (_eom_gdk_color_equal0 (old, new))
+ return FALSE;
+
+ if (old != NULL)
+ gdk_color_free (old);
+
+ *dest = (new) ? gdk_color_copy (new) : NULL;
+
+ return TRUE;
+}
+
+static void
+_eom_scroll_view_update_bg_color (EomScrollView *view)
+{
+ const GdkColor *selected;
+ EomScrollViewPrivate *priv = view->priv;
+
+ if (priv->override_bg_color)
+ selected = priv->override_bg_color;
+ else if (priv->use_bg_color)
+ selected = priv->background_color;
+ else
+ selected = NULL;
+
+ if (priv->transp_style == EOM_TRANSP_BACKGROUND
+ && priv->background_surface != NULL) {
+ /* Delete the SVG background to have it recreated with
+ * the correct color during the next SVG redraw */
+ cairo_surface_destroy (priv->background_surface);
+ priv->background_surface = NULL;
+ }
+
+ gtk_widget_modify_bg (GTK_WIDGET (view),
+ GTK_STATE_NORMAL,
+ selected);
+}
+
+void
+eom_scroll_view_set_background_color (EomScrollView *view,
+ const GdkColor *color)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ if (_eom_replace_gdk_color (&view->priv->background_color, color))
+ _eom_scroll_view_update_bg_color (view);
+}
+
+void
+eom_scroll_view_override_bg_color (EomScrollView *view,
+ const GdkColor *color)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ if (_eom_replace_gdk_color (&view->priv->override_bg_color, color))
+ _eom_scroll_view_update_bg_color (view);
+}
+
+void
+eom_scroll_view_set_use_bg_color (EomScrollView *view, gboolean use)
+{
+ EomScrollViewPrivate *priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ priv = view->priv;
+
+ if (use != priv->use_bg_color) {
+ priv->use_bg_color = use;
+
+ _eom_scroll_view_update_bg_color (view);
+
+ g_object_notify (G_OBJECT (view), "use-background-color");
+ }
+}
+
+void
+eom_scroll_view_set_scroll_wheel_zoom (EomScrollView *view,
+ gboolean scroll_wheel_zoom)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ view->priv->scroll_wheel_zoom = scroll_wheel_zoom;
+}
+
+void
+eom_scroll_view_set_zoom_multiplier (EomScrollView *view,
+ gdouble zoom_multiplier)
+{
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (view));
+
+ view->priv->zoom_multiplier = 1.0 + zoom_multiplier;
+}
diff --git a/src/eom-scroll-view.h b/src/eom-scroll-view.h
new file mode 100644
index 0000000..56f8a1b
--- /dev/null
+++ b/src/eom-scroll-view.h
@@ -0,0 +1,73 @@
+#ifndef _EOM_SCROLL_VIEW_H_
+#define _EOM_SCROLL_VIEW_H_
+
+#include <gtk/gtk.h>
+#include "eom-image.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EomScrollView EomScrollView;
+typedef struct _EomScrollViewClass EomScrollViewClass;
+typedef struct _EomScrollViewPrivate EomScrollViewPrivate;
+
+#define EOM_TYPE_SCROLL_VIEW (eom_scroll_view_get_type ())
+#define EOM_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_SCROLL_VIEW, EomScrollView))
+#define EOM_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_SCROLL_VIEW, EomScrollViewClass))
+#define EOM_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_SCROLL_VIEW))
+#define EOM_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_SCROLL_VIEW))
+
+
+struct _EomScrollView {
+ GtkTable widget;
+
+ EomScrollViewPrivate *priv;
+};
+
+struct _EomScrollViewClass {
+ GtkTableClass parent_class;
+
+ void (* zoom_changed) (EomScrollView *view, double zoom);
+};
+
+typedef enum {
+ EOM_TRANSP_BACKGROUND,
+ EOM_TRANSP_CHECKED,
+ EOM_TRANSP_COLOR
+} EomTransparencyStyle;
+
+GType eom_scroll_view_get_type (void) G_GNUC_CONST;
+GtkWidget* eom_scroll_view_new (void);
+
+/* loading stuff */
+void eom_scroll_view_set_image (EomScrollView *view, EomImage *image);
+
+/* general properties */
+void eom_scroll_view_set_scroll_wheel_zoom (EomScrollView *view, gboolean scroll_wheel_zoom);
+void eom_scroll_view_set_zoom_upscale (EomScrollView *view, gboolean upscale);
+void eom_scroll_view_set_zoom_multiplier (EomScrollView *view, gdouble multiplier);
+void eom_scroll_view_set_antialiasing_in (EomScrollView *view, gboolean state);
+void eom_scroll_view_set_antialiasing_out (EomScrollView *view, gboolean state);
+void eom_scroll_view_set_transparency (EomScrollView *view, EomTransparencyStyle style, GdkColor *color);
+gboolean eom_scroll_view_scrollbars_visible (EomScrollView *view);
+void eom_scroll_view_set_popup (EomScrollView *view, GtkMenu *menu);
+void eom_scroll_view_set_background_color (EomScrollView *view,
+ const GdkColor *color);
+void eom_scroll_view_override_bg_color (EomScrollView *view,
+ const GdkColor *color);
+void eom_scroll_view_set_use_bg_color (EomScrollView *view, gboolean use);
+/* zoom api */
+void eom_scroll_view_zoom_in (EomScrollView *view, gboolean smooth);
+void eom_scroll_view_zoom_out (EomScrollView *view, gboolean smooth);
+void eom_scroll_view_zoom_fit (EomScrollView *view);
+void eom_scroll_view_set_zoom (EomScrollView *view, double zoom);
+double eom_scroll_view_get_zoom (EomScrollView *view);
+gboolean eom_scroll_view_get_zoom_is_min (EomScrollView *view);
+gboolean eom_scroll_view_get_zoom_is_max (EomScrollView *view);
+void eom_scroll_view_show_cursor (EomScrollView *view);
+void eom_scroll_view_hide_cursor (EomScrollView *view);
+
+G_END_DECLS
+
+#endif /* _EOM_SCROLL_VIEW_H_ */
+
+
diff --git a/src/eom-session.c b/src/eom-session.c
new file mode 100644
index 0000000..bc926e5
--- /dev/null
+++ b/src/eom-session.c
@@ -0,0 +1,52 @@
+/* Eye Of Mate - Session Handler
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on gedit code (gedit/gedit-session.h) by:
+ * - Gedit Team
+ * - Federico Mena-Quintero <[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-session.h"
+#include "eom-window.h"
+#include "eom-application.h"
+
+void
+eom_session_init (EomApplication *application)
+{
+ g_return_if_fail (EOM_IS_APPLICATION (application));
+
+ /* FIXME: Session management is currently a no-op in eom. */
+}
+
+gboolean
+eom_session_is_restored (void)
+{
+ return FALSE;
+}
+
+gboolean
+eom_session_load (void)
+{
+ return TRUE;
+}
diff --git a/src/eom-session.h b/src/eom-session.h
new file mode 100644
index 0000000..5cf8892
--- /dev/null
+++ b/src/eom-session.h
@@ -0,0 +1,46 @@
+/* Eye Of Mate - Session Handler
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on gedit code (gedit/gedit-session.h) by:
+ * - Gedit Team
+ * - Federico Mena-Quintero <[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_SESSION_H__
+#define __EOM_SESSION_H__
+
+#include "eom-application.h"
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+void eom_session_init (EomApplication *application);
+
+G_GNUC_INTERNAL
+gboolean eom_session_is_restored (void);
+
+G_GNUC_INTERNAL
+gboolean eom_session_load (void);
+
+G_END_DECLS
+
+#endif /* __EOM_SESSION_H__ */
diff --git a/src/eom-sidebar.c b/src/eom-sidebar.c
new file mode 100644
index 0000000..c536189
--- /dev/null
+++ b/src/eom-sidebar.c
@@ -0,0 +1,594 @@
+/* Eye of Mate - Side bar
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2007 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on evince code (shell/ev-sidebar.c) by:
+ * - Jonathan Blandford <[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 <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "eom-sidebar.h"
+
+enum {
+ PROP_0,
+ PROP_CURRENT_PAGE
+};
+
+enum {
+ PAGE_COLUMN_TITLE,
+ PAGE_COLUMN_MENU_ITEM,
+ PAGE_COLUMN_MAIN_WIDGET,
+ PAGE_COLUMN_NOTEBOOK_INDEX,
+ PAGE_COLUMN_NUM_COLS
+};
+
+enum {
+ SIGNAL_PAGE_ADDED,
+ SIGNAL_PAGE_REMOVED,
+ SIGNAL_LAST
+};
+
+static gint signals[SIGNAL_LAST];
+
+struct _EomSidebarPrivate {
+ GtkWidget *notebook;
+ GtkWidget *select_button;
+ GtkWidget *menu;
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ GtkTreeModel *page_model;
+};
+
+G_DEFINE_TYPE (EomSidebar, eom_sidebar, GTK_TYPE_VBOX)
+
+#define EOM_SIDEBAR_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_SIDEBAR, EomSidebarPrivate))
+
+static void
+eom_sidebar_destroy (GtkObject *object)
+{
+ EomSidebar *eom_sidebar = EOM_SIDEBAR (object);
+
+ if (eom_sidebar->priv->menu) {
+ gtk_menu_detach (GTK_MENU (eom_sidebar->priv->menu));
+ eom_sidebar->priv->menu = NULL;
+ }
+
+ if (eom_sidebar->priv->page_model) {
+ g_object_unref (eom_sidebar->priv->page_model);
+ eom_sidebar->priv->page_model = NULL;
+ }
+
+ (* GTK_OBJECT_CLASS (eom_sidebar_parent_class)->destroy) (object);
+}
+
+static void
+eom_sidebar_select_page (EomSidebar *eom_sidebar, GtkTreeIter *iter)
+{
+ gchar *title;
+ gint index;
+
+ gtk_tree_model_get (eom_sidebar->priv->page_model, iter,
+ PAGE_COLUMN_TITLE, &title,
+ PAGE_COLUMN_NOTEBOOK_INDEX, &index,
+ -1);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (eom_sidebar->priv->notebook), index);
+ gtk_label_set_text (GTK_LABEL (eom_sidebar->priv->label), title);
+
+ g_free (title);
+}
+
+void
+eom_sidebar_set_page (EomSidebar *eom_sidebar,
+ GtkWidget *main_widget)
+{
+ GtkTreeIter iter;
+ gboolean valid;
+
+ valid = gtk_tree_model_get_iter_first (eom_sidebar->priv->page_model, &iter);
+
+ while (valid) {
+ GtkWidget *widget;
+
+ gtk_tree_model_get (eom_sidebar->priv->page_model, &iter,
+ PAGE_COLUMN_MAIN_WIDGET, &widget,
+ -1);
+
+ if (widget == main_widget) {
+ eom_sidebar_select_page (eom_sidebar, &iter);
+ valid = FALSE;
+ } else {
+ valid = gtk_tree_model_iter_next (eom_sidebar->priv->page_model, &iter);
+ }
+
+ g_object_unref (widget);
+ }
+
+ g_object_notify (G_OBJECT (eom_sidebar), "current-page");
+}
+
+static GtkWidget *
+eom_sidebar_get_current_page (EomSidebar *sidebar)
+{
+ GtkNotebook *notebook = GTK_NOTEBOOK (sidebar->priv->notebook);
+
+ return gtk_notebook_get_nth_page
+ (notebook, gtk_notebook_get_current_page (notebook));
+}
+
+static void
+eom_sidebar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomSidebar *sidebar = EOM_SIDEBAR (object);
+
+ switch (prop_id) {
+ case PROP_CURRENT_PAGE:
+ eom_sidebar_set_page (sidebar, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+eom_sidebar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomSidebar *sidebar = EOM_SIDEBAR (object);
+
+ switch (prop_id) {
+ case PROP_CURRENT_PAGE:
+ g_value_set_object (value, eom_sidebar_get_current_page (sidebar));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+eom_sidebar_class_init (EomSidebarClass *eom_sidebar_class)
+{
+ GObjectClass *g_object_class;
+ GtkWidgetClass *widget_class;
+ GtkObjectClass *gtk_object_klass;
+
+ g_object_class = G_OBJECT_CLASS (eom_sidebar_class);
+ widget_class = GTK_WIDGET_CLASS (eom_sidebar_class);
+ gtk_object_klass = GTK_OBJECT_CLASS (eom_sidebar_class);
+
+ g_type_class_add_private (g_object_class, sizeof (EomSidebarPrivate));
+
+ gtk_object_klass->destroy = eom_sidebar_destroy;
+ g_object_class->get_property = eom_sidebar_get_property;
+ g_object_class->set_property = eom_sidebar_set_property;
+
+ g_object_class_install_property (g_object_class,
+ PROP_CURRENT_PAGE,
+ g_param_spec_object ("current-page",
+ "Current page",
+ "The currently visible page",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+
+ signals[SIGNAL_PAGE_ADDED] =
+ g_signal_new ("page-added",
+ EOM_TYPE_SIDEBAR,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EomSidebarClass, page_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ GTK_TYPE_WIDGET);
+
+ signals[SIGNAL_PAGE_REMOVED] =
+ g_signal_new ("page-removed",
+ EOM_TYPE_SIDEBAR,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EomSidebarClass, page_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ GTK_TYPE_WIDGET);
+}
+
+static void
+eom_sidebar_menu_position_under (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *widget;
+ GtkAllocation allocation;
+
+ g_return_if_fail (GTK_IS_BUTTON (user_data));
+ g_return_if_fail (!gtk_widget_get_has_window (user_data));
+
+ widget = GTK_WIDGET (user_data);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+
+ *x += allocation.x;
+ *y += allocation.y + allocation.height;
+
+ *push_in = FALSE;
+}
+
+static gboolean
+eom_sidebar_select_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ EomSidebar *eom_sidebar = EOM_SIDEBAR (user_data);
+
+ if (event->button == 1) {
+ GtkRequisition requisition;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_widget_set_size_request (eom_sidebar->priv->menu, -1, -1);
+ gtk_widget_size_request (eom_sidebar->priv->menu, &requisition);
+ gtk_widget_set_size_request (eom_sidebar->priv->menu,
+ MAX (allocation.width,
+ requisition.width), -1);
+
+ gtk_widget_grab_focus (widget);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+
+ gtk_menu_popup (GTK_MENU (eom_sidebar->priv->menu),
+ NULL, NULL, eom_sidebar_menu_position_under, widget,
+ event->button, event->time);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+eom_sidebar_select_button_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ EomSidebar *eom_sidebar = EOM_SIDEBAR (user_data);
+
+ if (event->keyval == GDK_space ||
+ event->keyval == GDK_KP_Space ||
+ event->keyval == GDK_Return ||
+ event->keyval == GDK_KP_Enter) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+
+ gtk_menu_popup (GTK_MENU (eom_sidebar->priv->menu),
+ NULL, NULL, eom_sidebar_menu_position_under, widget,
+ 1, event->time);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+eom_sidebar_close_clicked_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ EomSidebar *eom_sidebar = EOM_SIDEBAR (user_data);
+
+ gtk_widget_hide (GTK_WIDGET (eom_sidebar));
+}
+
+static void
+eom_sidebar_menu_deactivate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *menu_button;
+
+ menu_button = GTK_WIDGET (user_data);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
+}
+
+static void
+eom_sidebar_menu_detach_cb (GtkWidget *widget,
+ GtkMenu *menu)
+{
+ EomSidebar *eom_sidebar = EOM_SIDEBAR (widget);
+
+ eom_sidebar->priv->menu = NULL;
+}
+
+static void
+eom_sidebar_menu_item_activate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ EomSidebar *eom_sidebar = EOM_SIDEBAR (user_data);
+ GtkTreeIter iter;
+ GtkWidget *menu_item, *item;
+ gboolean valid;
+
+ menu_item = gtk_menu_get_active (GTK_MENU (eom_sidebar->priv->menu));
+ valid = gtk_tree_model_get_iter_first (eom_sidebar->priv->page_model, &iter);
+
+ while (valid) {
+ gtk_tree_model_get (eom_sidebar->priv->page_model, &iter,
+ PAGE_COLUMN_MENU_ITEM, &item,
+ -1);
+
+ if (item == menu_item) {
+ eom_sidebar_select_page (eom_sidebar, &iter);
+ valid = FALSE;
+ } else {
+ valid = gtk_tree_model_iter_next (eom_sidebar->priv->page_model, &iter);
+ }
+
+ g_object_unref (item);
+ }
+
+ g_object_notify (G_OBJECT (eom_sidebar), "current-page");
+}
+
+static void
+eom_sidebar_init (EomSidebar *eom_sidebar)
+{
+ GtkWidget *hbox;
+ GtkWidget *close_button;
+ GtkWidget *select_hbox;
+ GtkWidget *arrow;
+ GtkWidget *image;
+
+ eom_sidebar->priv = EOM_SIDEBAR_GET_PRIVATE (eom_sidebar);
+
+ /* data model */
+ eom_sidebar->priv->page_model = (GtkTreeModel *)
+ gtk_list_store_new (PAGE_COLUMN_NUM_COLS,
+ G_TYPE_STRING,
+ GTK_TYPE_WIDGET,
+ GTK_TYPE_WIDGET,
+ G_TYPE_INT);
+
+ /* top option menu */
+ hbox = gtk_hbox_new (FALSE, 0);
+ eom_sidebar->priv->hbox = hbox;
+ gtk_box_pack_start (GTK_BOX (eom_sidebar), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ eom_sidebar->priv->select_button = gtk_toggle_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (eom_sidebar->priv->select_button),
+ GTK_RELIEF_NONE);
+
+ g_signal_connect (eom_sidebar->priv->select_button, "button_press_event",
+ G_CALLBACK (eom_sidebar_select_button_press_cb),
+ eom_sidebar);
+
+ g_signal_connect (eom_sidebar->priv->select_button, "key_press_event",
+ G_CALLBACK (eom_sidebar_select_button_key_press_cb),
+ eom_sidebar);
+
+ select_hbox = gtk_hbox_new (FALSE, 0);
+
+ eom_sidebar->priv->label = gtk_label_new ("");
+
+ gtk_box_pack_start (GTK_BOX (select_hbox),
+ eom_sidebar->priv->label,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show (eom_sidebar->priv->label);
+
+ arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
+ gtk_widget_show (arrow);
+
+ gtk_container_add (GTK_CONTAINER (eom_sidebar->priv->select_button), select_hbox);
+ gtk_widget_show (select_hbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox), eom_sidebar->priv->select_button, TRUE, TRUE, 0);
+ gtk_widget_show (eom_sidebar->priv->select_button);
+
+ close_button = gtk_button_new ();
+
+ gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
+
+ g_signal_connect (close_button, "clicked",
+ G_CALLBACK (eom_sidebar_close_clicked_cb),
+ eom_sidebar);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (close_button), image);
+ gtk_widget_show (image);
+
+ gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
+ gtk_widget_show (close_button);
+
+ eom_sidebar->priv->menu = gtk_menu_new ();
+
+ g_signal_connect (eom_sidebar->priv->menu, "deactivate",
+ G_CALLBACK (eom_sidebar_menu_deactivate_cb),
+ eom_sidebar->priv->select_button);
+
+ gtk_menu_attach_to_widget (GTK_MENU (eom_sidebar->priv->menu),
+ GTK_WIDGET (eom_sidebar),
+ eom_sidebar_menu_detach_cb);
+
+ gtk_widget_show (eom_sidebar->priv->menu);
+
+ eom_sidebar->priv->notebook = gtk_notebook_new ();
+
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (eom_sidebar->priv->notebook), FALSE);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (eom_sidebar->priv->notebook), FALSE);
+
+ gtk_box_pack_start (GTK_BOX (eom_sidebar), eom_sidebar->priv->notebook,
+ TRUE, TRUE, 0);
+
+ gtk_widget_show (eom_sidebar->priv->notebook);
+}
+
+GtkWidget *
+eom_sidebar_new (void)
+{
+ GtkWidget *eom_sidebar;
+
+ eom_sidebar = g_object_new (EOM_TYPE_SIDEBAR, NULL);
+
+ return eom_sidebar;
+}
+
+void
+eom_sidebar_add_page (EomSidebar *eom_sidebar,
+ const gchar *title,
+ GtkWidget *main_widget)
+{
+ GtkTreeIter iter;
+ GtkWidget *menu_item;
+ gchar *label_title;
+ gint index;
+
+ g_return_if_fail (EOM_IS_SIDEBAR (eom_sidebar));
+ g_return_if_fail (GTK_IS_WIDGET (main_widget));
+
+ index = gtk_notebook_append_page (GTK_NOTEBOOK (eom_sidebar->priv->notebook),
+ main_widget, NULL);
+
+ menu_item = gtk_image_menu_item_new_with_label (title);
+
+ g_signal_connect (menu_item, "activate",
+ G_CALLBACK (eom_sidebar_menu_item_activate_cb),
+ eom_sidebar);
+
+ gtk_widget_show (menu_item);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (eom_sidebar->priv->menu),
+ menu_item);
+
+ /* Insert and move to end */
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (eom_sidebar->priv->page_model),
+ &iter, 0,
+ PAGE_COLUMN_TITLE, title,
+ PAGE_COLUMN_MENU_ITEM, menu_item,
+ PAGE_COLUMN_MAIN_WIDGET, main_widget,
+ PAGE_COLUMN_NOTEBOOK_INDEX, index,
+ -1);
+
+ gtk_list_store_move_before (GTK_LIST_STORE(eom_sidebar->priv->page_model),
+ &iter,
+ NULL);
+
+ /* Set the first item added as active */
+ gtk_tree_model_get_iter_first (eom_sidebar->priv->page_model, &iter);
+ gtk_tree_model_get (eom_sidebar->priv->page_model,
+ &iter,
+ PAGE_COLUMN_TITLE, &label_title,
+ PAGE_COLUMN_NOTEBOOK_INDEX, &index,
+ -1);
+
+ gtk_menu_set_active (GTK_MENU (eom_sidebar->priv->menu), index);
+
+ gtk_label_set_text (GTK_LABEL (eom_sidebar->priv->label), label_title);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (eom_sidebar->priv->notebook),
+ index);
+
+ g_free (label_title);
+
+ g_signal_emit (G_OBJECT (eom_sidebar),
+ signals[SIGNAL_PAGE_ADDED], 0, main_widget);
+}
+
+void
+eom_sidebar_remove_page (EomSidebar *eom_sidebar, GtkWidget *main_widget)
+{
+ GtkTreeIter iter;
+ GtkWidget *widget, *menu_item;
+ gboolean valid;
+ gint index;
+
+ g_return_if_fail (EOM_IS_SIDEBAR (eom_sidebar));
+ g_return_if_fail (GTK_IS_WIDGET (main_widget));
+
+ valid = gtk_tree_model_get_iter_first (eom_sidebar->priv->page_model, &iter);
+
+ while (valid) {
+ gtk_tree_model_get (eom_sidebar->priv->page_model, &iter,
+ PAGE_COLUMN_NOTEBOOK_INDEX, &index,
+ PAGE_COLUMN_MENU_ITEM, &menu_item,
+ PAGE_COLUMN_MAIN_WIDGET, &widget,
+ -1);
+
+ if (widget == main_widget) {
+ break;
+ } else {
+ valid = gtk_tree_model_iter_next (eom_sidebar->priv->page_model,
+ &iter);
+ }
+
+ g_object_unref (menu_item);
+ g_object_unref (widget);
+ }
+
+ if (valid) {
+ gtk_notebook_remove_page (GTK_NOTEBOOK (eom_sidebar->priv->notebook),
+ index);
+
+ gtk_container_remove (GTK_CONTAINER (eom_sidebar->priv->menu), menu_item);
+
+ gtk_list_store_remove (GTK_LIST_STORE (eom_sidebar->priv->page_model),
+ &iter);
+
+ g_signal_emit (G_OBJECT (eom_sidebar),
+ signals[SIGNAL_PAGE_REMOVED], 0, main_widget);
+ }
+}
+
+gint
+eom_sidebar_get_n_pages (EomSidebar *eom_sidebar)
+{
+ g_return_val_if_fail (EOM_IS_SIDEBAR (eom_sidebar), TRUE);
+
+ return gtk_tree_model_iter_n_children (
+ GTK_TREE_MODEL (eom_sidebar->priv->page_model), NULL);
+}
+
+gboolean
+eom_sidebar_is_empty (EomSidebar *eom_sidebar)
+{
+ g_return_val_if_fail (EOM_IS_SIDEBAR (eom_sidebar), TRUE);
+
+ return gtk_tree_model_iter_n_children (
+ GTK_TREE_MODEL (eom_sidebar->priv->page_model), NULL) == 0;
+}
diff --git a/src/eom-sidebar.h b/src/eom-sidebar.h
new file mode 100644
index 0000000..864c873
--- /dev/null
+++ b/src/eom-sidebar.h
@@ -0,0 +1,82 @@
+/* Eye of Mate - Side bar
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2007 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on evince code (shell/ev-sidebar.h) by:
+ * - Jonathan Blandford <[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_SIDEBAR_H__
+#define __EOM_SIDEBAR_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EomSidebar EomSidebar;
+typedef struct _EomSidebarClass EomSidebarClass;
+typedef struct _EomSidebarPrivate EomSidebarPrivate;
+
+#define EOM_TYPE_SIDEBAR (eom_sidebar_get_type())
+#define EOM_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_SIDEBAR, EomSidebar))
+#define EOM_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_SIDEBAR, EomSidebarClass))
+#define EOM_IS_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_SIDEBAR))
+#define EOM_IS_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_SIDEBAR))
+#define EOM_SIDEBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_SIDEBAR, EomSidebarClass))
+
+struct _EomSidebar {
+ GtkVBox base_instance;
+
+ EomSidebarPrivate *priv;
+};
+
+struct _EomSidebarClass {
+ GtkVBoxClass base_class;
+
+ void (* page_added) (EomSidebar *eom_sidebar,
+ GtkWidget *main_widget);
+
+ void (* page_removed) (EomSidebar *eom_sidebar,
+ GtkWidget *main_widget);
+};
+
+GType eom_sidebar_get_type (void);
+
+GtkWidget *eom_sidebar_new (void);
+
+void eom_sidebar_add_page (EomSidebar *eom_sidebar,
+ const gchar *title,
+ GtkWidget *main_widget);
+
+void eom_sidebar_remove_page (EomSidebar *eom_sidebar,
+ GtkWidget *main_widget);
+
+void eom_sidebar_set_page (EomSidebar *eom_sidebar,
+ GtkWidget *main_widget);
+
+gint eom_sidebar_get_n_pages (EomSidebar *eom_sidebar);
+
+gboolean eom_sidebar_is_empty (EomSidebar *eom_sidebar);
+
+G_END_DECLS
+
+#endif /* __EOM_SIDEBAR_H__ */
+
+
diff --git a/src/eom-statusbar.c b/src/eom-statusbar.c
new file mode 100644
index 0000000..7b4b993
--- /dev/null
+++ b/src/eom-statusbar.c
@@ -0,0 +1,157 @@
+/* Eye of Mate - Statusbar
+ *
+ * Copyright (C) 2000-2006 The Free Software Foundation
+ *
+ * Author: 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "eom-statusbar.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#define EOM_STATUSBAR_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_STATUSBAR, EomStatusbarPrivate))
+
+G_DEFINE_TYPE (EomStatusbar, eom_statusbar, GTK_TYPE_STATUSBAR)
+
+struct _EomStatusbarPrivate
+{
+ GtkWidget *progressbar;
+ GtkWidget *img_num_statusbar;
+};
+
+static void
+eom_statusbar_class_init (EomStatusbarClass *klass)
+{
+ GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (g_object_class, sizeof (EomStatusbarPrivate));
+}
+
+static void
+eom_statusbar_init (EomStatusbar *statusbar)
+{
+ EomStatusbarPrivate *priv;
+ GtkWidget *vbox;
+
+ statusbar->priv = EOM_STATUSBAR_GET_PRIVATE (statusbar);
+ priv = statusbar->priv;
+
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar), TRUE);
+
+ priv->img_num_statusbar = gtk_statusbar_new ();
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->img_num_statusbar), FALSE);
+ gtk_widget_set_size_request (priv->img_num_statusbar, 100, 10);
+ gtk_widget_show (priv->img_num_statusbar);
+
+ gtk_box_pack_end (GTK_BOX (statusbar),
+ priv->img_num_statusbar,
+ FALSE,
+ TRUE,
+ 0);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+
+ gtk_box_pack_end (GTK_BOX (statusbar),
+ vbox,
+ FALSE,
+ FALSE,
+ 2);
+
+ statusbar->priv->progressbar = gtk_progress_bar_new ();
+
+ gtk_box_pack_end (GTK_BOX (vbox),
+ priv->progressbar,
+ TRUE,
+ TRUE,
+ 2);
+
+ gtk_widget_set_size_request (priv->progressbar, -1, 10);
+
+ gtk_widget_show (vbox);
+
+ gtk_widget_hide (statusbar->priv->progressbar);
+
+}
+
+GtkWidget *
+eom_statusbar_new (void)
+{
+ return GTK_WIDGET (g_object_new (EOM_TYPE_STATUSBAR, NULL));
+}
+
+void
+eom_statusbar_set_image_number (EomStatusbar *statusbar,
+ gint num,
+ gint tot)
+{
+ gchar *msg;
+
+ g_return_if_fail (EOM_IS_STATUSBAR (statusbar));
+
+ gtk_statusbar_pop (GTK_STATUSBAR (statusbar->priv->img_num_statusbar), 0);
+
+ /* Translators: This string is displayed in the statusbar.
+ * The first token is the image number, the second is total image
+ * count.
+ *
+ * Translate to "%Id" if you want to use localized digits, or
+ * translate to "%d" otherwise.
+ *
+ * Note that translating this doesn't guarantee that you get localized
+ * digits. That needs support from your system and locale definition
+ * too.*/
+ msg = g_strdup_printf (_("%d / %d"), num, tot);
+
+ gtk_statusbar_push (GTK_STATUSBAR (statusbar->priv->img_num_statusbar), 0, msg);
+
+ g_free (msg);
+}
+
+void
+eom_statusbar_set_progress (EomStatusbar *statusbar,
+ gdouble progress)
+{
+ g_return_if_fail (EOM_IS_STATUSBAR (statusbar));
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (statusbar->priv->progressbar),
+ progress);
+
+ if (progress > 0 && progress < 1) {
+ gtk_widget_show (statusbar->priv->progressbar);
+ gtk_widget_hide (statusbar->priv->img_num_statusbar);
+ } else {
+ gtk_widget_hide (statusbar->priv->progressbar);
+ gtk_widget_show (statusbar->priv->img_num_statusbar);
+ }
+}
+
+void
+eom_statusbar_set_has_resize_grip (EomStatusbar *statusbar, gboolean has_resize_grip)
+{
+ g_return_if_fail (EOM_IS_STATUSBAR (statusbar));
+
+ gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar),
+ has_resize_grip);
+}
diff --git a/src/eom-statusbar.h b/src/eom-statusbar.h
new file mode 100644
index 0000000..13cd192
--- /dev/null
+++ b/src/eom-statusbar.h
@@ -0,0 +1,71 @@
+/* Eye of Mate - Statusbar
+ *
+ * Copyright (C) 2000-2006 The Free Software Foundation
+ *
+ * Author: 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_STATUSBAR_H__
+#define __EOM_STATUSBAR_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EomStatusbar EomStatusbar;
+typedef struct _EomStatusbarPrivate EomStatusbarPrivate;
+typedef struct _EomStatusbarClass EomStatusbarClass;
+
+#define EOM_TYPE_STATUSBAR (eom_statusbar_get_type ())
+#define EOM_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_STATUSBAR, EomStatusbar))
+#define EOM_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_STATUSBAR, EomStatusbarClass))
+#define EOM_IS_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_STATUSBAR))
+#define EOM_IS_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_STATUSBAR))
+#define EOM_STATUSBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_STATUSBAR, EomStatusbarClass))
+
+struct _EomStatusbar
+{
+ GtkStatusbar parent;
+
+ EomStatusbarPrivate *priv;
+};
+
+struct _EomStatusbarClass
+{
+ GtkStatusbarClass parent_class;
+};
+
+GType eom_statusbar_get_type (void) G_GNUC_CONST;
+
+GtkWidget *eom_statusbar_new (void);
+
+void eom_statusbar_set_image_number (EomStatusbar *statusbar,
+ gint num,
+ gint tot);
+
+void eom_statusbar_set_progress (EomStatusbar *statusbar,
+ gdouble progress);
+
+void eom_statusbar_set_has_resize_grip (EomStatusbar *statusbar,
+ gboolean has_resize_grip);
+
+G_END_DECLS
+
+#endif /* __EOM_STATUSBAR_H__ */
diff --git a/src/eom-thumb-nav.c b/src/eom-thumb-nav.c
new file mode 100644
index 0000000..7ccc352
--- /dev/null
+++ b/src/eom-thumb-nav.c
@@ -0,0 +1,591 @@
+/* Eye Of Mate - Thumbnail Navigator
+ *
+ * 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-thumb-nav.h"
+#include "eom-thumb-view.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define EOM_THUMB_NAV_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_THUMB_NAV, EomThumbNavPrivate))
+
+G_DEFINE_TYPE (EomThumbNav, eom_thumb_nav, GTK_TYPE_HBOX);
+
+#define EOM_THUMB_NAV_SCROLL_INC 20
+#define EOM_THUMB_NAV_SCROLL_MOVE 20
+#define EOM_THUMB_NAV_SCROLL_TIMEOUT 20
+
+enum
+{
+ PROP_SHOW_BUTTONS = 1,
+ PROP_THUMB_VIEW,
+ PROP_MODE
+};
+
+struct _EomThumbNavPrivate {
+ EomThumbNavMode mode;
+
+ gboolean show_buttons;
+ gboolean scroll_dir;
+ gint scroll_pos;
+ gint scroll_id;
+
+ GtkWidget *button_left;
+ GtkWidget *button_right;
+ GtkWidget *sw;
+ GtkWidget *thumbview;
+ GtkAdjustment *adj;
+};
+
+static gboolean
+eom_thumb_nav_scroll_event (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
+{
+ EomThumbNav *nav = EOM_THUMB_NAV (user_data);
+ gint inc = EOM_THUMB_NAV_SCROLL_INC * 3;
+
+ if (nav->priv->mode != EOM_THUMB_NAV_MODE_ONE_ROW)
+ return FALSE;
+
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_LEFT:
+ inc *= -1;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ case GDK_SCROLL_RIGHT:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ if (inc < 0)
+ gtk_adjustment_set_value (nav->priv->adj, MAX (0, gtk_adjustment_get_value (nav->priv->adj) + inc));
+ else
+ gtk_adjustment_set_value (nav->priv->adj, MIN (gtk_adjustment_get_upper (nav->priv->adj) - gtk_adjustment_get_page_size (nav->priv->adj), gtk_adjustment_get_value (nav->priv->adj) + inc));
+
+ return TRUE;
+}
+
+static void
+eom_thumb_nav_adj_changed (GtkAdjustment *adj, gpointer user_data)
+{
+ EomThumbNav *nav;
+ EomThumbNavPrivate *priv;
+ gboolean ltr;
+
+ nav = EOM_THUMB_NAV (user_data);
+ priv = EOM_THUMB_NAV_GET_PRIVATE (nav);
+ ltr = gtk_widget_get_direction (priv->sw) == GTK_TEXT_DIR_LTR;
+
+ gtk_widget_set_sensitive (ltr ? priv->button_right : priv->button_left,
+ gtk_adjustment_get_value (adj)
+ < gtk_adjustment_get_upper (adj)
+ - gtk_adjustment_get_page_size (adj));
+}
+
+static void
+eom_thumb_nav_adj_value_changed (GtkAdjustment *adj, gpointer user_data)
+{
+ EomThumbNav *nav;
+ EomThumbNavPrivate *priv;
+ gboolean ltr;
+
+ nav = EOM_THUMB_NAV (user_data);
+ priv = EOM_THUMB_NAV_GET_PRIVATE (nav);
+ ltr = gtk_widget_get_direction (priv->sw) == GTK_TEXT_DIR_LTR;
+
+ gtk_widget_set_sensitive (ltr ? priv->button_left : priv->button_right,
+ gtk_adjustment_get_value (adj) > 0);
+
+ gtk_widget_set_sensitive (ltr ? priv->button_right : priv->button_left,
+ gtk_adjustment_get_value (adj)
+ < gtk_adjustment_get_upper (adj)
+ - gtk_adjustment_get_page_size (adj));
+}
+
+static gboolean
+eom_thumb_nav_scroll_step (gpointer user_data)
+{
+ EomThumbNav *nav = EOM_THUMB_NAV (user_data);
+ GtkAdjustment *adj = nav->priv->adj;
+ gint delta;
+
+ if (nav->priv->scroll_pos < 10)
+ delta = EOM_THUMB_NAV_SCROLL_INC;
+ else if (nav->priv->scroll_pos < 20)
+ delta = EOM_THUMB_NAV_SCROLL_INC * 2;
+ else if (nav->priv->scroll_pos < 30)
+ delta = EOM_THUMB_NAV_SCROLL_INC * 2 + 5;
+ else
+ delta = EOM_THUMB_NAV_SCROLL_INC * 2 + 12;
+
+ if (!nav->priv->scroll_dir)
+ delta *= -1;
+
+ if ((gint) (gtk_adjustment_get_value (adj) + (gdouble) delta) >= 0 &&
+ (gint) (gtk_adjustment_get_value (adj) + (gdouble) delta) <= gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj)) {
+ gtk_adjustment_set_value(adj,
+ gtk_adjustment_get_value (adj) + (gdouble) delta);
+ nav->priv->scroll_pos++;
+ } else {
+ if (delta > 0)
+ gtk_adjustment_set_value (adj,
+ gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
+ else
+ gtk_adjustment_set_value (adj, 0);
+
+ nav->priv->scroll_pos = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+eom_thumb_nav_button_clicked (GtkButton *button, EomThumbNav *nav)
+{
+ nav->priv->scroll_pos = 0;
+
+ nav->priv->scroll_dir = gtk_widget_get_direction (GTK_WIDGET (button)) == GTK_TEXT_DIR_LTR ?
+ GTK_WIDGET (button) == nav->priv->button_right :
+ GTK_WIDGET (button) == nav->priv->button_left;
+
+ eom_thumb_nav_scroll_step (nav);
+}
+
+static void
+eom_thumb_nav_start_scroll (GtkButton *button, EomThumbNav *nav)
+{
+ nav->priv->scroll_dir = gtk_widget_get_direction (GTK_WIDGET (button)) == GTK_TEXT_DIR_LTR ?
+ GTK_WIDGET (button) == nav->priv->button_right :
+ GTK_WIDGET (button) == nav->priv->button_left;
+
+ nav->priv->scroll_id = g_timeout_add (EOM_THUMB_NAV_SCROLL_TIMEOUT,
+ eom_thumb_nav_scroll_step,
+ nav);
+}
+
+static void
+eom_thumb_nav_stop_scroll (GtkButton *button, EomThumbNav *nav)
+{
+ if (nav->priv->scroll_id > 0) {
+ g_source_remove (nav->priv->scroll_id);
+ nav->priv->scroll_id = 0;
+ nav->priv->scroll_pos = 0;
+ }
+}
+
+static void
+eom_thumb_nav_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomThumbNav *nav = EOM_THUMB_NAV (object);
+
+ switch (property_id)
+ {
+ case PROP_SHOW_BUTTONS:
+ g_value_set_boolean (value,
+ eom_thumb_nav_get_show_buttons (nav));
+ break;
+
+ case PROP_THUMB_VIEW:
+ g_value_set_object (value, nav->priv->thumbview);
+ break;
+
+ case PROP_MODE:
+ g_value_set_int (value,
+ eom_thumb_nav_get_mode (nav));
+ break;
+ }
+}
+
+static void
+eom_thumb_nav_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomThumbNav *nav = EOM_THUMB_NAV (object);
+
+ switch (property_id)
+ {
+ case PROP_SHOW_BUTTONS:
+ eom_thumb_nav_set_show_buttons (nav,
+ g_value_get_boolean (value));
+ break;
+
+ case PROP_THUMB_VIEW:
+ nav->priv->thumbview =
+ GTK_WIDGET (g_value_get_object (value));
+ break;
+
+ case PROP_MODE:
+ eom_thumb_nav_set_mode (nav,
+ g_value_get_int (value));
+ break;
+ }
+}
+
+static GObject *
+eom_thumb_nav_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ EomThumbNavPrivate *priv;
+
+ object = G_OBJECT_CLASS (eom_thumb_nav_parent_class)->constructor
+ (type, n_construct_properties, construct_params);
+
+ priv = EOM_THUMB_NAV (object)->priv;
+
+ if (priv->thumbview != NULL) {
+ gtk_container_add (GTK_CONTAINER (priv->sw), priv->thumbview);
+ gtk_widget_show_all (priv->sw);
+ }
+
+ return object;
+}
+
+static void
+eom_thumb_nav_class_init (EomThumbNavClass *class)
+{
+ GObjectClass *g_object_class = (GObjectClass *) class;
+
+ g_object_class->constructor = eom_thumb_nav_constructor;
+ g_object_class->get_property = eom_thumb_nav_get_property;
+ g_object_class->set_property = eom_thumb_nav_set_property;
+
+ g_object_class_install_property (g_object_class,
+ PROP_SHOW_BUTTONS,
+ g_param_spec_boolean ("show-buttons",
+ "Show Buttons",
+ "Whether to show navigation buttons or not",
+ TRUE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property (g_object_class,
+ PROP_THUMB_VIEW,
+ g_param_spec_object ("thumbview",
+ "Thumbnail View",
+ "The internal thumbnail viewer widget",
+ EOM_TYPE_THUMB_VIEW,
+ (G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE)));
+
+ g_object_class_install_property (g_object_class,
+ PROP_MODE,
+ g_param_spec_int ("mode",
+ "Mode",
+ "Thumb navigator mode",
+ EOM_THUMB_NAV_MODE_ONE_ROW,
+ EOM_THUMB_NAV_MODE_MULTIPLE_ROWS,
+ EOM_THUMB_NAV_MODE_ONE_ROW,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_type_class_add_private (g_object_class, sizeof (EomThumbNavPrivate));
+}
+
+static void
+eom_thumb_nav_init (EomThumbNav *nav)
+{
+ EomThumbNavPrivate *priv;
+ GtkWidget *arrow;
+
+ nav->priv = EOM_THUMB_NAV_GET_PRIVATE (nav);
+
+ priv = nav->priv;
+
+ priv->mode = EOM_THUMB_NAV_MODE_ONE_ROW;
+
+ priv->show_buttons = TRUE;
+
+ priv->button_left = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (priv->button_left), GTK_RELIEF_NONE);
+
+ arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN);
+ gtk_container_add (GTK_CONTAINER (priv->button_left), arrow);
+
+ gtk_widget_set_size_request (GTK_WIDGET (priv->button_left), 25, 0);
+
+ gtk_box_pack_start (GTK_BOX (nav), priv->button_left, FALSE, FALSE, 0);
+
+ g_signal_connect (priv->button_left,
+ "clicked",
+ G_CALLBACK (eom_thumb_nav_button_clicked),
+ nav);
+
+ g_signal_connect (priv->button_left,
+ "pressed",
+ G_CALLBACK (eom_thumb_nav_start_scroll),
+ nav);
+
+ g_signal_connect (priv->button_left,
+ "released",
+ G_CALLBACK (eom_thumb_nav_stop_scroll),
+ nav);
+
+ priv->sw = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_widget_set_name (gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW (priv->sw)), "eom-image-collection-scrollbar");
+
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->sw),
+ GTK_SHADOW_IN);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_NEVER);
+
+ g_signal_connect (priv->sw,
+ "scroll-event",
+ G_CALLBACK (eom_thumb_nav_scroll_event),
+ nav);
+
+ priv->adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->sw));
+
+ g_signal_connect (priv->adj,
+ "changed",
+ G_CALLBACK (eom_thumb_nav_adj_changed),
+ nav);
+
+ g_signal_connect (priv->adj,
+ "value-changed",
+ G_CALLBACK (eom_thumb_nav_adj_value_changed),
+ nav);
+
+ gtk_box_pack_start (GTK_BOX (nav), priv->sw, TRUE, TRUE, 0);
+
+ priv->button_right = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (priv->button_right), GTK_RELIEF_NONE);
+
+ arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+ gtk_container_add (GTK_CONTAINER (priv->button_right), arrow);
+
+ gtk_widget_set_size_request (GTK_WIDGET (priv->button_right), 25, 0);
+
+ gtk_box_pack_start (GTK_BOX (nav), priv->button_right, FALSE, FALSE, 0);
+
+ g_signal_connect (priv->button_right,
+ "clicked",
+ G_CALLBACK (eom_thumb_nav_button_clicked),
+ nav);
+
+ g_signal_connect (priv->button_right,
+ "pressed",
+ G_CALLBACK (eom_thumb_nav_start_scroll),
+ nav);
+
+ g_signal_connect (priv->button_right,
+ "released",
+ G_CALLBACK (eom_thumb_nav_stop_scroll),
+ nav);
+
+ gtk_adjustment_value_changed (priv->adj);
+}
+
+/**
+ * eom_thumb_nav_new:
+ * @thumbview: an #EomThumbView to embed in the navigation widget.
+ * @mode: The navigation mode.
+ * @show_buttons: Whether to show the navigation buttons.
+ *
+ * Creates a new thumbnail navigation widget.
+ *
+ * Returns: a new #EomThumbNav object.
+ **/
+GtkWidget *
+eom_thumb_nav_new (GtkWidget *thumbview,
+ EomThumbNavMode mode,
+ gboolean show_buttons)
+{
+ GObject *nav;
+
+ nav = g_object_new (EOM_TYPE_THUMB_NAV,
+ "show-buttons", show_buttons,
+ "mode", mode,
+ "thumbview", thumbview,
+ "homogeneous", FALSE,
+ "spacing", 0,
+ NULL);
+
+ return GTK_WIDGET (nav);
+}
+
+/**
+ * eom_thumb_nav_get_show_buttons:
+ * @nav: an #EomThumbNav.
+ *
+ * Gets whether the navigation buttons are visible.
+ *
+ * Returns: %TRUE if the navigation buttons are visible,
+ * %FALSE otherwise.
+ **/
+gboolean
+eom_thumb_nav_get_show_buttons (EomThumbNav *nav)
+{
+ g_return_val_if_fail (EOM_IS_THUMB_NAV (nav), FALSE);
+
+ return nav->priv->show_buttons;
+}
+
+/**
+ * eom_thumb_nav_set_show_buttons:
+ * @nav: an #EomThumbNav.
+ * @show_buttons: %TRUE to show the buttons, %FALSE to hide them.
+ *
+ * Sets whether the navigation buttons to the left and right of the
+ * widget should be visible.
+ **/
+void
+eom_thumb_nav_set_show_buttons (EomThumbNav *nav, gboolean show_buttons)
+{
+ g_return_if_fail (EOM_IS_THUMB_NAV (nav));
+ g_return_if_fail (nav->priv->button_left != NULL);
+ g_return_if_fail (nav->priv->button_right != NULL);
+
+ nav->priv->show_buttons = show_buttons;
+
+ if (show_buttons &&
+ nav->priv->mode == EOM_THUMB_NAV_MODE_ONE_ROW) {
+ gtk_widget_show_all (nav->priv->button_left);
+ gtk_widget_show_all (nav->priv->button_right);
+ } else {
+ gtk_widget_hide_all (nav->priv->button_left);
+ gtk_widget_hide_all (nav->priv->button_right);
+ }
+}
+
+/**
+ * eom_thumb_nav_get_mode:
+ * @nav: an #EomThumbNav.
+ *
+ * Gets the navigation mode in @nav.
+ *
+ * Returns: A value in #EomThumbNavMode.
+ **/
+EomThumbNavMode
+eom_thumb_nav_get_mode (EomThumbNav *nav)
+{
+ g_return_val_if_fail (EOM_IS_THUMB_NAV (nav), FALSE);
+
+ return nav->priv->mode;
+}
+
+/**
+ * eom_thumb_nav_set_mode:
+ * @nav: An #EomThumbNav.
+ * @mode: One of #EomThumbNavMode.
+ *
+ * Sets the navigation mode in @nav. See #EomThumbNavMode for details.
+ **/
+void
+eom_thumb_nav_set_mode (EomThumbNav *nav, EomThumbNavMode mode)
+{
+ EomThumbNavPrivate *priv;
+
+ g_return_if_fail (EOM_IS_THUMB_NAV (nav));
+
+ priv = nav->priv;
+
+ priv->mode = mode;
+
+ switch (mode)
+ {
+ case EOM_THUMB_NAV_MODE_ONE_ROW:
+ gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview),
+ G_MAXINT);
+
+ gtk_widget_set_size_request (priv->thumbview, -1, -1);
+ eom_thumb_view_set_item_height (EOM_THUMB_VIEW (priv->thumbview),
+ 115);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_NEVER);
+
+ eom_thumb_nav_set_show_buttons (nav, priv->show_buttons);
+
+ break;
+
+ case EOM_THUMB_NAV_MODE_ONE_COLUMN:
+ gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), 1);
+
+ gtk_widget_set_size_request (priv->thumbview, -1, -1);
+ eom_thumb_view_set_item_height (EOM_THUMB_VIEW (priv->thumbview),
+ -1);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_hide_all (priv->button_left);
+ gtk_widget_hide_all (priv->button_right);
+
+ break;
+
+ case EOM_THUMB_NAV_MODE_MULTIPLE_ROWS:
+ gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), -1);
+
+ gtk_widget_set_size_request (priv->thumbview, -1, 220);
+ eom_thumb_view_set_item_height (EOM_THUMB_VIEW (priv->thumbview),
+ -1);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_hide_all (priv->button_left);
+ gtk_widget_hide_all (priv->button_right);
+
+ break;
+
+ case EOM_THUMB_NAV_MODE_MULTIPLE_COLUMNS:
+ gtk_icon_view_set_columns (GTK_ICON_VIEW (priv->thumbview), -1);
+
+ gtk_widget_set_size_request (priv->thumbview, 230, -1);
+ eom_thumb_view_set_item_height (EOM_THUMB_VIEW (priv->thumbview),
+ -1);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->sw),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_hide_all (priv->button_left);
+ gtk_widget_hide_all (priv->button_right);
+
+ break;
+ }
+}
diff --git a/src/eom-thumb-nav.h b/src/eom-thumb-nav.h
new file mode 100644
index 0000000..0b67664
--- /dev/null
+++ b/src/eom-thumb-nav.h
@@ -0,0 +1,79 @@
+/* Eye Of Mate - Thumbnail Navigator
+ *
+ * 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_THUMB_NAV_H__
+#define __EOM_THUMB_NAV_H__
+
+#include "eom-thumb-view.h"
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EomThumbNav EomThumbNav;
+typedef struct _EomThumbNavClass EomThumbNavClass;
+typedef struct _EomThumbNavPrivate EomThumbNavPrivate;
+
+#define EOM_TYPE_THUMB_NAV (eom_thumb_nav_get_type ())
+#define EOM_THUMB_NAV(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_THUMB_NAV, EomThumbNav))
+#define EOM_THUMB_NAV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EOM_TYPE_THUMB_NAV, EomThumbNavClass))
+#define EOM_IS_THUMB_NAV(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_THUMB_NAV))
+#define EOM_IS_THUMB_NAV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EOM_TYPE_THUMB_NAV))
+#define EOM_THUMB_NAV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EOM_TYPE_THUMB_NAV, EomThumbNavClass))
+
+typedef enum {
+ EOM_THUMB_NAV_MODE_ONE_ROW,
+ EOM_THUMB_NAV_MODE_ONE_COLUMN,
+ EOM_THUMB_NAV_MODE_MULTIPLE_ROWS,
+ EOM_THUMB_NAV_MODE_MULTIPLE_COLUMNS
+} EomThumbNavMode;
+
+struct _EomThumbNav {
+ GtkHBox base_instance;
+
+ EomThumbNavPrivate *priv;
+};
+
+struct _EomThumbNavClass {
+ GtkHBoxClass parent_class;
+};
+
+GType eom_thumb_nav_get_type (void) G_GNUC_CONST;
+
+GtkWidget *eom_thumb_nav_new (GtkWidget *thumbview,
+ EomThumbNavMode mode,
+ gboolean show_buttons);
+
+gboolean eom_thumb_nav_get_show_buttons (EomThumbNav *nav);
+
+void eom_thumb_nav_set_show_buttons (EomThumbNav *nav,
+ gboolean show_buttons);
+
+EomThumbNavMode eom_thumb_nav_get_mode (EomThumbNav *nav);
+
+void eom_thumb_nav_set_mode (EomThumbNav *nav,
+ EomThumbNavMode mode);
+
+G_END_DECLS
+
+#endif /* __EOM_THUMB_NAV_H__ */
diff --git a/src/eom-thumb-view.c b/src/eom-thumb-view.c
new file mode 100644
index 0000000..f0444a3
--- /dev/null
+++ b/src/eom-thumb-view.c
@@ -0,0 +1,918 @@
+/* Eye Of Mate - Thumbnail View
+ *
+ * Copyright (C) 2006-2008 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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-thumb-view.h"
+#include "eom-list-store.h"
+#include "eom-image.h"
+#include "eom-job-queue.h"
+
+#ifdef HAVE_EXIF
+#include "eom-exif-util.h"
+#include <libexif/exif-data.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define EOM_THUMB_VIEW_SPACING 0
+
+#define EOM_THUMB_VIEW_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_THUMB_VIEW, EomThumbViewPrivate))
+
+G_DEFINE_TYPE (EomThumbView, eom_thumb_view, GTK_TYPE_ICON_VIEW);
+
+static EomImage* eom_thumb_view_get_image_from_path (EomThumbView *thumbview,
+ GtkTreePath *path);
+
+static void eom_thumb_view_popup_menu (EomThumbView *widget,
+ GdkEventButton *event);
+
+struct _EomThumbViewPrivate {
+ gint start_thumb; /* the first visible thumbnail */
+ gint end_thumb; /* the last visible thumbnail */
+ GtkWidget *menu; /* a contextual menu for thumbnails */
+ GtkCellRenderer *pixbuf_cell;
+};
+
+/* Drag 'n Drop */
+
+static void
+eom_thumb_view_finalize (GObject *object)
+{
+ EomThumbView *thumbview;
+ g_return_if_fail (EOM_IS_THUMB_VIEW (object));
+ thumbview = EOM_THUMB_VIEW (object);
+
+ G_OBJECT_CLASS (eom_thumb_view_parent_class)->finalize (object);
+}
+
+static void
+eom_thumb_view_destroy (GtkObject *object)
+{
+ EomThumbView *thumbview;
+ g_return_if_fail (EOM_IS_THUMB_VIEW (object));
+ thumbview = EOM_THUMB_VIEW (object);
+
+ GTK_OBJECT_CLASS (eom_thumb_view_parent_class)->destroy (object);
+}
+
+static void
+eom_thumb_view_class_init (EomThumbViewClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
+
+ gobject_class->finalize = eom_thumb_view_finalize;
+ object_class->destroy = eom_thumb_view_destroy;
+
+ g_type_class_add_private (class, sizeof (EomThumbViewPrivate));
+}
+
+static void
+eom_thumb_view_clear_range (EomThumbView *thumbview,
+ const gint start_thumb,
+ const gint end_thumb)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ EomListStore *store = EOM_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)));
+ gint thumb = start_thumb;
+ gboolean result;
+
+ g_assert (start_thumb <= end_thumb);
+
+ path = gtk_tree_path_new_from_indices (start_thumb, -1);
+ for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+ result && thumb <= end_thumb;
+ result = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter), thumb++) {
+ eom_list_store_thumbnail_unset (store, &iter);
+ }
+ gtk_tree_path_free (path);
+}
+
+static void
+eom_thumb_view_add_range (EomThumbView *thumbview,
+ const gint start_thumb,
+ const gint end_thumb)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ EomListStore *store = EOM_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)));
+ gint thumb = start_thumb;
+ gboolean result;
+
+ g_assert (start_thumb <= end_thumb);
+
+ path = gtk_tree_path_new_from_indices (start_thumb, -1);
+ for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+ result && thumb <= end_thumb;
+ result = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter), thumb++) {
+ eom_list_store_thumbnail_set (store, &iter);
+ }
+ gtk_tree_path_free (path);
+}
+
+static void
+eom_thumb_view_update_visible_range (EomThumbView *thumbview,
+ const gint start_thumb,
+ const gint end_thumb)
+{
+ EomThumbViewPrivate *priv = thumbview->priv;
+ int old_start_thumb, old_end_thumb;
+
+ old_start_thumb= priv->start_thumb;
+ old_end_thumb = priv->end_thumb;
+
+ if (start_thumb == old_start_thumb &&
+ end_thumb == old_end_thumb) {
+ return;
+ }
+
+ if (old_start_thumb < start_thumb)
+ eom_thumb_view_clear_range (thumbview, old_start_thumb, MIN (start_thumb - 1, old_end_thumb));
+
+ if (old_end_thumb > end_thumb)
+ eom_thumb_view_clear_range (thumbview, MAX (end_thumb + 1, old_start_thumb), old_end_thumb);
+
+ eom_thumb_view_add_range (thumbview, start_thumb, end_thumb);
+
+ priv->start_thumb = start_thumb;
+ priv->end_thumb = end_thumb;
+}
+
+static void
+thumbview_on_visible_range_changed_cb (EomThumbView *thumbview,
+ gpointer user_data)
+{
+ GtkTreePath *path1, *path2;
+
+ if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (thumbview), &path1, &path2)) {
+ return;
+ }
+
+ if (path1 == NULL) {
+ path1 = gtk_tree_path_new_first ();
+ }
+ if (path2 == NULL) {
+ gint n_items = gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)), NULL);
+ path2 = gtk_tree_path_new_from_indices (n_items - 1 , -1);
+ }
+
+ eom_thumb_view_update_visible_range (thumbview, gtk_tree_path_get_indices (path1) [0],
+ gtk_tree_path_get_indices (path2) [0]);
+
+ gtk_tree_path_free (path1);
+ gtk_tree_path_free (path2);
+}
+
+static void
+thumbview_on_adjustment_changed_cb (EomThumbView *thumbview,
+ gpointer user_data)
+{
+ GtkTreePath *path1, *path2;
+ gint start_thumb, end_thumb;
+
+ if (!gtk_icon_view_get_visible_range (GTK_ICON_VIEW (thumbview), &path1, &path2)) {
+ return;
+ }
+
+ if (path1 == NULL) {
+ path1 = gtk_tree_path_new_first ();
+ }
+ if (path2 == NULL) {
+ gint n_items = gtk_tree_model_iter_n_children (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)), NULL);
+ path2 = gtk_tree_path_new_from_indices (n_items - 1 , -1);
+ }
+
+ start_thumb = gtk_tree_path_get_indices (path1) [0];
+ end_thumb = gtk_tree_path_get_indices (path2) [0];
+
+ eom_thumb_view_add_range (thumbview, start_thumb, end_thumb);
+
+ /* case we added an image, we need to make sure that the shifted thumbnail is cleared */
+ eom_thumb_view_clear_range (thumbview, end_thumb + 1, end_thumb + 1);
+
+ thumbview->priv->start_thumb = start_thumb;
+ thumbview->priv->end_thumb = end_thumb;
+
+ gtk_tree_path_free (path1);
+ gtk_tree_path_free (path2);
+}
+
+static void
+thumbview_on_parent_set_cb (GtkWidget *widget,
+ GtkObject *old_parent,
+ gpointer user_data)
+{
+ EomThumbView *thumbview = EOM_THUMB_VIEW (widget);
+ GtkScrolledWindow *sw;
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+
+ GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (thumbview));
+ if (!GTK_IS_SCROLLED_WINDOW (parent)) {
+ return;
+ }
+
+ /* if we have been set to a ScrolledWindow, we connect to the callback
+ to set and unset thumbnails. */
+ sw = GTK_SCROLLED_WINDOW (parent);
+ hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw));
+ vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
+
+ /* when scrolling */
+ g_signal_connect_data (G_OBJECT (hadjustment), "value-changed",
+ G_CALLBACK (thumbview_on_visible_range_changed_cb),
+ thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ g_signal_connect_data (G_OBJECT (vadjustment), "value-changed",
+ G_CALLBACK (thumbview_on_visible_range_changed_cb),
+ thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ /* when the adjustment is changed, ie. probably we have new images added. */
+ g_signal_connect_data (G_OBJECT (hadjustment), "changed",
+ G_CALLBACK (thumbview_on_adjustment_changed_cb),
+ thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ g_signal_connect_data (G_OBJECT (vadjustment), "changed",
+ G_CALLBACK (thumbview_on_adjustment_changed_cb),
+ thumbview, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ /* when resizing the scrolled window */
+ g_signal_connect_swapped (G_OBJECT (sw), "size-allocate",
+ G_CALLBACK (thumbview_on_visible_range_changed_cb),
+ thumbview);
+}
+
+static gboolean
+thumbview_on_button_press_event_cb (GtkWidget *thumbview, GdkEventButton *event,
+ gpointer user_data)
+{
+ GtkTreePath *path;
+
+ /* Ignore double-clicks and triple-clicks */
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ {
+ path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (thumbview),
+ (gint) event->x, (gint) event->y);
+ if (path == NULL) {
+ return FALSE;
+ }
+
+ if (!gtk_icon_view_path_is_selected (GTK_ICON_VIEW (thumbview), path) ||
+ eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (thumbview)) != 1) {
+ gtk_icon_view_unselect_all (GTK_ICON_VIEW (thumbview));
+ gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path);
+ gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE);
+ }
+ eom_thumb_view_popup_menu (EOM_THUMB_VIEW (thumbview), event);
+
+ gtk_tree_path_free (path);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+thumbview_on_drag_data_get_cb (GtkWidget *widget,
+ GdkDragContext *drag_context,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ GList *list;
+ GList *node;
+ EomImage *image;
+ GFile *file;
+ gchar **uris = NULL;
+ gint i = 0, n_images;
+
+ list = eom_thumb_view_get_selected_images (EOM_THUMB_VIEW (widget));
+ n_images = eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (widget));
+
+ uris = g_new (gchar *, n_images + 1);
+
+ for (node = list; node != NULL; node = node->next, i++) {
+ image = EOM_IMAGE (node->data);
+ file = eom_image_get_file (image);
+ uris[i] = g_file_get_uri (file);
+ g_object_unref (image);
+ g_object_unref (file);
+ }
+ uris[i] = NULL;
+
+ gtk_selection_data_set_uris (data, uris);
+ g_strfreev (uris);
+ g_list_free (list);
+}
+
+static gchar *
+thumbview_get_tooltip_string (EomImage *image)
+{
+ gchar *bytes;
+ char *type_str;
+ gint width, height;
+ GFile *file;
+ GFileInfo *file_info;
+ const char *mime_str;
+ gchar *tooltip_string;
+#ifdef HAVE_EXIF
+ ExifData *exif_data;
+#endif
+
+ bytes = g_format_size_for_display (eom_image_get_bytes (image));
+
+ eom_image_get_size (image, &width, &height);
+
+ file = eom_image_get_file (image);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ g_object_unref (file);
+ if (file_info == NULL) {
+ return NULL;
+ }
+
+ mime_str = g_file_info_get_content_type (file_info);
+
+ if (G_UNLIKELY (mime_str == NULL)) {
+ g_free (bytes);
+ g_object_unref (image);
+ return NULL;
+ }
+
+ type_str = g_content_type_get_description (mime_str);
+ g_object_unref (file_info);
+
+ if (width > -1 && height > -1) {
+ tooltip_string = g_markup_printf_escaped ("<b><big>%s</big></b>\n"
+ "%i x %i %s\n"
+ "%s\n"
+ "%s",
+ eom_image_get_caption (image),
+ width,
+ height,
+ ngettext ("pixel",
+ "pixels",
+ height),
+ bytes,
+ type_str);
+ } else {
+ tooltip_string = g_markup_printf_escaped ("<b><big>%s</big></b>\n"
+ "%s\n"
+ "%s",
+ eom_image_get_caption (image),
+ bytes,
+ type_str);
+
+ }
+
+#ifdef HAVE_EXIF
+ exif_data = (ExifData *) eom_image_get_exif_info (image);
+
+ if (exif_data) {
+ gchar *extra_info, *tmp, *date;
+ /* The EXIF standard says that the DATE_TIME tag is
+ * 20 bytes long. A 32-byte buffer should be large enough. */
+ gchar time_buffer[32];
+
+ date = eom_exif_util_format_date (
+ eom_exif_util_get_value (exif_data, EXIF_TAG_DATE_TIME_ORIGINAL, time_buffer, 32));
+
+ if (date) {
+ extra_info = g_strdup_printf ("\n%s %s", _("Taken on"), date);
+
+ tmp = g_strconcat (tooltip_string, extra_info, NULL);
+
+ g_free (date);
+ g_free (extra_info);
+ g_free (tooltip_string);
+
+ tooltip_string = tmp;
+ }
+ exif_data_unref (exif_data);
+ }
+#endif
+
+ g_free (type_str);
+ g_free (bytes);
+
+ return tooltip_string;
+}
+
+static void
+on_data_loaded_cb (EomJob *job, gpointer data)
+{
+ if (!job->error) {
+ gtk_tooltip_trigger_tooltip_query (gdk_display_get_default());
+ }
+}
+
+static gboolean
+thumbview_on_query_tooltip_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data)
+{
+ GtkTreePath *path;
+ EomImage *image;
+ gchar *tooltip_string;
+ EomImageData data = 0;
+
+ if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget),
+ &x, &y, keyboard_mode,
+ NULL, &path, NULL)) {
+ return FALSE;
+ }
+
+ image = eom_thumb_view_get_image_from_path (EOM_THUMB_VIEW (widget),
+ path);
+ gtk_tree_path_free (path);
+
+ if (image == NULL) {
+ return FALSE;
+ }
+
+ if (!eom_image_has_data (image, EOM_IMAGE_DATA_EXIF) &&
+ eom_image_get_metadata_status (image) == EOM_IMAGE_METADATA_NOT_READ) {
+ data = EOM_IMAGE_DATA_EXIF;
+ }
+
+ if (!eom_image_has_data (image, EOM_IMAGE_DATA_DIMENSION)) {
+ data |= EOM_IMAGE_DATA_DIMENSION;
+ }
+
+ if (data) {
+ EomJob *job;
+
+ job = eom_job_load_new (image, data);
+ g_signal_connect (G_OBJECT (job), "finished",
+ G_CALLBACK (on_data_loaded_cb),
+ widget);
+ eom_job_queue_add_job (job);
+ g_object_unref (image);
+ g_object_unref (job);
+ return FALSE;
+ }
+
+ tooltip_string = thumbview_get_tooltip_string (image);
+ g_object_unref (image);
+
+ if (tooltip_string == NULL) {
+ return FALSE;
+ }
+
+ gtk_tooltip_set_markup (tooltip, tooltip_string);
+ g_free (tooltip_string);
+
+ return TRUE;
+}
+
+static void
+eom_thumb_view_init (EomThumbView *thumbview)
+{
+ thumbview->priv = EOM_THUMB_VIEW_GET_PRIVATE (thumbview);
+
+ thumbview->priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new ();
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (thumbview),
+ thumbview->priv->pixbuf_cell,
+ FALSE);
+
+ g_object_set (thumbview->priv->pixbuf_cell,
+ "follow-state", FALSE,
+ "height", 100,
+ "width", 115,
+ "yalign", 0.5,
+ "xalign", 0.5,
+ NULL);
+
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (thumbview),
+ thumbview->priv->pixbuf_cell,
+ "pixbuf", EOM_LIST_STORE_THUMBNAIL,
+ NULL);
+
+ gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (thumbview),
+ GTK_SELECTION_MULTIPLE);
+
+ gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (thumbview),
+ EOM_THUMB_VIEW_SPACING);
+
+ gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (thumbview),
+ EOM_THUMB_VIEW_SPACING);
+
+ g_object_set (thumbview, "has-tooltip", TRUE, NULL);
+
+ g_signal_connect (thumbview,
+ "query-tooltip",
+ G_CALLBACK (thumbview_on_query_tooltip_cb),
+ NULL);
+
+ thumbview->priv->start_thumb = 0;
+ thumbview->priv->end_thumb = 0;
+ thumbview->priv->menu = NULL;
+
+ g_signal_connect (G_OBJECT (thumbview), "parent-set",
+ G_CALLBACK (thumbview_on_parent_set_cb), NULL);
+
+ gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (thumbview), 0,
+ NULL, 0,
+ GDK_ACTION_COPY);
+ gtk_drag_source_add_uri_targets (GTK_WIDGET (thumbview));
+
+ g_signal_connect (G_OBJECT (thumbview), "drag-data-get",
+ G_CALLBACK (thumbview_on_drag_data_get_cb), NULL);
+}
+
+/**
+ * eom_thumb_view_new:
+ *
+ * Creates a new #EomThumbView object.
+ *
+ * Returns: a newly created #EomThumbView.
+ **/
+GtkWidget *
+eom_thumb_view_new (void)
+{
+ EomThumbView *thumbview;
+
+ thumbview = g_object_new (EOM_TYPE_THUMB_VIEW, NULL);
+
+ return GTK_WIDGET (thumbview);
+}
+
+/**
+ * eom_thumb_view_set_model:
+ * @thumbview: A #EomThumbView.
+ * @store: A #EomListStore.
+ *
+ * Sets the #EomListStore to be used with @thumbview. If an initial image
+ * was set during @store creation, its thumbnail will be selected and visible.
+ *
+ **/
+void
+eom_thumb_view_set_model (EomThumbView *thumbview, EomListStore *store)
+{
+ gint index;
+
+ g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview));
+ g_return_if_fail (EOM_IS_LIST_STORE (store));
+
+ index = eom_list_store_get_initial_pos (store);
+
+ gtk_icon_view_set_model (GTK_ICON_VIEW (thumbview), GTK_TREE_MODEL (store));
+
+ if (index >= 0) {
+ GtkTreePath *path = gtk_tree_path_new_from_indices (index, -1);
+ gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path);
+ gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE);
+ gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumbview), path, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+ }
+}
+
+/**
+ * eom_thumb_view_set_item_height:
+ * @thumbview: A #EomThumbView.
+ * @height: The desired height.
+ *
+ * Sets the height of each thumbnail in @thumbview.
+ *
+ **/
+void
+eom_thumb_view_set_item_height (EomThumbView *thumbview, gint height)
+{
+ g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview));
+
+ g_object_set (thumbview->priv->pixbuf_cell,
+ "height", height,
+ NULL);
+}
+
+static void
+eom_thumb_view_get_n_selected_helper (GtkIconView *thumbview,
+ GtkTreePath *path,
+ gpointer data)
+{
+ /* data is of type (guint *) */
+ (*(guint *) data) ++;
+}
+
+/**
+ * eom_thumb_view_get_n_selected:
+ * @thumbview: An #EomThumbView.
+ *
+ * Gets the number of images that are currently selected in @thumbview.
+ *
+ * Returns: the number of selected images in @thumbview.
+ **/
+guint
+eom_thumb_view_get_n_selected (EomThumbView *thumbview)
+{
+ guint count = 0;
+ gtk_icon_view_selected_foreach (GTK_ICON_VIEW (thumbview),
+ eom_thumb_view_get_n_selected_helper,
+ (&count));
+ return count;
+}
+
+/**
+ * eom_thumb_view_get_image_from_path:
+ * @thumbview: A #EomThumbView.
+ * @path: A #GtkTreePath pointing to a #EomImage in the model for @thumbview.
+ *
+ * Gets the #EomImage stored in @thumbview's #EomListStore at the position indicated
+ * by @path.
+ *
+ * Returns: A #EomImage.
+ **/
+static EomImage *
+eom_thumb_view_get_image_from_path (EomThumbView *thumbview, GtkTreePath *path)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ EomImage *image;
+
+ model = gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview));
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_tree_model_get (model, &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+
+ return image;
+}
+
+/**
+ * eom_thumb_view_get_first_selected_image:
+ * @thumbview: A #EomThumbView.
+ *
+ * Returns the first selected image. Note that the returned #EomImage
+ * is not ensured to be really the first selected image in @thumbview, but
+ * generally, it will be.
+ *
+ * Returns: A #EomImage.
+ **/
+EomImage *
+eom_thumb_view_get_first_selected_image (EomThumbView *thumbview)
+{
+ /* The returned list is not sorted! We need to find the
+ smaller tree path value => tricky and expensive. Do we really need this?
+ */
+ EomImage *image;
+ GtkTreePath *path;
+ GList *list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumbview));
+
+ if (list == NULL) {
+ return NULL;
+ }
+
+ path = (GtkTreePath *) (list->data);
+
+ image = eom_thumb_view_get_image_from_path (thumbview, path);
+
+ g_list_foreach (list, (GFunc) gtk_tree_path_free , NULL);
+ g_list_free (list);
+
+ return image;
+}
+
+/**
+ * eom_thumb_view_get_selected_images:
+ * @thumbview: A #EomThumbView.
+ *
+ * Gets a list with the currently selected images. Note that a new reference is
+ * hold for each image and the list must be freed with g_list_free().
+ *
+ * Returns: A newly allocated list of #EomImage's.
+ **/
+GList *
+eom_thumb_view_get_selected_images (EomThumbView *thumbview)
+{
+ GList *l, *item;
+ GList *list = NULL;
+
+ GtkTreePath *path;
+
+ l = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumbview));
+
+ for (item = l; item != NULL; item = item->next) {
+ path = (GtkTreePath *) item->data;
+ list = g_list_prepend (list, eom_thumb_view_get_image_from_path (thumbview, path));
+ gtk_tree_path_free (path);
+ }
+
+ g_list_free (l);
+ list = g_list_reverse (list);
+
+ return list;
+}
+
+/**
+ * eom_thumb_view_set_current_image:
+ * @thumbview: A #EomThumbView.
+ * @image: The image to be selected.
+ * @deselect_other: Whether to deselect currently selected images.
+ *
+ * Changes the status of a image, marking it as currently selected.
+ * If @deselect_other is %TRUE, all other selected images will be
+ * deselected.
+ *
+ **/
+void
+eom_thumb_view_set_current_image (EomThumbView *thumbview, EomImage *image,
+ gboolean deselect_other)
+{
+ GtkTreePath *path;
+ EomListStore *store;
+ gint pos;
+
+ store = EOM_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview)));
+ pos = eom_list_store_get_pos_by_image (store, image);
+ path = gtk_tree_path_new_from_indices (pos, -1);
+
+ if (path == NULL) {
+ return;
+ }
+
+ if (deselect_other) {
+ gtk_icon_view_unselect_all (GTK_ICON_VIEW (thumbview));
+ }
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path);
+ gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE);
+ gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumbview), path, FALSE, 0, 0);
+
+ gtk_tree_path_free (path);
+}
+
+/**
+ * eom_thumb_view_select_single:
+ * @thumbview: A #EomThumbView.
+ * @change: A #EomThumbViewSelectionChange, describing the
+ * desired selection change.
+ *
+ * Changes the current selection according to a single movement
+ * described by #EomThumbViewSelectionChange. If there are no
+ * thumbnails currently selected, one is selected according to the
+ * natural selection according to the #EomThumbViewSelectionChange
+ * used, p.g., when %EOM_THUMB_VIEW_SELECT_RIGHT is the selected change,
+ * the first thumbnail will be selected.
+ *
+ **/
+void
+eom_thumb_view_select_single (EomThumbView *thumbview,
+ EomThumbViewSelectionChange change)
+{
+ GtkTreePath *path = NULL;
+ GtkTreeModel *model;
+ GList *list;
+ gint n_items;
+
+ g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview));
+
+ model = gtk_icon_view_get_model (GTK_ICON_VIEW (thumbview));
+
+ n_items = eom_list_store_length (EOM_LIST_STORE (model));
+
+ if (n_items == 0) {
+ return;
+ }
+
+ if (eom_thumb_view_get_n_selected (thumbview) == 0) {
+ switch (change) {
+ case EOM_THUMB_VIEW_SELECT_CURRENT:
+ break;
+ case EOM_THUMB_VIEW_SELECT_RIGHT:
+ case EOM_THUMB_VIEW_SELECT_FIRST:
+ path = gtk_tree_path_new_first ();
+ break;
+ case EOM_THUMB_VIEW_SELECT_LEFT:
+ case EOM_THUMB_VIEW_SELECT_LAST:
+ path = gtk_tree_path_new_from_indices (n_items - 1, -1);
+ break;
+ case EOM_THUMB_VIEW_SELECT_RANDOM:
+ path = gtk_tree_path_new_from_indices ((int)(((float)(n_items - 1) * rand()) / (float)(RAND_MAX + 1.f)), -1);
+ break;
+ }
+ } else {
+ list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (thumbview));
+ path = gtk_tree_path_copy ((GtkTreePath *) list->data);
+ g_list_foreach (list, (GFunc) gtk_tree_path_free , NULL);
+ g_list_free (list);
+
+ gtk_icon_view_unselect_all (GTK_ICON_VIEW (thumbview));
+
+ switch (change) {
+ case EOM_THUMB_VIEW_SELECT_CURRENT:
+ break;
+ case EOM_THUMB_VIEW_SELECT_LEFT:
+ if (!gtk_tree_path_prev (path)) {
+ gtk_tree_path_free (path);
+ path = gtk_tree_path_new_from_indices (n_items - 1, -1);
+ }
+ break;
+ case EOM_THUMB_VIEW_SELECT_RIGHT:
+ if (gtk_tree_path_get_indices (path) [0] == n_items - 1) {
+ gtk_tree_path_free (path);
+ path = gtk_tree_path_new_first ();
+ } else {
+ gtk_tree_path_next (path);
+ }
+ break;
+ case EOM_THUMB_VIEW_SELECT_FIRST:
+ gtk_tree_path_free (path);
+ path = gtk_tree_path_new_first ();
+ break;
+ case EOM_THUMB_VIEW_SELECT_LAST:
+ gtk_tree_path_free (path);
+ path = gtk_tree_path_new_from_indices (n_items - 1, -1);
+ break;
+ case EOM_THUMB_VIEW_SELECT_RANDOM:
+ gtk_tree_path_free (path);
+ path = gtk_tree_path_new_from_indices ((int)(((float)(n_items - 1) * rand()) / (float)(RAND_MAX + 1.f)), -1);
+ break;
+ }
+ }
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (thumbview), path);
+ gtk_icon_view_set_cursor (GTK_ICON_VIEW (thumbview), path, NULL, FALSE);
+ gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (thumbview), path, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+}
+
+
+/**
+ * eom_thumb_view_set_thumbnail_popup:
+ * @thumbview: An #EomThumbView.
+ * @menu: A #GtkMenu.
+ *
+ * Set the contextual menu to be used with the thumbnails in the
+ * widget. This can be done only once.
+ *
+ **/
+void
+eom_thumb_view_set_thumbnail_popup (EomThumbView *thumbview,
+ GtkMenu *menu)
+{
+ g_return_if_fail (EOM_IS_THUMB_VIEW (thumbview));
+ g_return_if_fail (thumbview->priv->menu == NULL);
+
+ thumbview->priv->menu = g_object_ref (menu);
+
+ gtk_menu_attach_to_widget (GTK_MENU (thumbview->priv->menu),
+ GTK_WIDGET (thumbview),
+ NULL);
+
+ g_signal_connect (G_OBJECT (thumbview), "button_press_event",
+ G_CALLBACK (thumbview_on_button_press_event_cb), NULL);
+
+}
+
+
+static void
+eom_thumb_view_popup_menu (EomThumbView *thumbview, GdkEventButton *event)
+{
+ GtkWidget *popup;
+ int button, event_time;
+
+ popup = thumbview->priv->menu;
+
+ if (event) {
+ button = event->button;
+ event_time = event->time;
+ } else {
+ button = 0;
+ event_time = gtk_get_current_event_time ();
+ }
+
+ gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL,
+ button, event_time);
+}
diff --git a/src/eom-thumb-view.h b/src/eom-thumb-view.h
new file mode 100644
index 0000000..a0f7bf1
--- /dev/null
+++ b/src/eom-thumb-view.h
@@ -0,0 +1,87 @@
+/* Eye Of Mate - Thumbnail View
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[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_THUMB_VIEW_H
+#define EOM_THUMB_VIEW_H
+
+#include "eom-image.h"
+#include "eom-list-store.h"
+
+G_BEGIN_DECLS
+
+#define EOM_TYPE_THUMB_VIEW (eom_thumb_view_get_type ())
+#define EOM_THUMB_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_THUMB_VIEW, EomThumbView))
+#define EOM_THUMB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_THUMB_VIEW, EomThumbViewClass))
+#define EOM_IS_THUMB_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_THUMB_VIEW))
+#define EOM_IS_THUMB_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_THUMB_VIEW))
+#define EOM_THUMB_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_THUMB_VIEW, EomThumbViewClass))
+
+typedef struct _EomThumbView EomThumbView;
+typedef struct _EomThumbViewClass EomThumbViewClass;
+typedef struct _EomThumbViewPrivate EomThumbViewPrivate;
+
+typedef enum {
+ EOM_THUMB_VIEW_SELECT_CURRENT = 0,
+ EOM_THUMB_VIEW_SELECT_LEFT,
+ EOM_THUMB_VIEW_SELECT_RIGHT,
+ EOM_THUMB_VIEW_SELECT_FIRST,
+ EOM_THUMB_VIEW_SELECT_LAST,
+ EOM_THUMB_VIEW_SELECT_RANDOM
+} EomThumbViewSelectionChange;
+
+struct _EomThumbView {
+ GtkIconView icon_view;
+ EomThumbViewPrivate *priv;
+};
+
+struct _EomThumbViewClass {
+ GtkIconViewClass icon_view_class;
+};
+
+GType eom_thumb_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget *eom_thumb_view_new (void);
+
+void eom_thumb_view_set_model (EomThumbView *thumbview,
+ EomListStore *store);
+
+void eom_thumb_view_set_item_height (EomThumbView *thumbview,
+ gint height);
+
+guint eom_thumb_view_get_n_selected (EomThumbView *thumbview);
+
+EomImage *eom_thumb_view_get_first_selected_image (EomThumbView *thumbview);
+
+GList *eom_thumb_view_get_selected_images (EomThumbView *thumbview);
+
+void eom_thumb_view_select_single (EomThumbView *thumbview,
+ EomThumbViewSelectionChange change);
+
+void eom_thumb_view_set_current_image (EomThumbView *thumbview,
+ EomImage *image,
+ gboolean deselect_other);
+
+void eom_thumb_view_set_thumbnail_popup (EomThumbView *thumbview,
+ GtkMenu *menu);
+
+G_END_DECLS
+
+#endif /* EOM_THUMB_VIEW_H */
diff --git a/src/eom-thumbnail.c b/src/eom-thumbnail.c
new file mode 100644
index 0000000..d84cb24
--- /dev/null
+++ b/src/eom-thumbnail.c
@@ -0,0 +1,509 @@
+/* Eye Of Mate - Thumbnailing functions
+ *
+ * Copyright (C) 2000-2008 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on eel code (eel/eel-graphic-effects.c) by:
+ * - Andy Hertzfeld <[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
+
+/* 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>
+
+#include "eom-thumbnail.h"
+#include "eom-list-store.h"
+#include "eom-debug.h"
+
+#define EOM_THUMB_ERROR eom_thumb_error_quark ()
+
+static MateDesktopThumbnailFactory *factory = NULL;
+static GdkPixbuf *frame = NULL;
+
+typedef enum {
+ EOM_THUMB_ERROR_VFS,
+ EOM_THUMB_ERROR_GENERIC,
+ EOM_THUMB_ERROR_UNKNOWN
+} EomThumbError;
+
+typedef struct {
+ char *uri_str;
+ char *thumb_path;
+ time_t mtime;
+ char *mime_type;
+ gboolean thumb_exists;
+ gboolean failed_thumb_exists;
+ gboolean can_read;
+} EomThumbData;
+
+static GQuark
+eom_thumb_error_quark (void)
+{
+ static GQuark q = 0;
+ if (q == 0)
+ q = g_quark_from_static_string ("eom-thumb-error-quark");
+
+ return q;
+}
+
+static void
+set_vfs_error (GError **error, GError *ioerror)
+{
+ g_set_error (error,
+ EOM_THUMB_ERROR,
+ EOM_THUMB_ERROR_VFS,
+ "%s", ioerror ? ioerror->message : "VFS error making a thumbnail");
+}
+
+static void
+set_thumb_error (GError **error, int error_id, const char *string)
+{
+ g_set_error (error,
+ EOM_THUMB_ERROR,
+ error_id,
+ "%s", string);
+}
+
+static GdkPixbuf*
+get_valid_thumbnail (EomThumbData *data, GError **error)
+{
+ GdkPixbuf *thumb = NULL;
+
+ g_return_val_if_fail (data != NULL, NULL);
+
+ /* does a thumbnail under the path exists? */
+ if (data->thumb_exists) {
+ thumb = gdk_pixbuf_new_from_file (data->thumb_path, error);
+
+ /* is this thumbnail file up to date? */
+ if (thumb != NULL && !mate_desktop_thumbnail_is_valid (thumb, data->uri_str, data->mtime)) {
+ g_object_unref (thumb);
+ thumb = NULL;
+ }
+ }
+
+ return thumb;
+}
+
+static GdkPixbuf *
+create_thumbnail_from_pixbuf (EomThumbData *data,
+ GdkPixbuf *pixbuf,
+ GError **error)
+{
+ GdkPixbuf *thumb;
+ gint width, height;
+ gfloat perc;
+
+ g_assert (factory != NULL);
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ perc = CLAMP (128.0/(MAX (width, height)), 0, 1);
+
+ thumb = mate_desktop_thumbnail_scale_down_pixbuf (pixbuf,
+ width*perc,
+ height*perc);
+
+ return thumb;
+}
+
+static void
+eom_thumb_data_free (EomThumbData *data)
+{
+ if (data == NULL)
+ return;
+
+ g_free (data->thumb_path);
+ g_free (data->mime_type);
+ g_free (data->uri_str);
+
+ g_slice_free (EomThumbData, data);
+}
+
+static EomThumbData*
+eom_thumb_data_new (GFile *file, GError **error)
+{
+ EomThumbData *data;
+ GFileInfo *file_info;
+ GError *ioerror = NULL;
+
+ g_return_val_if_fail (file != NULL, NULL);
+ g_return_val_if_fail (error != NULL && *error == NULL, NULL);
+
+ data = g_slice_new0 (EomThumbData);
+
+ data->uri_str = g_file_get_uri (file);
+ data->thumb_path = mate_desktop_thumbnail_path_for_uri (data->uri_str, MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL);
+
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED ","
+ G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
+ G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+ 0, NULL, &ioerror);
+ if (file_info == NULL)
+ {
+ set_vfs_error (error, ioerror);
+ g_clear_error (&ioerror);
+ }
+
+ if (*error == NULL) {
+ /* if available, copy data */
+ data->mtime = g_file_info_get_attribute_uint64 (file_info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ data->mime_type = g_strdup (g_file_info_get_content_type (file_info));
+
+ data->thumb_exists = (g_file_info_get_attribute_byte_string (file_info,
+ G_FILE_ATTRIBUTE_THUMBNAIL_PATH) != NULL);
+ data->failed_thumb_exists = g_file_info_get_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
+ data->can_read = TRUE;
+ if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
+ data->can_read = g_file_info_get_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
+ }
+ }
+ else {
+ eom_thumb_data_free (data);
+ data = NULL;
+ g_clear_error (&ioerror);
+ }
+
+ g_object_unref (file_info);
+
+ return data;
+}
+
+static void
+draw_frame_row (GdkPixbuf *frame_image,
+ gint target_width,
+ gint source_width,
+ gint source_v_position,
+ gint dest_v_position,
+ GdkPixbuf *result_pixbuf,
+ gint left_offset,
+ gint height)
+{
+ gint remaining_width, h_offset, slab_width;
+
+ remaining_width = target_width;
+ h_offset = 0;
+
+ while (remaining_width > 0) {
+ slab_width = remaining_width > source_width ?
+ source_width : remaining_width;
+
+ gdk_pixbuf_copy_area (frame_image,
+ left_offset,
+ source_v_position,
+ slab_width,
+ height,
+ result_pixbuf,
+ left_offset + h_offset,
+ dest_v_position);
+
+ remaining_width -= slab_width;
+ h_offset += slab_width;
+ }
+}
+
+static void
+draw_frame_column (GdkPixbuf *frame_image,
+ gint target_height,
+ gint source_height,
+ gint source_h_position,
+ gint dest_h_position,
+ GdkPixbuf *result_pixbuf,
+ gint top_offset,
+ gint width)
+{
+ gint remaining_height, v_offset, slab_height;
+
+ remaining_height = target_height;
+ v_offset = 0;
+
+ while (remaining_height > 0) {
+ slab_height = remaining_height > source_height ?
+ source_height : remaining_height;
+
+ gdk_pixbuf_copy_area (frame_image,
+ source_h_position,
+ top_offset,
+ width,
+ slab_height,
+ result_pixbuf,
+ dest_h_position,
+ top_offset + v_offset);
+
+ remaining_height -= slab_height;
+ v_offset += slab_height;
+ }
+}
+
+static GdkPixbuf *
+eom_thumbnail_stretch_frame_image (GdkPixbuf *frame_image,
+ gint left_offset,
+ gint top_offset,
+ gint right_offset,
+ gint bottom_offset,
+ gint dest_width,
+ gint dest_height,
+ gboolean fill_flag)
+{
+ GdkPixbuf *result_pixbuf;
+ gint frame_width, frame_height;
+ gint target_width, target_frame_width;
+ gint target_height, target_frame_height;
+
+ frame_width = gdk_pixbuf_get_width (frame_image);
+ frame_height = gdk_pixbuf_get_height (frame_image);
+
+ if (fill_flag) {
+ result_pixbuf = gdk_pixbuf_scale_simple (frame_image,
+ dest_width,
+ dest_height,
+ GDK_INTERP_NEAREST);
+ } else {
+ result_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ dest_width,
+ dest_height);
+
+ /* Clear pixbuf to fully opaque white */
+ gdk_pixbuf_fill (result_pixbuf, 0xffffffff);
+ }
+
+ target_width = dest_width - left_offset - right_offset;
+ target_frame_width = frame_width - left_offset - right_offset;
+
+ target_height = dest_height - top_offset - bottom_offset;
+ target_frame_height = frame_height - top_offset - bottom_offset;
+
+ /* Draw the left top corner and top row */
+ gdk_pixbuf_copy_area (frame_image,
+ 0, 0,
+ left_offset,
+ top_offset,
+ result_pixbuf,
+ 0, 0);
+
+ draw_frame_row (frame_image,
+ target_width,
+ target_frame_width,
+ 0, 0,
+ result_pixbuf,
+ left_offset,
+ top_offset);
+
+ /* Draw the right top corner and left column */
+ gdk_pixbuf_copy_area (frame_image,
+ frame_width - right_offset,
+ 0,
+ right_offset,
+ top_offset,
+ result_pixbuf,
+ dest_width - right_offset,
+ 0);
+
+ draw_frame_column (frame_image,
+ target_height,
+ target_frame_height,
+ 0, 0,
+ result_pixbuf,
+ top_offset,
+ left_offset);
+
+ /* Draw the bottom right corner and bottom row */
+ gdk_pixbuf_copy_area (frame_image,
+ frame_width - right_offset,
+ frame_height - bottom_offset,
+ right_offset,
+ bottom_offset,
+ result_pixbuf,
+ dest_width - right_offset,
+ dest_height - bottom_offset);
+
+ draw_frame_row (frame_image,
+ target_width,
+ target_frame_width,
+ frame_height - bottom_offset,
+ dest_height - bottom_offset,
+ result_pixbuf,
+ left_offset, bottom_offset);
+
+ /* Draw the bottom left corner and the right column */
+ gdk_pixbuf_copy_area (frame_image,
+ 0,
+ frame_height - bottom_offset,
+ left_offset,
+ bottom_offset,
+ result_pixbuf,
+ 0,
+ dest_height - bottom_offset);
+
+ draw_frame_column (frame_image,
+ target_height,
+ target_frame_height,
+ frame_width - right_offset,
+ dest_width - right_offset,
+ result_pixbuf, top_offset,
+ right_offset);
+
+ return result_pixbuf;
+}
+
+GdkPixbuf *
+eom_thumbnail_add_frame (GdkPixbuf *thumbnail)
+{
+ GdkPixbuf *result_pixbuf;
+ gint source_width, source_height;
+ gint dest_width, dest_height;
+
+ source_width = gdk_pixbuf_get_width (thumbnail);
+ source_height = gdk_pixbuf_get_height (thumbnail);
+
+ dest_width = source_width + 9;
+ dest_height = source_height + 9;
+
+ result_pixbuf = eom_thumbnail_stretch_frame_image (frame,
+ 3, 3, 6, 6,
+ dest_width,
+ dest_height,
+ FALSE);
+
+ gdk_pixbuf_copy_area (thumbnail,
+ 0, 0,
+ source_width,
+ source_height,
+ result_pixbuf,
+ 3, 3);
+
+ return result_pixbuf;
+}
+
+GdkPixbuf *
+eom_thumbnail_fit_to_size (GdkPixbuf *thumbnail, gint dimension)
+{
+ gint width, height;
+
+ width = gdk_pixbuf_get_width (thumbnail);
+ height = gdk_pixbuf_get_height (thumbnail);
+
+ if (width > dimension || height > dimension) {
+ GdkPixbuf *result_pixbuf;
+ gfloat factor;
+
+ if (width > height) {
+ factor = (gfloat) dimension / (gfloat) width;
+ } else {
+ factor = (gfloat) dimension / (gfloat) height;
+ }
+
+ width = MAX (width * factor, 1);
+ height = MAX (height * factor, 1);
+
+ result_pixbuf = mate_desktop_thumbnail_scale_down_pixbuf (thumbnail, width, height);
+
+ return result_pixbuf;
+ }
+ return gdk_pixbuf_copy (thumbnail);
+}
+
+GdkPixbuf*
+eom_thumbnail_load (EomImage *image, GError **error)
+{
+ GdkPixbuf *thumb = NULL;
+ GFile *file;
+ EomThumbData *data;
+ GdkPixbuf *pixbuf;
+
+ g_return_val_if_fail (image != NULL, NULL);
+ g_return_val_if_fail (error != NULL && *error == NULL, NULL);
+
+ file = eom_image_get_file (image);
+ data = eom_thumb_data_new (file, error);
+ g_object_unref (file);
+
+ if (data == NULL)
+ return NULL;
+
+ if (!data->can_read ||
+ (data->failed_thumb_exists && mate_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory, data->uri_str, data->mtime))) {
+ eom_debug_message (DEBUG_THUMBNAIL, "%s: bad permissions or valid failed thumbnail present",data->uri_str);
+ set_thumb_error (error, EOM_THUMB_ERROR_GENERIC, "Thumbnail creation failed");
+ return NULL;
+ }
+
+ /* check if there is already a valid cached thumbnail */
+ thumb = get_valid_thumbnail (data, error);
+
+ if (thumb != NULL) {
+ eom_debug_message (DEBUG_THUMBNAIL, "%s: loaded from cache",data->uri_str);
+ } else if (mate_desktop_thumbnail_factory_can_thumbnail (factory, data->uri_str, data->mime_type, data->mtime)) {
+ pixbuf = eom_image_get_pixbuf (image);
+
+ if (pixbuf != NULL) {
+ /* generate a thumbnail from the in-memory image,
+ if we have already loaded the image */
+ eom_debug_message (DEBUG_THUMBNAIL, "%s: creating from pixbuf",data->uri_str);
+ thumb = create_thumbnail_from_pixbuf (data, pixbuf, error);
+ g_object_unref (pixbuf);
+ } else {
+ /* generate a thumbnail from the file */
+ eom_debug_message (DEBUG_THUMBNAIL, "%s: creating from file",data->uri_str);
+ thumb = mate_desktop_thumbnail_factory_generate_thumbnail (factory, data->uri_str, data->mime_type);
+ }
+
+ if (thumb != NULL) {
+ /* Save the new thumbnail */
+ mate_desktop_thumbnail_factory_save_thumbnail (factory, thumb, data->uri_str, data->mtime);
+ eom_debug_message (DEBUG_THUMBNAIL, "%s: normal thumbnail saved",data->uri_str);
+ } else {
+ /* Save a failed thumbnail, to stop further thumbnail attempts */
+ mate_desktop_thumbnail_factory_create_failed_thumbnail (factory, data->uri_str, data->mtime);
+ eom_debug_message (DEBUG_THUMBNAIL, "%s: failed thumbnail saved",data->uri_str);
+ set_thumb_error (error, EOM_THUMB_ERROR_GENERIC, "Thumbnail creation failed");
+ }
+ }
+
+ eom_thumb_data_free (data);
+
+ return thumb;
+}
+
+void
+eom_thumbnail_init (void)
+{
+ if (factory == NULL) {
+ factory = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL);
+ }
+
+ if (frame == NULL) {
+ frame = gdk_pixbuf_new_from_file (EOM_DATA_DIR "/pixmaps/thumbnail-frame.png", NULL);
+ }
+}
diff --git a/src/eom-thumbnail.h b/src/eom-thumbnail.h
new file mode 100644
index 0000000..68b155f
--- /dev/null
+++ b/src/eom-thumbnail.h
@@ -0,0 +1,48 @@
+/* Eye Of Mate - Thumbnailing functions
+ *
+ * Copyright (C) 2000-2007 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on caja code (libcaja-private/caja-thumbnail.c) by:
+ * - Andy Hertzfeld <[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_THUMBNAIL_H_
+#define _EOM_THUMBNAIL_H_
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "eom-image.h"
+
+G_BEGIN_DECLS
+
+void eom_thumbnail_init (void);
+
+GdkPixbuf* eom_thumbnail_fit_to_size (GdkPixbuf *thumbnail,
+ gint dimension);
+
+GdkPixbuf* eom_thumbnail_add_frame (GdkPixbuf *thumbnail);
+
+GdkPixbuf* eom_thumbnail_load (EomImage *image,
+ GError **error);
+
+#define EOM_THUMBNAIL_ORIGINAL_WIDTH "eom-thumbnail-orig-width"
+#define EOM_THUMBNAIL_ORIGINAL_HEIGHT "eom-thumbnail-orig-height"
+
+G_END_DECLS
+
+#endif /* _EOM_THUMBNAIL_H_ */
diff --git a/src/eom-transform.c b/src/eom-transform.c
new file mode 100644
index 0000000..ab6935d
--- /dev/null
+++ b/src/eom-transform.c
@@ -0,0 +1,418 @@
+/* Eye Of MATE -- Affine Transformations
+ *
+ * Copyright (C) 2003-2009 The Free Software Foundation
+ *
+ * Portions based on code from libart_lgpl by Raph Levien.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h>
+#include <math.h>
+#include <gtk/gtk.h>
+#include <cairo/cairo.h>
+
+#include "eom-transform.h"
+#include "eom-jobs.h"
+
+/* The number of progress updates per transformation */
+#define EOM_TRANSFORM_N_PROG_UPDATES 20
+
+struct _EomTransformPrivate {
+ cairo_matrix_t affine;
+};
+
+typedef struct {
+ gdouble x;
+ gdouble y;
+} EomPoint;
+
+/* Convert degrees into radians */
+#define EOM_DEG_TO_RAD(degree) ((degree) * (G_PI/180.0))
+
+#define EOM_TRANSFORM_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_TRANSFORM, EomTransformPrivate))
+
+G_DEFINE_TYPE (EomTransform, eom_transform, G_TYPE_OBJECT)
+
+static void
+eom_transform_init (EomTransform *trans)
+{
+ trans->priv = EOM_TRANSFORM_GET_PRIVATE (trans);
+}
+
+static void
+eom_transform_class_init (EomTransformClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (EomTransformPrivate));
+}
+
+GdkPixbuf*
+eom_transform_apply (EomTransform *trans, GdkPixbuf *pixbuf, EomJob *job)
+{
+ EomPoint dest_top_left;
+ EomPoint dest_bottom_right;
+ EomPoint vertices[4] = { {0, 0}, {1, 0}, {1, 1}, {0, 1} };
+ double r_det;
+ int inverted [6];
+ EomPoint dest;
+
+ int src_width;
+ int src_height;
+ int src_rowstride;
+ int src_n_channels;
+ guchar *src_buffer;
+
+ GdkPixbuf *dest_pixbuf;
+ int dest_width;
+ int dest_height;
+ int dest_rowstride;
+ int dest_n_channels;
+ guchar *dest_buffer;
+
+ guchar *src_pos;
+ guchar *dest_pos;
+ int dx, dy, sx, sy;
+ int i, x, y;
+
+ int progress_delta;
+
+ g_return_val_if_fail (pixbuf != NULL, NULL);
+
+ g_object_ref (pixbuf);
+
+ src_width = gdk_pixbuf_get_width (pixbuf);
+ src_height = gdk_pixbuf_get_height (pixbuf);
+ src_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ src_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+ src_buffer = gdk_pixbuf_get_pixels (pixbuf);
+
+ /* find out the dimension of the destination pixbuf */
+ dest_top_left.x = 100000;
+ dest_top_left.y = 100000;
+ dest_bottom_right.x = -100000;
+ dest_bottom_right.y = -100000;
+
+ for (i = 0; i < 4; i++) {
+ dest.x = vertices[i].x * (src_width - 1);
+ dest.y = vertices[i].y * (src_height -1);
+
+ cairo_matrix_transform_point (&trans->priv->affine,
+ &dest.x, &dest.y);
+
+ dest_top_left.x = MIN (dest_top_left.x, dest.x);
+ dest_top_left.y = MIN (dest_top_left.y, dest.y);
+
+ dest_bottom_right.x = MAX (dest_bottom_right.x, dest.x);
+ dest_bottom_right.y = MAX (dest_bottom_right.y, dest.y);
+ }
+
+ /* create the resulting pixbuf */
+ dest_width = abs (dest_bottom_right.x - dest_top_left.x + 1);
+ dest_height = abs (dest_bottom_right.y - dest_top_left.y + 1);
+
+ dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ gdk_pixbuf_get_has_alpha (pixbuf),
+ gdk_pixbuf_get_bits_per_sample (pixbuf),
+ dest_width,
+ dest_height);
+ dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
+ dest_n_channels = gdk_pixbuf_get_n_channels (dest_pixbuf);
+ dest_buffer = gdk_pixbuf_get_pixels (dest_pixbuf);
+
+ /* invert the matrix so that we can compute the source pixel
+ from the target pixel and convert the values to integer
+ ones (faster!) FIXME: Maybe we can do some more
+ improvements by using special mmx/3dnow features if
+ available.
+ */
+ r_det = 1.0 / (trans->priv->affine.xx * trans->priv->affine.yy - trans->priv->affine.yx * trans->priv->affine.xy);
+ inverted[0] = trans->priv->affine.yy * r_det;
+ inverted[1] = -trans->priv->affine.yx * r_det;
+ inverted[2] = -trans->priv->affine.xy * r_det;
+ inverted[3] = trans->priv->affine.xx * r_det;
+ inverted[4] = -trans->priv->affine.x0 * inverted[0] - trans->priv->affine.y0 * inverted[2];
+ inverted[5] = -trans->priv->affine.x0 * inverted[1] - trans->priv->affine.y0 * inverted[3];
+
+ progress_delta = MAX (1, dest_height / EOM_TRANSFORM_N_PROG_UPDATES);
+
+ /*
+ * for every destination pixel (dx,dy) compute the source pixel (sx, sy)
+ * and copy the color values
+ */
+ for (y = 0, dy = dest_top_left.y; y < dest_height; y++, dy++) {
+ for (x = 0, dx = dest_top_left.x; x < dest_width; x++, dx++) {
+
+ sx = dx * inverted[0] + dy * inverted[2] + inverted[4];
+ sy = dx * inverted[1] + dy * inverted[3] + inverted[5];
+
+ if (sx >= 0 && sx < src_width && sy >= 0 && sy < src_height) {
+ src_pos = src_buffer + sy * src_rowstride + sx * src_n_channels;
+ dest_pos = dest_buffer + y * dest_rowstride + x * dest_n_channels;
+
+ for (i = 0; i < src_n_channels; i++) {
+ dest_pos[i] = src_pos[i];
+ }
+ }
+ }
+
+ if (job != NULL && y % progress_delta == 0) {
+ gfloat progress;
+
+ progress = (gfloat) (y + 1.0) / (gfloat) dest_height;
+
+ eom_job_set_progress (job, progress);
+ }
+ }
+
+ g_object_unref (pixbuf);
+
+ if (job != NULL) {
+ eom_job_set_progress (job, 1.0);
+ }
+
+ return dest_pixbuf;
+}
+
+static void
+_eom_cairo_matrix_copy (const cairo_matrix_t *src, cairo_matrix_t *dest)
+{
+ cairo_matrix_init (dest, src->xx, src->yx, src->xy, src->yy, src->x0, src->y0);
+}
+
+#define DOUBLE_EQUAL_MAX_DIFF 1e-6
+#define DOUBLE_EQUAL(a,b) (fabs (a - b) < DOUBLE_EQUAL_MAX_DIFF)
+/* art_affine_equal modified to work with cairo_matrix_t */
+static gboolean
+_eom_cairo_matrix_equal (const cairo_matrix_t *a, const cairo_matrix_t *b)
+{
+ return (DOUBLE_EQUAL (a->xx, b->xx) && DOUBLE_EQUAL (a->yx, b->yx) &&
+ DOUBLE_EQUAL (a->xy, b->xy) && DOUBLE_EQUAL (a->yy, b->yy) &&
+ DOUBLE_EQUAL (a->x0, b->x0) && DOUBLE_EQUAL (a->y0, b->y0) );
+}
+
+/* art_affine_flip modified to work with cairo_matrix_t */
+static void
+_eom_cairo_matrix_flip (cairo_matrix_t *dst, const cairo_matrix_t *src, gboolean horiz, gboolean vert)
+{
+ dst->xx = horiz ? -src->xx : src->xx;
+ dst->yx = horiz ? -src->yx : src->yx;
+ dst->xy = vert ? -src->xy : src->xy;
+ dst->yy = vert ? -src->yy : src->yy;
+ dst->x0 = horiz ? -src->x0 : src->x0;
+ dst->y0 = vert ? -src->y0 : src->y0;
+}
+
+EomTransform*
+eom_transform_reverse (EomTransform *trans)
+{
+ EomTransform *reverse;
+
+ g_return_val_if_fail (EOM_IS_TRANSFORM (trans), NULL);
+
+ reverse = EOM_TRANSFORM (g_object_new (EOM_TYPE_TRANSFORM, NULL));
+
+ _eom_cairo_matrix_copy (&trans->priv->affine, &reverse->priv->affine);
+
+ g_return_val_if_fail (cairo_matrix_invert (&reverse->priv->affine) == CAIRO_STATUS_SUCCESS, reverse);
+
+ return reverse;
+}
+
+EomTransform*
+eom_transform_compose (EomTransform *trans, EomTransform *compose)
+{
+ EomTransform *composition;
+
+ g_return_val_if_fail (EOM_IS_TRANSFORM (trans), NULL);
+ g_return_val_if_fail (EOM_IS_TRANSFORM (compose), NULL);
+
+ composition = EOM_TRANSFORM (g_object_new (EOM_TYPE_TRANSFORM, NULL));
+
+ cairo_matrix_multiply (&composition->priv->affine,
+ &trans->priv->affine,
+ &compose->priv->affine);
+
+ return composition;
+}
+
+gboolean
+eom_transform_is_identity (EomTransform *trans)
+{
+ static const cairo_matrix_t identity = { 1, 0, 0, 1, 0, 0 };
+
+ g_return_val_if_fail (EOM_IS_TRANSFORM (trans), FALSE);
+
+ return _eom_cairo_matrix_equal (&identity, &trans->priv->affine);
+}
+
+EomTransform*
+eom_transform_identity_new (void)
+{
+ EomTransform *trans;
+
+ trans = EOM_TRANSFORM (g_object_new (EOM_TYPE_TRANSFORM, NULL));
+
+ cairo_matrix_init_identity (&trans->priv->affine);
+
+ return trans;
+}
+
+EomTransform*
+eom_transform_rotate_new (int degree)
+{
+ EomTransform *trans;
+
+ trans = EOM_TRANSFORM (g_object_new (EOM_TYPE_TRANSFORM, NULL));
+
+ cairo_matrix_init_rotate (&trans->priv->affine, EOM_DEG_TO_RAD(degree));
+
+ return trans;
+}
+
+EomTransform*
+eom_transform_flip_new (EomTransformType type)
+{
+ EomTransform *trans;
+ gboolean horiz, vert;
+
+ trans = EOM_TRANSFORM (g_object_new (EOM_TYPE_TRANSFORM, NULL));
+
+ cairo_matrix_init_identity (&trans->priv->affine);
+
+ horiz = (type == EOM_TRANSFORM_FLIP_HORIZONTAL);
+ vert = (type == EOM_TRANSFORM_FLIP_VERTICAL);
+
+ _eom_cairo_matrix_flip (&trans->priv->affine,
+ &trans->priv->affine,
+ horiz, vert);
+
+ return trans;
+}
+
+EomTransform*
+eom_transform_new (EomTransformType type)
+{
+ EomTransform *trans = NULL;
+ EomTransform *temp1 = NULL, *temp2 = NULL;
+
+ switch (type) {
+ case EOM_TRANSFORM_NONE:
+ trans = eom_transform_identity_new ();
+ break;
+ case EOM_TRANSFORM_FLIP_HORIZONTAL:
+ trans = eom_transform_flip_new (EOM_TRANSFORM_FLIP_HORIZONTAL);
+ break;
+ case EOM_TRANSFORM_ROT_180:
+ trans = eom_transform_rotate_new (180);
+ break;
+ case EOM_TRANSFORM_FLIP_VERTICAL:
+ trans = eom_transform_flip_new (EOM_TRANSFORM_FLIP_VERTICAL);
+ break;
+ case EOM_TRANSFORM_TRANSPOSE:
+ temp1 = eom_transform_rotate_new (90);
+ temp2 = eom_transform_flip_new (EOM_TRANSFORM_FLIP_HORIZONTAL);
+ trans = eom_transform_compose (temp1, temp2);
+ g_object_unref (temp1);
+ g_object_unref (temp2);
+ break;
+ case EOM_TRANSFORM_ROT_90:
+ trans = eom_transform_rotate_new (90);
+ break;
+ case EOM_TRANSFORM_TRANSVERSE:
+ temp1 = eom_transform_rotate_new (90);
+ temp2 = eom_transform_flip_new (EOM_TRANSFORM_FLIP_VERTICAL);
+ trans = eom_transform_compose (temp1, temp2);
+ g_object_unref (temp1);
+ g_object_unref (temp2);
+ break;
+ case EOM_TRANSFORM_ROT_270:
+ trans = eom_transform_rotate_new (270);
+ break;
+ default:
+ trans = eom_transform_identity_new ();
+ break;
+ }
+
+ return trans;
+}
+
+EomTransformType
+eom_transform_get_transform_type (EomTransform *trans)
+{
+ cairo_matrix_t affine;
+ EomTransformPrivate *priv;
+
+ g_return_val_if_fail (EOM_IS_TRANSFORM (trans), EOM_TRANSFORM_NONE);
+
+ priv = trans->priv;
+
+ cairo_matrix_init_rotate (&affine, EOM_DEG_TO_RAD(90));
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_ROT_90;
+ }
+
+ cairo_matrix_init_rotate (&affine, EOM_DEG_TO_RAD(180));
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_ROT_180;
+ }
+
+ cairo_matrix_init_rotate (&affine, EOM_DEG_TO_RAD(270));
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_ROT_270;
+ }
+
+ cairo_matrix_init_identity (&affine);
+ _eom_cairo_matrix_flip (&affine, &affine, TRUE, FALSE);
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_FLIP_HORIZONTAL;
+ }
+
+ cairo_matrix_init_identity (&affine);
+ _eom_cairo_matrix_flip (&affine, &affine, FALSE, TRUE);
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_FLIP_VERTICAL;
+ }
+
+ cairo_matrix_init_rotate (&affine, EOM_DEG_TO_RAD(90));
+ _eom_cairo_matrix_flip (&affine, &affine, TRUE, FALSE);
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_TRANSPOSE;
+ }
+
+ cairo_matrix_init_rotate (&affine, EOM_DEG_TO_RAD(90));
+ _eom_cairo_matrix_flip (&affine, &affine, FALSE, TRUE);
+ if (_eom_cairo_matrix_equal (&affine, &priv->affine)) {
+ return EOM_TRANSFORM_TRANSVERSE;
+ }
+
+ return EOM_TRANSFORM_NONE;
+}
+
+gboolean
+eom_transform_get_affine (EomTransform *trans, cairo_matrix_t *affine)
+{
+ g_return_val_if_fail (EOM_IS_TRANSFORM (trans), FALSE);
+
+ _eom_cairo_matrix_copy (&trans->priv->affine, affine);
+
+ return TRUE;
+}
+
diff --git a/src/eom-transform.h b/src/eom-transform.h
new file mode 100644
index 0000000..a321e03
--- /dev/null
+++ b/src/eom-transform.h
@@ -0,0 +1,75 @@
+#ifndef _EOM_TRANSFORM_H_
+#define _EOM_TRANSFORM_H_
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#ifndef __EOM_JOB_DECLR__
+#define __EOM_JOB_DECLR__
+typedef struct _EomJob EomJob;
+#endif
+
+typedef enum {
+ EOM_TRANSFORM_NONE,
+ EOM_TRANSFORM_ROT_90,
+ EOM_TRANSFORM_ROT_180,
+ EOM_TRANSFORM_ROT_270,
+ EOM_TRANSFORM_FLIP_HORIZONTAL,
+ EOM_TRANSFORM_FLIP_VERTICAL,
+ EOM_TRANSFORM_TRANSPOSE,
+ EOM_TRANSFORM_TRANSVERSE
+} EomTransformType;
+
+#define EOM_TYPE_TRANSFORM (eom_transform_get_type ())
+#define EOM_TRANSFORM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EOM_TYPE_TRANSFORM, EomTransform))
+#define EOM_TRANSFORM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EOM_TYPE_TRANSFORM, EomTransformClass))
+#define EOM_IS_TRANSFORM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_TRANSFORM))
+#define EOM_IS_TRANSFORM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EOM_TYPE_TRANSFORM))
+#define EOM_TRANSFORM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EOM_TYPE_TRANSFORM, EomTransformClass))
+
+/* =========================================
+
+ GObjecat wrapper around an affine transformation
+
+ ----------------------------------------*/
+
+typedef struct _EomTransform EomTransform;
+typedef struct _EomTransformClass EomTransformClass;
+typedef struct _EomTransformPrivate EomTransformPrivate;
+
+struct _EomTransform {
+ GObject parent;
+
+ EomTransformPrivate *priv;
+};
+
+struct _EomTransformClass {
+ GObjectClass parent_klass;
+};
+
+GType eom_transform_get_type (void) G_GNUC_CONST;
+
+GdkPixbuf* eom_transform_apply (EomTransform *trans, GdkPixbuf *pixbuf, EomJob *job);
+EomTransform* eom_transform_reverse (EomTransform *trans);
+EomTransform* eom_transform_compose (EomTransform *trans, EomTransform *compose);
+gboolean eom_transform_is_identity (EomTransform *trans);
+
+EomTransform* eom_transform_identity_new (void);
+EomTransform* eom_transform_rotate_new (int degree);
+EomTransform* eom_transform_flip_new (EomTransformType type /* only EOM_TRANSFORM_FLIP_* are valid */);
+#if 0
+EomTransform* eom_transform_scale_new (double sx, double sy);
+#endif
+EomTransform* eom_transform_new (EomTransformType trans);
+
+EomTransformType eom_transform_get_transform_type (EomTransform *trans);
+
+gboolean eom_transform_get_affine (EomTransform *trans, cairo_matrix_t *affine);
+
+G_END_DECLS
+
+#endif /* _EOM_TRANSFORM_H_ */
+
+
diff --git a/src/eom-uri-converter.c b/src/eom-uri-converter.c
new file mode 100644
index 0000000..738820c
--- /dev/null
+++ b/src/eom-uri-converter.c
@@ -0,0 +1,988 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+
+#include "eom-uri-converter.h"
+#include "eom-pixbuf-util.h"
+
+enum {
+ PROP_0,
+ PROP_CONVERT_SPACES,
+ PROP_SPACE_CHARACTER,
+ PROP_COUNTER_START,
+ PROP_COUNTER_N_DIGITS,
+ PROP_N_IMAGES
+};
+
+typedef struct {
+ EomUCType type;
+ union {
+ char *string; /* if type == EOM_UC_STRING */
+ gulong counter; /* if type == EOM_UC_COUNTER */
+ } data;
+} EomUCToken;
+
+
+struct _EomURIConverterPrivate {
+ GFile *base_file;
+ GList *token_list;
+ char *suffix;
+ GdkPixbufFormat *img_format;
+ gboolean requires_exif;
+
+ /* options */
+ gboolean convert_spaces;
+ gchar space_character;
+ gulong counter_start;
+ guint counter_n_digits;
+};
+
+static void eom_uri_converter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void eom_uri_converter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+#define EOM_URI_CONVERTER_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_URI_CONVERTER, EomURIConverterPrivate))
+
+G_DEFINE_TYPE (EomURIConverter, eom_uri_converter, G_TYPE_OBJECT)
+
+static void
+free_token (gpointer data)
+{
+ EomUCToken *token = (EomUCToken*) data;
+
+ if (token->type == EOM_UC_STRING) {
+ g_free (token->data.string);
+ }
+
+ g_slice_free (EomUCToken, token);
+}
+
+static void
+eom_uri_converter_dispose (GObject *object)
+{
+ EomURIConverter *instance = EOM_URI_CONVERTER (object);
+ EomURIConverterPrivate *priv;
+
+ priv = instance->priv;
+
+ if (priv->base_file) {
+ g_object_unref (priv->base_file);
+ priv->base_file = NULL;
+ }
+
+ if (priv->token_list) {
+ g_list_foreach (priv->token_list, (GFunc) free_token, NULL);
+ g_list_free (priv->token_list);
+ priv->token_list = NULL;
+ }
+
+ if (priv->suffix) {
+ g_free (priv->suffix);
+ priv->suffix = NULL;
+ }
+
+
+ G_OBJECT_CLASS (eom_uri_converter_parent_class)->dispose (object);
+}
+
+static void
+eom_uri_converter_init (EomURIConverter *obj)
+{
+ EomURIConverterPrivate *priv;
+
+ priv = obj->priv = EOM_URI_CONVERTER_GET_PRIVATE (obj);
+
+ priv->convert_spaces = FALSE;
+ priv->space_character = '_';
+ priv->counter_start = 0;
+ priv->counter_n_digits = 1;
+ priv->requires_exif = FALSE;
+}
+
+static void
+eom_uri_converter_class_init (EomURIConverterClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass*) klass;
+
+ object_class->dispose = eom_uri_converter_dispose;
+
+ /* GObjectClass */
+ object_class->set_property = eom_uri_converter_set_property;
+ object_class->get_property = eom_uri_converter_get_property;
+
+ /* Properties */
+ g_object_class_install_property (
+ object_class,
+ PROP_CONVERT_SPACES,
+ g_param_spec_boolean ("convert-spaces", NULL, NULL,
+ FALSE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SPACE_CHARACTER,
+ g_param_spec_char ("space-character", NULL, NULL,
+ ' ', '~', '_', G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_COUNTER_START,
+ g_param_spec_ulong ("counter-start", NULL, NULL,
+ 0,
+ G_MAXULONG,
+ 1,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_COUNTER_N_DIGITS,
+ g_param_spec_uint ("counter-n-digits", NULL, NULL,
+ 1,
+ G_MAXUINT,
+ 1,
+ G_PARAM_READWRITE));
+
+
+ g_object_class_install_property (
+ object_class,
+ PROP_N_IMAGES,
+ g_param_spec_uint ("n-images", NULL, NULL,
+ 1,
+ G_MAXUINT,
+ 1,
+ G_PARAM_WRITABLE));
+
+ g_type_class_add_private (klass, sizeof (EomURIConverterPrivate));
+}
+
+GQuark
+eom_uc_error_quark (void)
+{
+ static GQuark q = 0;
+ if (q == 0)
+ q = g_quark_from_static_string ("eom-uri-converter-error-quark");
+
+ return q;
+}
+
+
+static void
+eom_uri_converter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomURIConverter *conv;
+ EomURIConverterPrivate *priv;
+
+ g_return_if_fail (EOM_IS_URI_CONVERTER (object));
+
+ conv = EOM_URI_CONVERTER (object);
+ priv = conv->priv;
+
+ switch (property_id)
+ {
+ case PROP_CONVERT_SPACES:
+ priv->convert_spaces = g_value_get_boolean (value);
+ break;
+
+ case PROP_SPACE_CHARACTER:
+ priv->space_character = g_value_get_char (value);
+ break;
+
+ case PROP_COUNTER_START:
+ {
+ guint new_n_digits;
+
+ priv->counter_start = g_value_get_ulong (value);
+
+ new_n_digits = ceil (log10 (priv->counter_start + pow (10, priv->counter_n_digits) - 1));
+
+ if (new_n_digits != priv->counter_n_digits) {
+ priv->counter_n_digits = ceil (MIN (log10 (G_MAXULONG), new_n_digits));
+ }
+ break;
+ }
+
+ case PROP_COUNTER_N_DIGITS:
+ priv->counter_n_digits = ceil (MIN (log10 (G_MAXULONG), g_value_get_uint (value)));
+ break;
+
+ case PROP_N_IMAGES:
+ priv->counter_n_digits = ceil (MIN (log10 (G_MAXULONG),
+ log10 (priv->counter_start + g_value_get_uint (value))));
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+eom_uri_converter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomURIConverter *conv;
+ EomURIConverterPrivate *priv;
+
+ g_return_if_fail (EOM_IS_URI_CONVERTER (object));
+
+ conv = EOM_URI_CONVERTER (object);
+ priv = conv->priv;
+
+ switch (property_id)
+ {
+ case PROP_CONVERT_SPACES:
+ g_value_set_boolean (value, priv->convert_spaces);
+ break;
+
+ case PROP_SPACE_CHARACTER:
+ g_value_set_char (value, priv->space_character);
+ break;
+
+ case PROP_COUNTER_START:
+ g_value_set_ulong (value, priv->counter_start);
+ break;
+
+ case PROP_COUNTER_N_DIGITS:
+ g_value_set_uint (value, priv->counter_n_digits);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/* parser states */
+enum {
+ PARSER_NONE,
+ PARSER_STRING,
+ PARSER_TOKEN
+};
+
+static EomUCToken*
+create_token_string (const char *string, int substr_start, int substr_len)
+{
+ char *start_byte;
+ char *end_byte;
+ int n_bytes;
+ EomUCToken *token;
+
+ if (string == NULL) return NULL;
+ if (substr_len <= 0) return NULL;
+
+ start_byte = g_utf8_offset_to_pointer (string, substr_start);
+ end_byte = g_utf8_offset_to_pointer (string, substr_start + substr_len);
+
+ /* FIXME: is this right? */
+ n_bytes = end_byte - start_byte;
+
+ token = g_slice_new0 (EomUCToken);
+ token->type = EOM_UC_STRING;
+ token->data.string = g_new0 (char, n_bytes);
+ token->data.string = g_utf8_strncpy (token->data.string, start_byte, substr_len);
+
+ return token;
+}
+
+static EomUCToken*
+create_token_counter (int start_counter)
+{
+ EomUCToken *token;
+
+ token = g_slice_new0 (EomUCToken);
+ token->type = EOM_UC_COUNTER;
+ token->data.counter = 0;
+
+ return token;
+}
+
+static EomUCToken*
+create_token_other (EomUCType type)
+{
+ EomUCToken *token;
+
+ token = g_slice_new0 (EomUCToken);
+ token->type = type;
+
+ return token;
+}
+
+static GList*
+eom_uri_converter_parse_string (EomURIConverter *conv, const char *string)
+{
+ EomURIConverterPrivate *priv;
+ GList *list = NULL;
+ gulong len;
+ int i;
+ int state = PARSER_NONE;
+ int start = -1;
+ int substr_len = 0;
+ gunichar c;
+ const char *s;
+ EomUCToken *token;
+
+ g_return_val_if_fail (EOM_IS_URI_CONVERTER (conv), NULL);
+
+ priv = conv->priv;
+
+ if (string == NULL) return NULL;
+
+ if (!g_utf8_validate (string, -1, NULL))
+ return NULL;
+
+ len = g_utf8_strlen (string, -1);
+ s = string;
+
+ for (i = 0; i < len; i++) {
+ c = g_utf8_get_char (s);
+ token = NULL;
+
+ switch (state) {
+ case PARSER_NONE:
+ if (c == '%') {
+ start = -1;
+ state = PARSER_TOKEN;
+ } else {
+ start = i;
+ substr_len = 1;
+ state = PARSER_STRING;
+ }
+ break;
+
+ case PARSER_STRING:
+ if (c == '%') {
+ if (start != -1) {
+ token = create_token_string (string, start, substr_len);
+ }
+
+ state = PARSER_TOKEN;
+ start = -1;
+ } else {
+ substr_len++;
+ }
+ break;
+
+ case PARSER_TOKEN: {
+ EomUCType type = EOM_UC_END;
+
+ if (c == 'f') {
+ type = EOM_UC_FILENAME;
+ }
+ else if (c == 'n') {
+ type = EOM_UC_COUNTER;
+ token = create_token_counter (priv->counter_start);
+ }
+ else if (c == 'c') {
+ type = EOM_UC_COMMENT;
+ }
+ else if (c == 'd') {
+ type = EOM_UC_DATE;
+ }
+ else if (c == 't') {
+ type = EOM_UC_TIME;
+ }
+ else if (c == 'a') {
+ type = EOM_UC_DAY;
+ }
+ else if (c == 'm') {
+ type = EOM_UC_MONTH;
+ }
+ else if (c == 'y') {
+ type = EOM_UC_YEAR;
+ }
+ else if (c == 'h') {
+ type = EOM_UC_HOUR;
+ }
+ else if (c == 'i') {
+ type = EOM_UC_MINUTE;
+ }
+ else if (c == 's') {
+ type = EOM_UC_SECOND;
+ }
+
+ if (type != EOM_UC_END && token == NULL) {
+ token = create_token_other (type);
+ priv->requires_exif = TRUE;
+ }
+ state = PARSER_NONE;
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+
+
+ if (token != NULL) {
+ list = g_list_append (list, token);
+ }
+
+ s = g_utf8_next_char (s);
+ }
+
+ if (state != PARSER_TOKEN && start >= 0) {
+ /* add remaining chars as string token */
+ list = g_list_append (list, create_token_string (string, start, substr_len));
+ }
+
+ return list;
+}
+
+void
+eom_uri_converter_print_list (EomURIConverter *conv)
+{
+ EomURIConverterPrivate *priv;
+ GList *it;
+
+ g_return_if_fail (EOM_URI_CONVERTER (conv));
+
+ priv = conv->priv;
+
+ for (it = priv->token_list; it != NULL; it = it->next) {
+ EomUCToken *token;
+ char *str;
+
+ token = (EomUCToken*) it->data;
+
+ switch (token->type) {
+ case EOM_UC_STRING:
+ str = g_strdup_printf ("string [%s]", token->data.string);
+ break;
+ case EOM_UC_FILENAME:
+ str = "filename";
+ break;
+ case EOM_UC_COUNTER:
+ str = g_strdup_printf ("counter [%lu]", token->data.counter);
+ break;
+ case EOM_UC_COMMENT:
+ str = "comment";
+ break;
+ case EOM_UC_DATE:
+ str = "date";
+ break;
+ case EOM_UC_TIME:
+ str = "time";
+ break;
+ case EOM_UC_DAY:
+ str = "day";
+ break;
+ case EOM_UC_MONTH:
+ str = "month";
+ break;
+ case EOM_UC_YEAR:
+ str = "year";
+ break;
+ case EOM_UC_HOUR:
+ str = "hour";
+ break;
+ case EOM_UC_MINUTE:
+ str = "minute";
+ break;
+ case EOM_UC_SECOND:
+ str = "second";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+
+ g_print ("- %s\n", str);
+
+ if (token->type == EOM_UC_STRING || token->type == EOM_UC_COUNTER) {
+ g_free (str);
+ }
+ }
+}
+
+
+EomURIConverter*
+eom_uri_converter_new (GFile *base_file, GdkPixbufFormat *img_format, const char *format_str)
+{
+ EomURIConverter *conv;
+
+ g_return_val_if_fail (format_str != NULL, NULL);
+
+ conv = g_object_new (EOM_TYPE_URI_CONVERTER, NULL);
+
+ if (base_file != NULL) {
+ conv->priv->base_file = g_object_ref (base_file);
+ }
+ else {
+ conv->priv->base_file = NULL;
+ }
+ conv->priv->img_format = img_format;
+ conv->priv->token_list = eom_uri_converter_parse_string (conv, format_str);
+
+ return conv;
+}
+
+static GFile*
+get_file_directory (EomURIConverter *conv, EomImage *image)
+{
+ GFile *file = NULL;
+ EomURIConverterPrivate *priv;
+
+ g_return_val_if_fail (EOM_IS_URI_CONVERTER (conv), NULL);
+ g_return_val_if_fail (EOM_IS_IMAGE (image), NULL);
+
+ priv = conv->priv;
+
+ if (priv->base_file != NULL) {
+ file = g_object_ref (priv->base_file);
+ }
+ else {
+ GFile *img_file;
+
+ img_file = eom_image_get_file (image);
+ g_assert (img_file != NULL);
+
+ file = g_file_get_parent (img_file);
+
+ g_object_unref (img_file);
+ }
+
+ return file;
+}
+
+static void
+split_filename (GFile *file, char **name, char **suffix)
+{
+ char *basename;
+ char *suffix_start;
+ guint len;
+
+ *name = NULL;
+ *suffix = NULL;
+
+ /* get unescaped string */
+ basename = g_file_get_basename (file);
+
+ /* FIXME: does this work for all locales? */
+ suffix_start = g_utf8_strrchr (basename, -1, '.');
+
+ if (suffix_start == NULL) { /* no suffix found */
+ *name = g_strdup (basename);
+ }
+ else {
+ len = (suffix_start - basename);
+ *name = g_strndup (basename, len);
+
+ len = strlen (basename) - len - 1;
+ *suffix = g_strndup (suffix_start+1, len);
+ }
+
+ g_free (basename);
+}
+
+static GString*
+append_filename (GString *str, EomImage *img)
+{
+ /* appends the name of the original file without
+ filetype suffix */
+ GFile *img_file;
+ char *name;
+ char *suffix;
+ GString *result;
+
+ img_file = eom_image_get_file (img);
+ split_filename (img_file, &name, &suffix);
+
+ result = g_string_append (str, name);
+
+ g_free (name);
+ g_free (suffix);
+
+ g_object_unref (img_file);
+
+ return result;
+}
+
+static GString*
+append_counter (GString *str, gulong counter, EomURIConverter *conv)
+{
+ EomURIConverterPrivate *priv;
+
+ priv = conv->priv;
+
+ g_string_append_printf (str, "%.*lu", priv->counter_n_digits, counter);
+
+ return str;
+}
+
+
+static void
+build_absolute_file (EomURIConverter *conv, EomImage *image, GString *str, /* input */
+ GFile **file, GdkPixbufFormat **format) /* output */
+{
+ GFile *dir_file;
+ EomURIConverterPrivate *priv;
+
+ *file = NULL;
+ if (format != NULL)
+ *format = NULL;
+
+ g_return_if_fail (EOM_IS_URI_CONVERTER (conv));
+ g_return_if_fail (EOM_IS_IMAGE (image));
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (str != NULL);
+
+ priv = conv->priv;
+
+ dir_file = get_file_directory (conv, image);
+ g_assert (dir_file != NULL);
+
+ if (priv->img_format == NULL) {
+ /* use same file type/suffix */
+ char *name;
+ char *old_suffix;
+ GFile *img_file;
+
+ img_file = eom_image_get_file (image);
+ split_filename (img_file, &name, &old_suffix);
+
+ g_assert (old_suffix != NULL);
+
+ g_string_append_unichar (str, '.');
+ g_string_append (str, old_suffix);
+
+ if (format != NULL)
+ *format = eom_pixbuf_get_format_by_suffix (old_suffix);
+
+ g_object_unref (img_file);
+ } else {
+ if (priv->suffix == NULL)
+ priv->suffix = eom_pixbuf_get_common_suffix (priv->img_format);
+
+ g_string_append_unichar (str, '.');
+ g_string_append (str, priv->suffix);
+
+ if (format != NULL)
+ *format = priv->img_format;
+ }
+
+ *file = g_file_get_child (dir_file, str->str);
+
+ g_object_unref (dir_file);
+}
+
+
+static GString*
+replace_remove_chars (GString *str, gboolean convert_spaces, gunichar space_char)
+{
+ GString *result;
+ guint len;
+ char *s;
+ int i;
+ gunichar c;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ if (!g_utf8_validate (str->str, -1, NULL))
+ return NULL;
+
+ result = g_string_new (NULL);
+
+ len = g_utf8_strlen (str->str, -1);
+ s = str->str;
+
+ for (i = 0; i < len; i++, s = g_utf8_next_char (s)) {
+ c = g_utf8_get_char (s);
+
+ if (c == '/') {
+ continue;
+ }
+ else if (g_unichar_isspace (c) && convert_spaces) {
+ result = g_string_append_unichar (result, space_char);
+ }
+ else {
+ result = g_string_append_unichar (result, c);
+ }
+ }
+
+ /* ensure maximum length of 250 characters */
+ len = MIN (result->len, 250);
+ result = g_string_truncate (result, len);
+
+ return result;
+}
+
+/*
+ * This function converts the uri of the EomImage object, according to the
+ * EomUCToken list. The absolute uri (converted filename appended to base uri)
+ * is returned in uri and the image format will be in the format pointer.
+ */
+gboolean
+eom_uri_converter_do (EomURIConverter *conv, EomImage *image,
+ GFile **file, GdkPixbufFormat **format, GError **error)
+{
+ EomURIConverterPrivate *priv;
+ GList *it;
+ GString *str;
+ GString *repl_str;
+
+ g_return_val_if_fail (EOM_IS_URI_CONVERTER (conv), FALSE);
+
+ priv = conv->priv;
+
+ *file = NULL;
+ if (format != NULL)
+ *format = NULL;
+
+ str = g_string_new ("");
+
+ for (it = priv->token_list; it != NULL; it = it->next) {
+ EomUCToken *token = (EomUCToken*) it->data;
+
+ switch (token->type) {
+ case EOM_UC_STRING:
+ str = g_string_append (str, token->data.string);
+ break;
+
+ case EOM_UC_FILENAME:
+ str = append_filename (str, image);
+ break;
+
+ case EOM_UC_COUNTER: {
+ if (token->data.counter < priv->counter_start)
+ token->data.counter = priv->counter_start;
+
+ str = append_counter (str, token->data.counter++, conv);
+ break;
+ }
+#if 0
+ case EOM_UC_COMMENT:
+ str = g_string_append_printf ();
+ str = "comment";
+ break;
+ case EOM_UC_DATE:
+ str = "date";
+ break;
+ case EOM_UC_TIME:
+ str = "time";
+ break;
+ case EOM_UC_DAY:
+ str = "day";
+ break;
+ case EOM_UC_MONTH:
+ str = "month";
+ break;
+ case EOM_UC_YEAR:
+ str = "year";
+ break;
+ case EOM_UC_HOUR:
+ str = "hour";
+ break;
+ case EOM_UC_MINUTE:
+ str = "minute";
+ break;
+ case EOM_UC_SECOND:
+ str = "second";
+ break;
+#endif
+ default:
+ /* skip all others */
+
+ break;
+ }
+ }
+
+ repl_str = replace_remove_chars (str, priv->convert_spaces, priv->space_character);
+
+ if (repl_str->len > 0) {
+ build_absolute_file (conv, image, repl_str, file, format);
+ }
+
+ g_string_free (repl_str, TRUE);
+ g_string_free (str, TRUE);
+
+
+ return (*file != NULL);
+}
+
+
+char*
+eom_uri_converter_preview (const char *format_str, EomImage *img, GdkPixbufFormat *format,
+ gulong counter, guint n_images,
+ gboolean convert_spaces, gunichar space_char)
+{
+ GString *str;
+ GString *repl_str;
+ guint n_digits;
+ guint len;
+ int i;
+ const char *s;
+ gunichar c;
+ char *filename;
+ gboolean token_next;
+
+ g_return_val_if_fail (format_str != NULL, NULL);
+ g_return_val_if_fail (EOM_IS_IMAGE (img), NULL);
+
+ if (n_images == 0) return NULL;
+
+ n_digits = ceil (MIN (log10 (G_MAXULONG), MAX (log10 (counter), log10 (n_images))));
+
+ str = g_string_new ("");
+
+ if (!g_utf8_validate (format_str, -1, NULL))
+ return NULL;
+
+ len = g_utf8_strlen (format_str, -1);
+ s = format_str;
+ token_next = FALSE;
+
+ for (i = 0; i < len; i++, s = g_utf8_next_char (s)) {
+ c = g_utf8_get_char (s);
+
+ if (token_next) {
+ if (c == 'f') {
+ str = append_filename (str, img);
+ }
+ else if (c == 'n') {
+ g_string_append_printf (str, "%.*lu",
+ n_digits ,counter);
+
+ }
+#if 0 /* ignore the rest for now */
+ else if (c == 'c') {
+ type = EOM_UC_COMMENT;
+ }
+ else if (c == 'd') {
+ type = EOM_UC_DATE;
+ }
+ else if (c == 't') {
+ type = EOM_UC_TIME;
+ }
+ else if (c == 'a') {
+ type = EOM_UC_DAY;
+ }
+ else if (c == 'm') {
+ type = EOM_UC_MONTH;
+ }
+ else if (c == 'y') {
+ type = EOM_UC_YEAR;
+ }
+ else if (c == 'h') {
+ type = EOM_UC_HOUR;
+ }
+ else if (c == 'i') {
+ type = EOM_UC_MINUTE;
+ }
+ else if (c == 's') {
+ type = EOM_UC_SECOND;
+ }
+#endif
+ token_next = FALSE;
+ }
+ else if (c == '%') {
+ token_next = TRUE;
+ }
+ else {
+ str = g_string_append_unichar (str, c);
+ }
+ }
+
+
+ filename = NULL;
+ repl_str = replace_remove_chars (str, convert_spaces, space_char);
+
+ if (repl_str->len > 0) {
+ if (format == NULL) {
+ /* use same file type/suffix */
+ char *name;
+ char *old_suffix;
+ GFile *img_file;
+
+ img_file = eom_image_get_file (img);
+ split_filename (img_file, &name, &old_suffix);
+
+ g_assert (old_suffix != NULL);
+
+ g_string_append_unichar (repl_str, '.');
+ g_string_append (repl_str, old_suffix);
+
+ g_free (old_suffix);
+ g_free (name);
+ g_object_unref (img_file);
+ }
+ else {
+ char *suffix = eom_pixbuf_get_common_suffix (format);
+
+ g_string_append_unichar (repl_str, '.');
+ g_string_append (repl_str, suffix);
+
+ g_free (suffix);
+ }
+
+ filename = repl_str->str;
+ }
+
+ g_string_free (repl_str, FALSE);
+ g_string_free (str, TRUE);
+
+ return filename;
+}
+
+gboolean
+eom_uri_converter_requires_exif (EomURIConverter *converter)
+{
+ g_return_val_if_fail (EOM_IS_URI_CONVERTER (converter), FALSE);
+
+ return converter->priv->requires_exif;
+}
+
+gboolean
+eom_uri_converter_check (EomURIConverter *converter, GList *img_list, GError **error)
+{
+ GList *it;
+ GList *file_list = NULL;
+ gboolean all_different = TRUE;
+
+ g_return_val_if_fail (EOM_IS_URI_CONVERTER (converter), FALSE);
+
+ /* convert all image uris */
+ for (it = img_list; it != NULL; it = it->next) {
+ gboolean result;
+ GFile *file;
+ GError *conv_error = NULL;
+
+ result = eom_uri_converter_do (converter, EOM_IMAGE (it->data),
+ &file, NULL, &conv_error);
+
+ if (result) {
+ file_list = g_list_prepend (file_list, file);
+ }
+ }
+
+ /* check for all different uris */
+ for (it = file_list; it != NULL && all_different; it = it->next) {
+ GList *p;
+ GFile *file;
+
+ file = (GFile*) it->data;
+
+ for (p = it->next; p != NULL && all_different; p = p->next) {
+ all_different = !g_file_equal (file, (GFile*) p->data);
+ }
+ }
+
+ if (!all_different) {
+ g_set_error (error, EOM_UC_ERROR,
+ EOM_UC_ERROR_EQUAL_FILENAMES,
+ _("At least two file names are equal."));
+ }
+
+ return all_different;
+}
diff --git a/src/eom-uri-converter.h b/src/eom-uri-converter.h
new file mode 100644
index 0000000..b1cbe94
--- /dev/null
+++ b/src/eom-uri-converter.h
@@ -0,0 +1,107 @@
+#ifndef _EOM_URI_CONVERTER_H_
+#define _EOM_URI_CONVERTER_H_
+
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include "eom-image.h"
+
+G_BEGIN_DECLS
+
+#define EOM_TYPE_URI_CONVERTER (eom_uri_converter_get_type ())
+#define EOM_URI_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EOM_TYPE_URI_CONVERTER, EomURIConverter))
+#define EOM_URI_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EOM_TYPE_URI_CONVERTER, EomURIConverterClass))
+#define EOM_IS_URI_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EOM_TYPE_URI_CONVERTER))
+#define EOM_IS_URI_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EOM_TYPE_URI_CONVERTER))
+#define EOM_URI_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EOM_TYPE_URI_CONVERTER, EomURIConverterClass))
+
+#ifndef __EOM_URI_CONVERTER_DECLR__
+#define __EOM_URI_CONVERTER_DECLR__
+typedef struct _EomURIConverter EomURIConverter;
+#endif
+typedef struct _EomURIConverterClass EomURIConverterClass;
+typedef struct _EomURIConverterPrivate EomURIConverterPrivate;
+
+typedef enum {
+ EOM_UC_STRING,
+ EOM_UC_FILENAME,
+ EOM_UC_COUNTER,
+ EOM_UC_COMMENT,
+ EOM_UC_DATE,
+ EOM_UC_TIME,
+ EOM_UC_DAY,
+ EOM_UC_MONTH,
+ EOM_UC_YEAR,
+ EOM_UC_HOUR,
+ EOM_UC_MINUTE,
+ EOM_UC_SECOND,
+ EOM_UC_END
+} EomUCType;
+
+typedef struct {
+ char *description;
+ char *rep;
+ gboolean req_exif;
+} EomUCInfo;
+
+typedef enum {
+ EOM_UC_ERROR_INVALID_UNICODE,
+ EOM_UC_ERROR_INVALID_CHARACTER,
+ EOM_UC_ERROR_EQUAL_FILENAMES,
+ EOM_UC_ERROR_UNKNOWN
+} EomUCError;
+
+#define EOM_UC_ERROR eom_uc_error_quark ()
+
+
+struct _EomURIConverter {
+ GObject parent;
+
+ EomURIConverterPrivate *priv;
+};
+
+struct _EomURIConverterClass {
+ GObjectClass parent_klass;
+};
+
+G_GNUC_INTERNAL
+GType eom_uri_converter_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GQuark eom_uc_error_quark (void);
+
+G_GNUC_INTERNAL
+EomURIConverter* eom_uri_converter_new (GFile *base_file,
+ GdkPixbufFormat *img_format,
+ const char *format_string);
+
+G_GNUC_INTERNAL
+gboolean eom_uri_converter_check (EomURIConverter *converter,
+ GList *img_list,
+ GError **error);
+
+G_GNUC_INTERNAL
+gboolean eom_uri_converter_requires_exif (EomURIConverter *converter);
+
+G_GNUC_INTERNAL
+gboolean eom_uri_converter_do (EomURIConverter *converter,
+ EomImage *image,
+ GFile **file,
+ GdkPixbufFormat **format,
+ GError **error);
+
+G_GNUC_INTERNAL
+char* eom_uri_converter_preview (const char *format_str,
+ EomImage *img,
+ GdkPixbufFormat *format,
+ gulong counter,
+ guint n_images,
+ gboolean convert_spaces,
+ gunichar space_char);
+
+/* for debugging purpose only */
+G_GNUC_INTERNAL
+void eom_uri_converter_print_list (EomURIConverter *conv);
+
+G_END_DECLS
+
+#endif /* _EOM_URI_CONVERTER_H_ */
diff --git a/src/eom-util.c b/src/eom-util.c
new file mode 100644
index 0000000..2d74305
--- /dev/null
+++ b/src/eom-util.c
@@ -0,0 +1,348 @@
+/* Eye Of Mate - General Utilities
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[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
+
+#include <sys/time.h>
+#ifdef HAVE_STRPTIME
+#define _XOPEN_SOURCE
+#endif /* HAVE_STRPTIME */
+
+#include <time.h>
+
+#include "eom-util.h"
+
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+void
+eom_util_show_help (const gchar *section, GtkWindow *parent)
+{
+ GError *error = NULL;
+ gchar *uri = NULL;
+
+ if (section)
+ uri = g_strdup_printf ("ghelp:eom?%s", section);
+
+ gtk_show_uri (NULL, ((uri != NULL) ? uri : "ghelp:eom"),
+ gtk_get_current_event_time (), &error);
+
+ g_free (uri);
+
+ if (error) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (parent,
+ 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Could not display help for Eye of MATE"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+
+ g_signal_connect_swapped (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ dialog);
+ gtk_widget_show (dialog);
+
+ g_error_free (error);
+ }
+}
+
+gchar *
+eom_util_make_valid_utf8 (const gchar *str)
+{
+ GString *string;
+ const char *remainder, *invalid;
+ int remaining_bytes, valid_bytes;
+
+ string = NULL;
+ remainder = str;
+ remaining_bytes = strlen (str);
+
+ while (remaining_bytes != 0) {
+ if (g_utf8_validate (remainder, remaining_bytes, &invalid)) {
+ break;
+ }
+
+ valid_bytes = invalid - remainder;
+
+ if (string == NULL) {
+ string = g_string_sized_new (remaining_bytes);
+ }
+
+ g_string_append_len (string, remainder, valid_bytes);
+ g_string_append_c (string, '?');
+
+ remaining_bytes -= valid_bytes + 1;
+ remainder = invalid + 1;
+ }
+
+ if (string == NULL) {
+ return g_strdup (str);
+ }
+
+ g_string_append (string, remainder);
+ g_string_append (string, _(" (invalid Unicode)"));
+
+ g_assert (g_utf8_validate (string->str, -1, NULL));
+
+ return g_string_free (string, FALSE);
+}
+
+GSList*
+eom_util_parse_uri_string_list_to_file_list (const gchar *uri_list)
+{
+ GSList* file_list = NULL;
+ gsize i = 0;
+ gchar **uris;
+
+ uris = g_uri_list_extract_uris (uri_list);
+
+ while (uris[i] != NULL) {
+ file_list = g_slist_append (file_list, g_file_new_for_uri (uris[i]));
+ i++;
+ }
+
+ g_strfreev (uris);
+
+ return file_list;
+}
+
+GSList*
+eom_util_string_list_to_file_list (GSList *string_list)
+{
+ GSList *it = NULL;
+ GSList *file_list = NULL;
+
+ for (it = string_list; it != NULL; it = it->next) {
+ char *uri_str;
+
+ uri_str = (gchar *) it->data;
+
+ file_list = g_slist_prepend (file_list,
+ g_file_new_for_uri (uri_str));
+ }
+
+ return g_slist_reverse (file_list);
+}
+
+#ifdef HAVE_DBUS
+GSList*
+eom_util_strings_to_file_list (gchar **strings)
+{
+ int i;
+ GSList *file_list = NULL;
+
+ for (i = 0; strings[i]; i++) {
+ file_list = g_slist_prepend (file_list,
+ g_file_new_for_uri (strings[i]));
+ }
+
+ return g_slist_reverse (file_list);
+}
+#endif
+
+GSList*
+eom_util_string_array_to_list (const gchar **files, gboolean create_uri)
+{
+ gint i;
+ GSList *list = NULL;
+
+ if (files == NULL) return list;
+
+ for (i = 0; files[i]; i++) {
+ char *str;
+
+ if (create_uri) {
+ GFile *file;
+
+ file = g_file_new_for_commandline_arg (files[i]);
+ str = g_file_get_uri (file);
+
+ g_object_unref (file);
+ } else {
+ str = g_strdup (files[i]);
+ }
+
+ if (str) {
+ list = g_slist_prepend (list, g_strdup (str));
+ g_free (str);
+ }
+ }
+
+ return g_slist_reverse (list);
+}
+
+gchar **
+eom_util_string_array_make_absolute (gchar **files)
+{
+ int i;
+ int size;
+ gchar **abs_files;
+ GFile *file;
+
+ if (files == NULL)
+ return NULL;
+
+ size = g_strv_length (files);
+
+ /* Ensure new list is NULL-terminated */
+ abs_files = g_new0 (gchar *, size+1);
+
+ for (i = 0; i < size; i++) {
+ file = g_file_new_for_commandline_arg (files[i]);
+ abs_files[i] = g_file_get_uri (file);
+
+ g_object_unref (file);
+ }
+
+ return abs_files;
+}
+
+static gchar *dot_dir = NULL;
+
+static gboolean
+ensure_dir_exists (const char *dir)
+{
+ if (g_file_test (dir, G_FILE_TEST_IS_DIR))
+ return TRUE;
+
+ if (g_mkdir_with_parents (dir, 0700) == 0)
+ return TRUE;
+
+ if (errno == EEXIST)
+ return g_file_test (dir, G_FILE_TEST_IS_DIR);
+
+ g_warning ("Failed to create directory %s: %s", dir, strerror (errno));
+ return FALSE;
+}
+
+const gchar *
+eom_util_dot_dir (void)
+{
+ if (dot_dir == NULL) {
+ gboolean exists;
+
+ dot_dir = g_build_filename (g_get_home_dir (),
+ ".mate2",
+ "eom",
+ NULL);
+
+ exists = ensure_dir_exists (dot_dir);
+
+ if (G_UNLIKELY (!exists)) {
+ static gboolean printed_warning = FALSE;
+
+ if (!printed_warning) {
+ g_warning ("EOM could not save some of your preferences in its settings directory due to a file with the same name (%s) blocking its creation. Please remove that file, or move it away.", dot_dir);
+ printed_warning = TRUE;
+ }
+ dot_dir = NULL;
+ return NULL;
+ }
+ }
+
+ return dot_dir;
+}
+
+/* Based on eel_filename_strip_extension() */
+
+/**
+ * eom_util_filename_get_extension:
+ * @filename: a filename
+ *
+ * Returns a reasonably good guess of the file extension of @filename.
+ *
+ * Returns: a newly allocated string with the file extension of @filename.
+ **/
+char *
+eom_util_filename_get_extension (const char * filename)
+{
+ char *begin, *begin2;
+
+ if (filename == NULL) {
+ return NULL;
+ }
+
+ begin = strrchr (filename, '.');
+
+ if (begin && begin != filename) {
+ if (strcmp (begin, ".gz") == 0 ||
+ strcmp (begin, ".bz2") == 0 ||
+ strcmp (begin, ".sit") == 0 ||
+ strcmp (begin, ".Z") == 0) {
+ begin2 = begin - 1;
+ while (begin2 > filename &&
+ *begin2 != '.') {
+ begin2--;
+ }
+ if (begin2 != filename) {
+ begin = begin2;
+ }
+ }
+ begin ++;
+ } else {
+ return NULL;
+ }
+
+ return g_strdup (begin);
+}
+
+
+/**
+ * eom_util_file_is_persistent:
+ * @file: a #GFile
+ *
+ * Checks whether @file is a non-removable local mount.
+ *
+ * Returns: %TRUE if @file is in a non-removable mount,
+ * %FALSE otherwise or when it is remote.
+ **/
+gboolean
+eom_util_file_is_persistent (GFile *file)
+{
+ GMount *mount;
+
+ if (!g_file_is_native (file))
+ return FALSE;
+
+ mount = g_file_find_enclosing_mount (file, NULL, NULL);
+ if (mount) {
+ if (g_mount_can_unmount (mount)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/src/eom-util.h b/src/eom-util.h
new file mode 100644
index 0000000..e9a3dc8
--- /dev/null
+++ b/src/eom-util.h
@@ -0,0 +1,72 @@
+/* Eye Of Mate - General Utilities
+ *
+ * Copyright (C) 2006 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[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_UTIL_H__
+#define __EOM_UTIL_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+void eom_util_show_help (const gchar *section,
+ GtkWindow *parent);
+
+G_GNUC_INTERNAL
+gchar *eom_util_make_valid_utf8 (const gchar *name);
+
+G_GNUC_INTERNAL
+GSList *eom_util_parse_uri_string_list_to_file_list (const gchar *uri_list);
+
+G_GNUC_INTERNAL
+GSList *eom_util_string_list_to_file_list (GSList *string_list);
+
+#ifdef HAVE_DBUS
+G_GNUC_INTERNAL
+GSList *eom_util_strings_to_file_list (gchar **strings);
+#endif
+
+G_GNUC_INTERNAL
+GSList *eom_util_string_array_to_list (const gchar **files,
+ gboolean create_uri);
+
+G_GNUC_INTERNAL
+gchar **eom_util_string_array_make_absolute (gchar **files);
+
+G_GNUC_INTERNAL
+gboolean eom_util_launch_desktop_file (const gchar *filename,
+ guint32 user_time);
+
+G_GNUC_INTERNAL
+const gchar *eom_util_dot_dir (void);
+
+G_GNUC_INTERNAL
+char * eom_util_filename_get_extension (const char * filename_with_extension);
+
+G_GNUC_INTERNAL
+gboolean eom_util_file_is_persistent (GFile *file);
+
+G_END_DECLS
+
+#endif /* __EOM_UTIL_H__ */
diff --git a/src/eom-window.c b/src/eom-window.c
new file mode 100644
index 0000000..a5a85e9
--- /dev/null
+++ b/src/eom-window.c
@@ -0,0 +1,5796 @@
+/* Eye Of Mate - Main Window
+ *
+ * Copyright (C) 2000-2008 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on code by:
+ * - Federico Mena-Quintero <[email protected]>
+ * - Jens Finke <[email protected]>
+ * Based on evince code (shell/ev-window.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+
+#include "eom-window.h"
+#include "eom-scroll-view.h"
+#include "eom-debug.h"
+#include "eom-file-chooser.h"
+#include "eom-thumb-view.h"
+#include "eom-list-store.h"
+#include "eom-sidebar.h"
+#include "eom-statusbar.h"
+#include "eom-preferences-dialog.h"
+#include "eom-properties-dialog.h"
+#include "eom-print.h"
+#include "eom-error-message-area.h"
+#include "eom-application.h"
+#include "eom-thumb-nav.h"
+#include "eom-config-keys.h"
+#include "eom-job-queue.h"
+#include "eom-jobs.h"
+#include "eom-util.h"
+#include "eom-save-as-dialog-helper.h"
+#include "eom-plugin-engine.h"
+#include "eom-close-confirmation-dialog.h"
+
+#include "eom-enum-types.h"
+
+#include "egg-toolbar-editor.h"
+#include "egg-editable-toolbar.h"
+#include "egg-toolbars-model.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#if HAVE_LCMS
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#include <lcms.h>
+#endif
+
+#define EOM_WINDOW_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_WINDOW, EomWindowPrivate))
+
+G_DEFINE_TYPE (EomWindow, eom_window, GTK_TYPE_WINDOW);
+
+#define EOM_WINDOW_MIN_WIDTH 440
+#define EOM_WINDOW_MIN_HEIGHT 350
+
+#define EOM_WINDOW_DEFAULT_WIDTH 540
+#define EOM_WINDOW_DEFAULT_HEIGHT 450
+
+#define EOM_WINDOW_FULLSCREEN_TIMEOUT 5 * 1000
+#define EOM_WINDOW_FULLSCREEN_POPUP_THRESHOLD 5
+
+#define EOM_RECENT_FILES_GROUP "Graphics"
+#define EOM_RECENT_FILES_APP_NAME "Eye of MATE Image Viewer"
+#define EOM_RECENT_FILES_LIMIT 5
+
+#define EOM_WALLPAPER_FILENAME "eom-wallpaper"
+
+#define is_rtl (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
+
+typedef enum {
+ EOM_WINDOW_STATUS_UNKNOWN,
+ EOM_WINDOW_STATUS_INIT,
+ EOM_WINDOW_STATUS_NORMAL
+} EomWindowStatus;
+
+enum {
+ PROP_0,
+ PROP_STARTUP_FLAGS
+};
+
+enum {
+ SIGNAL_PREPARED,
+ SIGNAL_LAST
+};
+
+static gint signals[SIGNAL_LAST];
+
+/* MateConfNotifications */
+enum {
+ EOM_WINDOW_NOTIFY_INTERPOLATE,
+ EOM_WINDOW_NOTIFY_EXTRAPOLATE,
+ EOM_WINDOW_NOTIFY_SCROLLWHEEL_ZOOM,
+ EOM_WINDOW_NOTIFY_ZOOM_MULTIPLIER,
+ EOM_WINDOW_NOTIFY_BACKGROUND_COLOR,
+ EOM_WINDOW_NOTIFY_USE_BG_COLOR,
+ EOM_WINDOW_NOTIFY_TRANSPARENCY,
+ EOM_WINDOW_NOTIFY_TRANS_COLOR,
+ EOM_WINDOW_NOTIFY_SCROLL_BUTTONS,
+ EOM_WINDOW_NOTIFY_COLLECTION_POS,
+ EOM_WINDOW_NOTIFY_COLLECTION_RESIZABLE,
+ EOM_WINDOW_NOTIFY_CAN_SAVE,
+ EOM_WINDOW_NOTIFY_PROPSDIALOG_NETBOOK_MODE,
+ EOM_WINDOW_NOTIFY_LENGTH
+};
+
+struct _EomWindowPrivate {
+ MateConfClient *client;
+ guint client_notifications[EOM_WINDOW_NOTIFY_LENGTH];
+
+ EomListStore *store;
+ EomImage *image;
+ EomWindowMode mode;
+ EomWindowStatus status;
+
+ GtkUIManager *ui_mgr;
+ GtkWidget *box;
+ GtkWidget *layout;
+ GtkWidget *cbox;
+ GtkWidget *view;
+ GtkWidget *sidebar;
+ GtkWidget *thumbview;
+ GtkWidget *statusbar;
+ GtkWidget *nav;
+ GtkWidget *message_area;
+ GtkWidget *toolbar;
+ GObject *properties_dlg;
+
+ GtkActionGroup *actions_window;
+ GtkActionGroup *actions_image;
+ GtkActionGroup *actions_collection;
+ GtkActionGroup *actions_recent;
+
+ GtkWidget *fullscreen_popup;
+ GSource *fullscreen_timeout_source;
+
+ gboolean slideshow_loop;
+ gint slideshow_switch_timeout;
+ GSource *slideshow_switch_source;
+
+ guint recent_menu_id;
+
+ EomJob *load_job;
+ EomJob *transform_job;
+ EomJob *save_job;
+ GFile *last_save_as_folder;
+ EomJob *copy_job;
+
+ guint image_info_message_cid;
+ guint tip_message_cid;
+ guint copy_file_cid;
+
+ EomStartupFlags flags;
+ GSList *file_list;
+
+ gint collection_position;
+ gboolean collection_resizable;
+
+ GtkActionGroup *actions_open_with;
+ guint open_with_menu_id;
+
+ gboolean save_disabled;
+ gboolean needs_reload_confirmation;
+
+#ifdef HAVE_LCMS
+ cmsHPROFILE *display_profile;
+#endif
+};
+
+static void eom_window_cmd_fullscreen (GtkAction *action, gpointer user_data);
+static void eom_window_run_fullscreen (EomWindow *window, gboolean slideshow);
+static void eom_window_cmd_slideshow (GtkAction *action, gpointer user_data);
+static void eom_window_cmd_pause_slideshow (GtkAction *action, gpointer user_data);
+static void eom_window_stop_fullscreen (EomWindow *window, gboolean slideshow);
+static void eom_job_load_cb (EomJobLoad *job, gpointer data);
+static void eom_job_save_progress_cb (EomJobSave *job, float progress, gpointer data);
+static void eom_job_progress_cb (EomJobLoad *job, float progress, gpointer data);
+static void eom_job_transform_cb (EomJobTransform *job, gpointer data);
+static void fullscreen_set_timeout (EomWindow *window);
+static void fullscreen_clear_timeout (EomWindow *window);
+static void update_action_groups_state (EomWindow *window);
+static void open_with_launch_application_cb (GtkAction *action, gpointer callback_data);
+static void eom_window_update_openwith_menu (EomWindow *window, EomImage *image);
+static void eom_window_list_store_image_added (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data);
+static void eom_window_list_store_image_removed (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ gpointer user_data);
+static void eom_window_set_wallpaper (EomWindow *window, const gchar *filename, const gchar *visible_filename);
+static gboolean eom_window_save_images (EomWindow *window, GList *images);
+static void eom_window_finish_saving (EomWindow *window);
+
+static GQuark
+eom_window_error_quark (void)
+{
+ static GQuark q = 0;
+
+ if (q == 0)
+ q = g_quark_from_static_string ("eom-window-error-quark");
+
+ return q;
+}
+
+static void
+eom_window_interp_in_type_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ gboolean interpolate_in = TRUE;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) {
+ interpolate_in = mateconf_value_get_bool (entry->value);
+ }
+
+ eom_scroll_view_set_antialiasing_in (EOM_SCROLL_VIEW (priv->view),
+ interpolate_in);
+}
+
+static void
+eom_window_interp_out_type_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ gboolean interpolate_out = TRUE;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) {
+ interpolate_out = mateconf_value_get_bool (entry->value);
+ }
+
+ eom_scroll_view_set_antialiasing_out (EOM_SCROLL_VIEW (priv->view),
+ interpolate_out);
+}
+
+static void
+eom_window_scroll_wheel_zoom_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ gboolean scroll_wheel_zoom = FALSE;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) {
+ scroll_wheel_zoom = mateconf_value_get_bool (entry->value);
+ }
+
+ eom_scroll_view_set_scroll_wheel_zoom (EOM_SCROLL_VIEW (priv->view),
+ scroll_wheel_zoom);
+}
+
+static void
+eom_window_zoom_multiplier_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ gdouble multiplier = 0.05;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_FLOAT) {
+ multiplier = mateconf_value_get_float (entry->value);
+ }
+
+ eom_scroll_view_set_zoom_multiplier (EOM_SCROLL_VIEW (priv->view),
+ multiplier);
+}
+
+static void
+eom_window_transparency_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ const char *value = NULL;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_STRING) {
+ value = mateconf_value_get_string (entry->value);
+ }
+
+ if (value == NULL) {
+ return;
+ } else if (g_ascii_strcasecmp (value, "COLOR") == 0) {
+ GdkColor color;
+ char *color_str;
+
+ color_str = mateconf_client_get_string (priv->client,
+ EOM_CONF_VIEW_TRANS_COLOR, NULL);
+ if (gdk_color_parse (color_str, &color)) {
+ eom_scroll_view_set_transparency (EOM_SCROLL_VIEW (priv->view),
+ EOM_TRANSP_COLOR, &color);
+ }
+ g_free (color_str);
+ } else if (g_ascii_strcasecmp (value, "CHECK_PATTERN") == 0) {
+ eom_scroll_view_set_transparency (EOM_SCROLL_VIEW (priv->view),
+ EOM_TRANSP_CHECKED, NULL);
+ } else {
+ eom_scroll_view_set_transparency (EOM_SCROLL_VIEW (priv->view),
+ EOM_TRANSP_BACKGROUND, NULL);
+ }
+}
+
+static void
+eom_window_bg_color_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ GdkColor color;
+ const char *color_str;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_STRING) {
+ color_str = mateconf_value_get_string (entry->value);
+
+ if (gdk_color_parse (color_str, &color)) {
+ eom_scroll_view_set_background_color (EOM_SCROLL_VIEW (priv->view), &color);
+ }
+ }
+}
+
+static void
+eom_window_use_bg_color_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ gboolean use_bg_color = TRUE;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) {
+ use_bg_color = mateconf_value_get_bool (entry->value);
+ }
+
+ eom_scroll_view_set_use_bg_color (EOM_SCROLL_VIEW (priv->view),
+ use_bg_color);
+}
+
+
+static void
+eom_window_trans_color_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ GdkColor color;
+ const char *color_str;
+ char *value;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ value = mateconf_client_get_string (priv->client,
+ EOM_CONF_VIEW_TRANSPARENCY,
+ NULL);
+
+ if (!value || g_ascii_strcasecmp (value, "COLOR") != 0) {
+ g_free (value);
+ return;
+ }
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_STRING) {
+ color_str = mateconf_value_get_string (entry->value);
+
+ if (gdk_color_parse (color_str, &color)) {
+ eom_scroll_view_set_transparency (EOM_SCROLL_VIEW (priv->view),
+ EOM_TRANSP_COLOR, &color);
+ }
+ }
+ g_free (value);
+}
+
+static void
+eom_window_scroll_buttons_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ gboolean show_buttons = TRUE;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ g_return_if_fail (EOM_IS_SCROLL_VIEW (priv->view));
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) {
+ show_buttons = mateconf_value_get_bool (entry->value);
+ }
+
+ eom_thumb_nav_set_show_buttons (EOM_THUMB_NAV (priv->nav),
+ show_buttons);
+}
+
+static void
+eom_window_collection_mode_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ MateConfEntry *mode_entry;
+ GtkWidget *hpaned;
+ EomThumbNavMode mode = EOM_THUMB_NAV_MODE_ONE_ROW;
+ gint position = 0;
+ gboolean resizable = FALSE;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ mode_entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION_POSITION,
+ NULL, TRUE, NULL);
+
+ if (G_LIKELY (mode_entry != NULL)) {
+ if (mode_entry->value != NULL &&
+ mode_entry->value->type == MATECONF_VALUE_INT) {
+ position = mateconf_value_get_int (mode_entry->value);
+ }
+ mateconf_entry_unref (mode_entry);
+ }
+
+ mode_entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION_RESIZABLE,
+ NULL, TRUE, NULL);
+
+ if (G_LIKELY (mode_entry != NULL)) {
+ if (mode_entry->value != NULL &&
+ mode_entry->value->type == MATECONF_VALUE_BOOL) {
+ resizable = mateconf_value_get_bool (mode_entry->value);
+ }
+ mateconf_entry_unref (mode_entry);
+ }
+
+ if (priv->collection_position == position &&
+ priv->collection_resizable == resizable)
+ return;
+
+ priv->collection_position = position;
+ priv->collection_resizable = resizable;
+
+ hpaned = gtk_widget_get_parent (priv->sidebar);
+
+ g_object_ref (hpaned);
+ g_object_ref (priv->nav);
+
+ gtk_container_remove (GTK_CONTAINER (priv->layout), hpaned);
+ gtk_container_remove (GTK_CONTAINER (priv->layout), priv->nav);
+
+ gtk_widget_destroy (priv->layout);
+
+ switch (position) {
+ case 0:
+ case 2:
+ if (resizable) {
+ mode = EOM_THUMB_NAV_MODE_MULTIPLE_ROWS;
+
+ priv->layout = gtk_vpaned_new ();
+
+ if (position == 0) {
+ gtk_paned_pack1 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
+ gtk_paned_pack2 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
+ } else {
+ gtk_paned_pack1 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
+ gtk_paned_pack2 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
+ }
+ } else {
+ mode = EOM_THUMB_NAV_MODE_ONE_ROW;
+
+ priv->layout = gtk_vbox_new (FALSE, 2);
+
+ if (position == 0) {
+ gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
+ } else {
+ gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
+ }
+ }
+ break;
+
+ case 1:
+ case 3:
+ if (resizable) {
+ mode = EOM_THUMB_NAV_MODE_MULTIPLE_COLUMNS;
+
+ priv->layout = gtk_hpaned_new ();
+
+ if (position == 1) {
+ gtk_paned_pack1 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
+ gtk_paned_pack2 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
+ } else {
+ gtk_paned_pack1 (GTK_PANED (priv->layout), hpaned, TRUE, FALSE);
+ gtk_paned_pack2 (GTK_PANED (priv->layout), priv->nav, FALSE, TRUE);
+ }
+ } else {
+ mode = EOM_THUMB_NAV_MODE_ONE_COLUMN;
+
+ priv->layout = gtk_hbox_new (FALSE, 2);
+
+ if (position == 1) {
+ gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
+ } else {
+ gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
+ }
+ }
+
+ break;
+ }
+
+ gtk_box_pack_end (GTK_BOX (priv->cbox), priv->layout, TRUE, TRUE, 0);
+
+ eom_thumb_nav_set_mode (EOM_THUMB_NAV (priv->nav), mode);
+
+ if (priv->mode != EOM_WINDOW_STATUS_UNKNOWN) {
+ update_action_groups_state (EOM_WINDOW (user_data));
+ }
+}
+
+static void
+eom_window_can_save_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ EomWindow *window;
+ gboolean save_disabled = FALSE;
+ GtkAction *action_save, *action_save_as;
+
+ eom_debug (DEBUG_PREFERENCES);
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+ priv = EOM_WINDOW (user_data)->priv;
+
+ if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) {
+ save_disabled = mateconf_value_get_bool (entry->value);
+ }
+
+ priv->save_disabled = save_disabled;
+
+ action_save =
+ gtk_action_group_get_action (priv->actions_image, "ImageSave");
+ action_save_as =
+ gtk_action_group_get_action (priv->actions_image, "ImageSaveAs");
+
+ if (priv->save_disabled) {
+ gtk_action_set_sensitive (action_save, FALSE);
+ gtk_action_set_sensitive (action_save_as, FALSE);
+ } else {
+ EomImage *image = eom_window_get_image (window);
+
+ if (EOM_IS_IMAGE (image)) {
+ gtk_action_set_sensitive (action_save,
+ eom_image_is_modified (image));
+
+ gtk_action_set_sensitive (action_save_as, TRUE);
+ }
+ }
+}
+
+static void
+eom_window_pd_nbmode_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ if (window->priv->properties_dlg != NULL) {
+ gboolean netbook_mode;
+ EomPropertiesDialog *dlg;
+
+ netbook_mode = mateconf_value_get_bool (entry->value);
+ dlg = EOM_PROPERTIES_DIALOG (window->priv->properties_dlg);
+
+ eom_properties_dialog_set_netbook_mode (dlg, netbook_mode);
+ }
+}
+
+#ifdef HAVE_LCMS
+static cmsHPROFILE *
+eom_window_get_display_profile (GdkScreen *screen)
+{
+ Display *dpy;
+ Atom icc_atom, type;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ gulong length;
+ guchar *str;
+ int result;
+ cmsHPROFILE *profile;
+ char *atom_name;
+ int lcms_error_action;
+
+ dpy = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
+
+ if (gdk_screen_get_number (screen) > 0)
+ atom_name = g_strdup_printf ("_ICC_PROFILE_%d", gdk_screen_get_number (screen));
+ else
+ atom_name = g_strdup ("_ICC_PROFILE");
+
+ icc_atom = gdk_x11_get_xatom_by_name_for_display (gdk_screen_get_display (screen), atom_name);
+
+ g_free (atom_name);
+
+ result = XGetWindowProperty (dpy,
+ GDK_WINDOW_XID (gdk_screen_get_root_window (screen)),
+ icc_atom,
+ 0,
+ G_MAXLONG,
+ False,
+ XA_CARDINAL,
+ &type,
+ &format,
+ &nitems,
+ &bytes_after,
+ (guchar **)&str);
+
+ /* TODO: handle bytes_after != 0 */
+
+ if ((result == Success) && (type == XA_CARDINAL) && (nitems > 0)) {
+ switch (format)
+ {
+ case 8:
+ length = nitems;
+ break;
+ case 16:
+ length = sizeof(short) * nitems;
+ break;
+ case 32:
+ length = sizeof(long) * nitems;
+ break;
+ default:
+ eom_debug_message (DEBUG_LCMS, "Unable to read profile, not correcting");
+
+ XFree (str);
+ return NULL;
+ }
+
+ /* Make lcms errors non-fatal here, as it is possible
+ * to load invalid profiles with XICC.
+ * We don't want lcms to abort EOM in that case.
+ */
+ lcms_error_action = cmsErrorAction (LCMS_ERROR_IGNORE);
+
+ profile = cmsOpenProfileFromMem (str, length);
+
+ // Restore the previous error setting
+ cmsErrorAction (lcms_error_action);
+
+ if (G_UNLIKELY (profile == NULL)) {
+ eom_debug_message (DEBUG_LCMS,
+ "Invalid display profile, "
+ "not correcting");
+ }
+
+ XFree (str);
+ } else {
+ profile = NULL;
+ eom_debug_message (DEBUG_LCMS, "No profile, not correcting");
+ }
+
+ return profile;
+}
+#endif
+
+static void
+update_image_pos (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ gint pos, n_images;
+
+ priv = window->priv;
+
+ n_images = eom_list_store_length (EOM_LIST_STORE (priv->store));
+
+ if (n_images > 0) {
+ pos = eom_list_store_get_pos_by_image (EOM_LIST_STORE (priv->store),
+ priv->image);
+
+ /* Images: (image pos) / (n_total_images) */
+ eom_statusbar_set_image_number (EOM_STATUSBAR (priv->statusbar),
+ pos + 1,
+ n_images);
+ }
+}
+
+static void
+update_status_bar (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ char *str = NULL;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = window->priv;
+
+ if (priv->image != NULL &&
+ eom_image_has_data (priv->image, EOM_IMAGE_DATA_ALL)) {
+ int zoom, width, height;
+ goffset bytes = 0;
+
+ zoom = floor (100 * eom_scroll_view_get_zoom (EOM_SCROLL_VIEW (priv->view)) + 0.5);
+
+ eom_image_get_size (priv->image, &width, &height);
+
+ bytes = eom_image_get_bytes (priv->image);
+
+ if ((width > 0) && (height > 0)) {
+ char *size_string;
+
+ size_string = g_format_size_for_display (bytes);
+
+ /* Translators: This is the string displayed in the statusbar
+ * The tokens are from left to right:
+ * - image width
+ * - image height
+ * - image size in bytes
+ * - zoom in percent */
+ str = g_strdup_printf (ngettext("%i × %i pixel %s %i%%",
+ "%i × %i pixels %s %i%%", height),
+ width,
+ height,
+ size_string,
+ zoom);
+
+ g_free (size_string);
+ }
+
+ update_image_pos (window);
+ }
+
+ gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar),
+ priv->image_info_message_cid);
+
+ gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
+ priv->image_info_message_cid, str ? str : "");
+
+ g_free (str);
+}
+
+static void
+eom_window_set_message_area (EomWindow *window,
+ GtkWidget *message_area)
+{
+ if (window->priv->message_area == message_area)
+ return;
+
+ if (window->priv->message_area != NULL)
+ gtk_widget_destroy (window->priv->message_area);
+
+ window->priv->message_area = message_area;
+
+ if (message_area == NULL) return;
+
+ gtk_box_pack_start (GTK_BOX (window->priv->cbox),
+ window->priv->message_area,
+ FALSE,
+ FALSE,
+ 0);
+
+ g_object_add_weak_pointer (G_OBJECT (window->priv->message_area),
+ (void *) &window->priv->message_area);
+}
+
+static void
+update_action_groups_state (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ GtkAction *action_collection;
+ GtkAction *action_sidebar;
+ GtkAction *action_fscreen;
+ GtkAction *action_sshow;
+ GtkAction *action_print;
+ gboolean print_disabled = FALSE;
+ gboolean page_setup_disabled = FALSE;
+ gboolean show_image_collection = FALSE;
+ gint n_images = 0;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = window->priv;
+
+ action_collection =
+ gtk_action_group_get_action (priv->actions_window,
+ "ViewImageCollection");
+
+ action_sidebar =
+ gtk_action_group_get_action (priv->actions_window,
+ "ViewSidebar");
+
+ action_fscreen =
+ gtk_action_group_get_action (priv->actions_image,
+ "ViewFullscreen");
+
+ action_sshow =
+ gtk_action_group_get_action (priv->actions_collection,
+ "ViewSlideshow");
+
+ action_print =
+ gtk_action_group_get_action (priv->actions_image,
+ "ImagePrint");
+
+ g_assert (action_collection != NULL);
+ g_assert (action_sidebar != NULL);
+ g_assert (action_fscreen != NULL);
+ g_assert (action_sshow != NULL);
+ g_assert (action_print != NULL);
+
+ if (priv->store != NULL) {
+ n_images = eom_list_store_length (EOM_LIST_STORE (priv->store));
+ }
+
+ if (n_images == 0) {
+ gtk_widget_hide (priv->layout);
+
+ gtk_action_group_set_sensitive (priv->actions_window, TRUE);
+ gtk_action_group_set_sensitive (priv->actions_image, FALSE);
+ gtk_action_group_set_sensitive (priv->actions_collection, FALSE);
+
+ gtk_action_set_sensitive (action_fscreen, FALSE);
+ gtk_action_set_sensitive (action_sshow, FALSE);
+
+ /* If there are no images on model, initialization
+ stops here. */
+ if (priv->status == EOM_WINDOW_STATUS_INIT) {
+ priv->status = EOM_WINDOW_STATUS_NORMAL;
+ }
+ } else {
+ if (priv->flags & EOM_STARTUP_DISABLE_COLLECTION) {
+ mateconf_client_set_bool (priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION,
+ FALSE,
+ NULL);
+
+ show_image_collection = FALSE;
+ } else {
+ show_image_collection =
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION,
+ NULL);
+ }
+
+ show_image_collection = show_image_collection &&
+ n_images > 1 &&
+ priv->mode != EOM_WINDOW_MODE_SLIDESHOW;
+
+ gtk_widget_show (priv->layout);
+
+ if (show_image_collection)
+ gtk_widget_show (priv->nav);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_collection),
+ show_image_collection);
+
+ gtk_action_group_set_sensitive (priv->actions_window, TRUE);
+ gtk_action_group_set_sensitive (priv->actions_image, TRUE);
+
+ gtk_action_set_sensitive (action_fscreen, TRUE);
+
+ if (n_images == 1) {
+ gtk_action_group_set_sensitive (priv->actions_collection, FALSE);
+ gtk_action_set_sensitive (action_collection, FALSE);
+ gtk_action_set_sensitive (action_sshow, FALSE);
+ } else {
+ gtk_action_group_set_sensitive (priv->actions_collection, TRUE);
+ gtk_action_set_sensitive (action_sshow, TRUE);
+ }
+
+ if (show_image_collection)
+ gtk_widget_grab_focus (priv->thumbview);
+ else
+ gtk_widget_grab_focus (priv->view);
+ }
+
+ print_disabled = mateconf_client_get_bool (priv->client,
+ EOM_CONF_DESKTOP_CAN_PRINT,
+ NULL);
+
+ if (print_disabled) {
+ gtk_action_set_sensitive (action_print, FALSE);
+ }
+
+ page_setup_disabled = mateconf_client_get_bool (priv->client,
+ EOM_CONF_DESKTOP_CAN_SETUP_PAGE,
+ NULL);
+
+ if (eom_sidebar_is_empty (EOM_SIDEBAR (priv->sidebar))) {
+ gtk_action_set_sensitive (action_sidebar, FALSE);
+ gtk_widget_hide (priv->sidebar);
+ }
+}
+
+static void
+update_selection_ui_visibility (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ GtkAction *wallpaper_action;
+ gint n_selected;
+
+ priv = window->priv;
+
+ n_selected = eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (priv->thumbview));
+
+ wallpaper_action =
+ gtk_action_group_get_action (priv->actions_image,
+ "ImageSetAsWallpaper");
+
+ if (n_selected == 1) {
+ gtk_action_set_sensitive (wallpaper_action, TRUE);
+ } else {
+ gtk_action_set_sensitive (wallpaper_action, FALSE);
+ }
+}
+
+static gboolean
+add_file_to_recent_files (GFile *file)
+{
+ gchar *text_uri;
+ GFileInfo *file_info;
+ GtkRecentData *recent_data;
+ static gchar *groups[2] = { EOM_RECENT_FILES_GROUP , NULL };
+
+ if (file == NULL) return FALSE;
+
+ /* The password gets stripped here because ~/.recently-used.xbel is
+ * readable by everyone (chmod 644). It also makes the workaround
+ * for the bug with gtk_recent_info_get_uri_display() easier
+ * (see the comment in eom_window_update_recent_files_menu()). */
+ text_uri = g_file_get_uri (file);
+
+ if (text_uri == NULL)
+ return FALSE;
+
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ if (file_info == NULL)
+ return FALSE;
+
+ recent_data = g_slice_new (GtkRecentData);
+ recent_data->display_name = NULL;
+ recent_data->description = NULL;
+ recent_data->mime_type = (gchar *) g_file_info_get_content_type (file_info);
+ recent_data->app_name = EOM_RECENT_FILES_APP_NAME;
+ recent_data->app_exec = g_strjoin(" ", g_get_prgname (), "%u", NULL);
+ recent_data->groups = groups;
+ recent_data->is_private = FALSE;
+
+ gtk_recent_manager_add_full (gtk_recent_manager_get_default (),
+ text_uri,
+ recent_data);
+
+ g_free (recent_data->app_exec);
+ g_free (text_uri);
+ g_object_unref (file_info);
+
+ g_slice_free (GtkRecentData, recent_data);
+
+ return FALSE;
+}
+
+static void
+image_thumb_changed_cb (EomImage *image, gpointer data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+ GdkPixbuf *thumb;
+
+ g_return_if_fail (EOM_IS_WINDOW (data));
+
+ window = EOM_WINDOW (data);
+ priv = window->priv;
+
+ thumb = eom_image_get_thumbnail (image);
+
+ if (thumb != NULL) {
+ gtk_window_set_icon (GTK_WINDOW (window), thumb);
+
+ if (window->priv->properties_dlg != NULL) {
+ eom_properties_dialog_update (EOM_PROPERTIES_DIALOG (priv->properties_dlg),
+ image);
+ }
+
+ g_object_unref (thumb);
+ } else if (!gtk_widget_get_visible (window->priv->nav)) {
+ gint img_pos = eom_list_store_get_pos_by_image (window->priv->store, image);
+ GtkTreePath *path = gtk_tree_path_new_from_indices (img_pos,-1);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->store), &iter, path);
+ eom_list_store_thumbnail_set (window->priv->store, &iter);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+file_changed_info_bar_response (GtkInfoBar *info_bar,
+ gint response,
+ EomWindow *window)
+{
+ if (response == GTK_RESPONSE_YES) {
+ eom_window_reload_image (window);
+ }
+
+ window->priv->needs_reload_confirmation = TRUE;
+
+ eom_window_set_message_area (window, NULL);
+}
+static void
+image_file_changed_cb (EomImage *img, EomWindow *window)
+{
+ GtkWidget *info_bar;
+ gchar *text, *markup;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *hbox;
+
+ if (window->priv->needs_reload_confirmation == FALSE)
+ return;
+
+ window->priv->needs_reload_confirmation = FALSE;
+
+ info_bar = gtk_info_bar_new_with_buttons (_("_Reload"),
+ GTK_RESPONSE_YES,
+ C_("MessageArea", "Hi_de"),
+ GTK_RESPONSE_NO, NULL);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
+ GTK_MESSAGE_QUESTION);
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION,
+ GTK_ICON_SIZE_DIALOG);
+ label = gtk_label_new (NULL);
+
+ /* The newline character is currently necessary due to a problem
+ * with the automatic line break. */
+ text = g_strdup_printf (_("The image \"%s\" has been modified by an external application."
+ "\nWould you like to reload it?"), eom_image_get_caption (img));
+ markup = g_markup_printf_escaped ("<b>%s</b>", text);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (text);
+ g_free (markup);
+
+ hbox = gtk_hbox_new (FALSE, 8);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))), hbox, TRUE, TRUE, 0);
+ gtk_widget_show_all (hbox);
+ gtk_widget_show (info_bar);
+
+ eom_window_set_message_area (window, info_bar);
+ g_signal_connect (info_bar, "response",
+ G_CALLBACK (file_changed_info_bar_response), window);
+}
+
+static void
+eom_window_display_image (EomWindow *window, EomImage *image)
+{
+ EomWindowPrivate *priv;
+ GFile *file;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+ g_return_if_fail (EOM_IS_IMAGE (image));
+
+ eom_debug (DEBUG_WINDOW);
+
+ g_assert (eom_image_has_data (image, EOM_IMAGE_DATA_ALL));
+
+ priv = window->priv;
+
+ if (image != NULL) {
+ g_signal_connect (image,
+ "thumbnail_changed",
+ G_CALLBACK (image_thumb_changed_cb),
+ window);
+ g_signal_connect (image, "file-changed",
+ G_CALLBACK (image_file_changed_cb),
+ window);
+
+ image_thumb_changed_cb (image, window);
+ }
+
+ priv->needs_reload_confirmation = TRUE;
+
+ eom_scroll_view_set_image (EOM_SCROLL_VIEW (priv->view), image);
+
+ gtk_window_set_title (GTK_WINDOW (window), eom_image_get_caption (image));
+
+ update_status_bar (window);
+
+ file = eom_image_get_file (image);
+ g_idle_add_full (G_PRIORITY_LOW,
+ (GSourceFunc) add_file_to_recent_files,
+ file,
+ (GDestroyNotify) g_object_unref);
+
+ eom_window_update_openwith_menu (window, image);
+}
+
+static void
+open_with_launch_application_cb (GtkAction *action, gpointer data) {
+ EomImage *image;
+ GAppInfo *app;
+ GFile *file;
+ GList *files = NULL;
+
+ image = EOM_IMAGE (data);
+ file = eom_image_get_file (image);
+
+ app = g_object_get_data (G_OBJECT (action), "app");
+ files = g_list_append (files, file);
+ g_app_info_launch (app,
+ files,
+ NULL, NULL);
+
+ g_object_unref (file);
+ g_list_free (files);
+}
+
+static void
+eom_window_update_openwith_menu (EomWindow *window, EomImage *image)
+{
+ GFile *file;
+ GFileInfo *file_info;
+ GList *iter;
+ gchar *label, *tip;
+ const gchar *mime_type;
+ GtkAction *action;
+ EomWindowPrivate *priv;
+ GList *apps;
+ guint action_id = 0;
+ GIcon *app_icon;
+ char *path;
+ GtkWidget *menuitem;
+
+ priv = window->priv;
+
+ file = eom_image_get_file (image);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+
+ if (file_info == NULL)
+ return;
+ else {
+ mime_type = g_file_info_get_content_type (file_info);
+ }
+
+ if (priv->open_with_menu_id != 0) {
+ gtk_ui_manager_remove_ui (priv->ui_mgr, priv->open_with_menu_id);
+ priv->open_with_menu_id = 0;
+ }
+
+ if (priv->actions_open_with != NULL) {
+ gtk_ui_manager_remove_action_group (priv->ui_mgr, priv->actions_open_with);
+ priv->actions_open_with = NULL;
+ }
+
+ if (mime_type == NULL) {
+ g_object_unref (file_info);
+ return;
+ }
+
+ apps = g_app_info_get_all_for_type (mime_type);
+
+ g_object_unref (file_info);
+
+ if (!apps)
+ return;
+
+ priv->actions_open_with = gtk_action_group_new ("OpenWithActions");
+ gtk_ui_manager_insert_action_group (priv->ui_mgr, priv->actions_open_with, -1);
+
+ priv->open_with_menu_id = gtk_ui_manager_new_merge_id (priv->ui_mgr);
+
+ for (iter = apps; iter; iter = iter->next) {
+ GAppInfo *app = iter->data;
+ gchar name[64];
+
+ /* Do not include eom itself */
+ if (g_ascii_strcasecmp (g_app_info_get_executable (app),
+ g_get_prgname ()) == 0) {
+ g_object_unref (app);
+ continue;
+ }
+
+ g_snprintf (name, sizeof (name), "OpenWith%u", action_id++);
+
+ label = g_strdup (g_app_info_get_name (app));
+ tip = g_strdup_printf (_("Use \"%s\" to open the selected image"), g_app_info_get_name (app));
+
+ action = gtk_action_new (name, label, tip, NULL);
+
+ app_icon = g_app_info_get_icon (app);
+ if (G_LIKELY (app_icon != NULL)) {
+ g_object_ref (app_icon);
+ gtk_action_set_gicon (action, app_icon);
+ g_object_unref (app_icon);
+ }
+
+ g_free (label);
+ g_free (tip);
+
+ g_object_set_data_full (G_OBJECT (action), "app", app,
+ (GDestroyNotify) g_object_unref);
+
+ g_signal_connect (action,
+ "activate",
+ G_CALLBACK (open_with_launch_application_cb),
+ image);
+
+ gtk_action_group_add_action (priv->actions_open_with, action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (priv->ui_mgr,
+ priv->open_with_menu_id,
+ "/MainMenu/Image/ImageOpenWith/Applications Placeholder",
+ name,
+ name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ gtk_ui_manager_add_ui (priv->ui_mgr,
+ priv->open_with_menu_id,
+ "/ThumbnailPopup/ImageOpenWith/Applications Placeholder",
+ name,
+ name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ gtk_ui_manager_add_ui (priv->ui_mgr,
+ priv->open_with_menu_id,
+ "/ViewPopup/ImageOpenWith/Applications Placeholder",
+ name,
+ name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ path = g_strdup_printf ("/MainMenu/Image/ImageOpenWith/Applications Placeholder/%s", name);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr, path);
+
+ /* Only force displaying the icon if it is an application icon */
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
+
+ g_free (path);
+
+ path = g_strdup_printf ("/ThumbnailPopup/ImageOpenWith/Applications Placeholder/%s", name);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr, path);
+
+ /* Only force displaying the icon if it is an application icon */
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
+
+ g_free (path);
+
+ path = g_strdup_printf ("/ViewPopup/ImageOpenWith/Applications Placeholder/%s", name);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr, path);
+
+ /* Only force displaying the icon if it is an application icon */
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
+
+ g_free (path);
+ }
+
+ g_list_free (apps);
+}
+
+static void
+eom_window_clear_load_job (EomWindow *window)
+{
+ EomWindowPrivate *priv = window->priv;
+
+ if (priv->load_job != NULL) {
+ if (!priv->load_job->finished)
+ eom_job_queue_remove_job (priv->load_job);
+
+ g_signal_handlers_disconnect_by_func (priv->load_job,
+ eom_job_progress_cb,
+ window);
+
+ g_signal_handlers_disconnect_by_func (priv->load_job,
+ eom_job_load_cb,
+ window);
+
+ eom_image_cancel_load (EOM_JOB_LOAD (priv->load_job)->image);
+
+ g_object_unref (priv->load_job);
+ priv->load_job = NULL;
+
+ /* Hide statusbar */
+ eom_statusbar_set_progress (EOM_STATUSBAR (priv->statusbar), 0);
+ }
+}
+
+static void
+eom_job_progress_cb (EomJobLoad *job, float progress, gpointer user_data)
+{
+ EomWindow *window;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+
+ eom_statusbar_set_progress (EOM_STATUSBAR (window->priv->statusbar),
+ progress);
+}
+
+static void
+eom_job_save_progress_cb (EomJobSave *job, float progress, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ EomWindow *window;
+
+ static EomImage *image = NULL;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+
+ eom_statusbar_set_progress (EOM_STATUSBAR (priv->statusbar),
+ progress);
+
+ if (image != job->current_image) {
+ gchar *str_image, *status_message;
+ guint n_images;
+
+ image = job->current_image;
+
+ n_images = g_list_length (job->images);
+
+ str_image = eom_image_get_uri_for_display (image);
+
+ /* Translators: This string is displayed in the statusbar
+ * while saving images. The tokens are from left to right:
+ * - the original filename
+ * - the current image's position in the queue
+ * - the total number of images queued for saving */
+ status_message = g_strdup_printf (_("Saving image \"%s\" (%u/%u)"),
+ str_image,
+ job->current_pos + 1,
+ n_images);
+ g_free (str_image);
+
+ gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar),
+ priv->image_info_message_cid);
+
+ gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
+ priv->image_info_message_cid,
+ status_message);
+
+ g_free (status_message);
+ }
+
+ if (progress == 1.0)
+ image = NULL;
+}
+
+static void
+eom_window_obtain_desired_size (EomImage *image,
+ gint width,
+ gint height,
+ EomWindow *window)
+{
+ GdkScreen *screen;
+ GdkRectangle monitor;
+ GtkAllocation allocation;
+ gint final_width, final_height;
+ gint screen_width, screen_height;
+ gint window_width, window_height;
+ gint img_width, img_height;
+ gint view_width, view_height;
+ gint deco_width, deco_height;
+
+ update_action_groups_state (window);
+
+ img_width = width;
+ img_height = height;
+
+#if GTK_CHECK_VERSION (2, 20, 0)
+ if (!gtk_widget_get_realized (window->priv->view)) {
+#else
+ if (!GTK_WIDGET_REALIZED (window->priv->view)) {
+#endif
+ gtk_widget_realize (window->priv->view);
+ }
+
+ gtk_widget_get_allocation (window->priv->view, &allocation);
+ view_width = allocation.width;
+ view_height = allocation.height;
+
+#if GTK_CHECK_VERSION (2, 20, 0)
+ if (!gtk_widget_get_realized (GTK_WIDGET (window))) {
+#else
+ if (!GTK_WIDGET_REALIZED (GTK_WIDGET (window))) {
+#endif
+ gtk_widget_realize (GTK_WIDGET (window));
+ }
+
+ gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
+ window_width = allocation.width;
+ window_height = allocation.height;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (window));
+
+ gdk_screen_get_monitor_geometry (screen,
+ gdk_screen_get_monitor_at_window (screen,
+ gtk_widget_get_window (GTK_WIDGET (window))),
+ &monitor);
+
+ screen_width = monitor.width;
+ screen_height = monitor.height;
+
+ deco_width = window_width - view_width;
+ deco_height = window_height - view_height;
+
+ if (img_width > 0 && img_height > 0) {
+ if ((img_width + deco_width > screen_width) ||
+ (img_height + deco_height > screen_height))
+ {
+ double factor;
+
+ if (img_width > img_height) {
+ factor = (screen_width * 0.75 - deco_width) / (double) img_width;
+ } else {
+ factor = (screen_height * 0.75 - deco_height) / (double) img_height;
+ }
+
+ img_width = img_width * factor;
+ img_height = img_height * factor;
+ }
+ }
+
+ final_width = MAX (EOM_WINDOW_MIN_WIDTH, img_width + deco_width);
+ final_height = MAX (EOM_WINDOW_MIN_HEIGHT, img_height + deco_height);
+
+ eom_debug_message (DEBUG_WINDOW, "Setting window size: %d x %d", final_width, final_height);
+
+ gtk_window_set_default_size (GTK_WINDOW (window), final_width, final_height);
+
+ g_signal_emit (window, signals[SIGNAL_PREPARED], 0);
+}
+
+static void
+eom_window_error_message_area_response (GtkInfoBar *message_area,
+ gint response_id,
+ EomWindow *window)
+{
+ if (response_id != GTK_RESPONSE_OK) {
+ eom_window_set_message_area (window, NULL);
+
+ return;
+ }
+
+ /* Trigger loading for current image again */
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (window->priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_CURRENT);
+}
+
+static void
+eom_job_load_cb (EomJobLoad *job, gpointer data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+ GtkAction *action_undo, *action_save;
+
+ g_return_if_fail (EOM_IS_WINDOW (data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ window = EOM_WINDOW (data);
+ priv = window->priv;
+
+ eom_statusbar_set_progress (EOM_STATUSBAR (priv->statusbar), 0.0);
+
+ gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
+ priv->image_info_message_cid);
+
+ if (priv->image != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->image,
+ image_thumb_changed_cb,
+ window);
+ g_signal_handlers_disconnect_by_func (priv->image,
+ image_file_changed_cb,
+ window);
+
+ g_object_unref (priv->image);
+ }
+
+ priv->image = g_object_ref (job->image);
+
+ if (EOM_JOB (job)->error == NULL) {
+#ifdef HAVE_LCMS
+ eom_image_apply_display_profile (job->image,
+ priv->display_profile);
+#endif
+
+ gtk_action_group_set_sensitive (priv->actions_image, TRUE);
+
+ eom_window_display_image (window, job->image);
+ } else {
+ GtkWidget *message_area;
+
+ message_area = eom_image_load_error_message_area_new (
+ eom_image_get_caption (job->image),
+ EOM_JOB (job)->error);
+
+ g_signal_connect (message_area,
+ "response",
+ G_CALLBACK (eom_window_error_message_area_response),
+ window);
+
+ gtk_window_set_icon (GTK_WINDOW (window), NULL);
+ gtk_window_set_title (GTK_WINDOW (window),
+ eom_image_get_caption (job->image));
+
+ eom_window_set_message_area (window, message_area);
+
+ gtk_info_bar_set_default_response (GTK_INFO_BAR (message_area),
+ GTK_RESPONSE_CANCEL);
+
+ gtk_widget_show (message_area);
+
+ update_status_bar (window);
+
+ eom_scroll_view_set_image (EOM_SCROLL_VIEW (priv->view), NULL);
+
+ if (window->priv->status == EOM_WINDOW_STATUS_INIT) {
+ update_action_groups_state (window);
+
+ g_signal_emit (window, signals[SIGNAL_PREPARED], 0);
+ }
+
+ gtk_action_group_set_sensitive (priv->actions_image, FALSE);
+ }
+
+ eom_window_clear_load_job (window);
+
+ if (window->priv->status == EOM_WINDOW_STATUS_INIT) {
+ window->priv->status = EOM_WINDOW_STATUS_NORMAL;
+
+ g_signal_handlers_disconnect_by_func
+ (job->image,
+ G_CALLBACK (eom_window_obtain_desired_size),
+ window);
+ }
+
+ action_save = gtk_action_group_get_action (priv->actions_image, "ImageSave");
+ action_undo = gtk_action_group_get_action (priv->actions_image, "EditUndo");
+
+ /* Set Save and Undo sensitive according to image state.
+ * Respect lockdown in case of Save.*/
+ gtk_action_set_sensitive (action_save, (!priv->save_disabled && eom_image_is_modified (job->image)));
+ gtk_action_set_sensitive (action_undo, eom_image_is_modified (job->image));
+
+ g_object_unref (job->image);
+}
+
+static void
+eom_window_clear_transform_job (EomWindow *window)
+{
+ EomWindowPrivate *priv = window->priv;
+
+ if (priv->transform_job != NULL) {
+ if (!priv->transform_job->finished)
+ eom_job_queue_remove_job (priv->transform_job);
+
+ g_signal_handlers_disconnect_by_func (priv->transform_job,
+ eom_job_transform_cb,
+ window);
+ g_object_unref (priv->transform_job);
+ priv->transform_job = NULL;
+ }
+}
+
+static void
+eom_job_transform_cb (EomJobTransform *job, gpointer data)
+{
+ EomWindow *window;
+ GtkAction *action_undo, *action_save;
+ EomImage *image;
+
+ g_return_if_fail (EOM_IS_WINDOW (data));
+
+ window = EOM_WINDOW (data);
+
+ eom_window_clear_transform_job (window);
+
+ action_undo =
+ gtk_action_group_get_action (window->priv->actions_image, "EditUndo");
+ action_save =
+ gtk_action_group_get_action (window->priv->actions_image, "ImageSave");
+
+ image = eom_window_get_image (window);
+
+ gtk_action_set_sensitive (action_undo, eom_image_is_modified (image));
+
+ if (!window->priv->save_disabled)
+ {
+ gtk_action_set_sensitive (action_save, eom_image_is_modified (image));
+ }
+}
+
+static void
+apply_transformation (EomWindow *window, EomTransform *trans)
+{
+ EomWindowPrivate *priv;
+ GList *images;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ priv = window->priv;
+
+ images = eom_thumb_view_get_selected_images (EOM_THUMB_VIEW (priv->thumbview));
+
+ eom_window_clear_transform_job (window);
+
+ priv->transform_job = eom_job_transform_new (images, trans);
+
+ g_signal_connect (priv->transform_job,
+ "finished",
+ G_CALLBACK (eom_job_transform_cb),
+ window);
+
+ g_signal_connect (priv->transform_job,
+ "progress",
+ G_CALLBACK (eom_job_progress_cb),
+ window);
+
+ eom_job_queue_add_job (priv->transform_job);
+}
+
+static void
+handle_image_selection_changed_cb (EomThumbView *thumbview, EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ EomImage *image;
+ gchar *status_message;
+ gchar *str_image;
+
+ priv = window->priv;
+
+ if (eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (priv->thumbview)) == 0)
+ return;
+
+ update_selection_ui_visibility (window);
+
+ image = eom_thumb_view_get_first_selected_image (EOM_THUMB_VIEW (priv->thumbview));
+
+ g_assert (EOM_IS_IMAGE (image));
+
+ eom_window_clear_load_job (window);
+
+ eom_window_set_message_area (window, NULL);
+
+ gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar),
+ priv->image_info_message_cid);
+
+ if (image == priv->image) {
+ update_status_bar (window);
+ return;
+ }
+
+ if (eom_image_has_data (image, EOM_IMAGE_DATA_ALL)) {
+ eom_window_display_image (window, image);
+ return;
+ }
+
+ if (priv->status == EOM_WINDOW_STATUS_INIT) {
+ g_signal_connect (image,
+ "size-prepared",
+ G_CALLBACK (eom_window_obtain_desired_size),
+ window);
+ }
+
+ priv->load_job = eom_job_load_new (image, EOM_IMAGE_DATA_ALL);
+
+ g_signal_connect (priv->load_job,
+ "finished",
+ G_CALLBACK (eom_job_load_cb),
+ window);
+
+ g_signal_connect (priv->load_job,
+ "progress",
+ G_CALLBACK (eom_job_progress_cb),
+ window);
+
+ eom_job_queue_add_job (priv->load_job);
+
+ str_image = eom_image_get_uri_for_display (image);
+
+ status_message = g_strdup_printf (_("Opening image \"%s\""),
+ str_image);
+
+ g_free (str_image);
+
+ gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
+ priv->image_info_message_cid, status_message);
+
+ g_free (status_message);
+}
+
+static void
+view_zoom_changed_cb (GtkWidget *widget, double zoom, gpointer user_data)
+{
+ EomWindow *window;
+ GtkAction *action_zoom_in;
+ GtkAction *action_zoom_out;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+
+ update_status_bar (window);
+
+ action_zoom_in =
+ gtk_action_group_get_action (window->priv->actions_image,
+ "ViewZoomIn");
+
+ action_zoom_out =
+ gtk_action_group_get_action (window->priv->actions_image,
+ "ViewZoomOut");
+
+ gtk_action_set_sensitive (action_zoom_in,
+ !eom_scroll_view_get_zoom_is_max (EOM_SCROLL_VIEW (window->priv->view)));
+ gtk_action_set_sensitive (action_zoom_out,
+ !eom_scroll_view_get_zoom_is_min (EOM_SCROLL_VIEW (window->priv->view)));
+}
+
+static void
+eom_window_open_recent_cb (GtkAction *action, EomWindow *window)
+{
+ GtkRecentInfo *info;
+ const gchar *uri;
+ GSList *list = NULL;
+
+ info = g_object_get_data (G_OBJECT (action), "gtk-recent-info");
+ g_return_if_fail (info != NULL);
+
+ uri = gtk_recent_info_get_uri (info);
+ list = g_slist_prepend (list, g_strdup (uri));
+
+ eom_application_open_uri_list (EOM_APP,
+ list,
+ GDK_CURRENT_TIME,
+ 0,
+ NULL);
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+}
+
+static void
+file_open_dialog_response_cb (GtkWidget *chooser,
+ gint response_id,
+ EomWindow *ev_window)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ GSList *uris;
+
+ uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (chooser));
+
+ eom_application_open_uri_list (EOM_APP,
+ uris,
+ GDK_CURRENT_TIME,
+ 0,
+ NULL);
+
+ g_slist_foreach (uris, (GFunc) g_free, NULL);
+ g_slist_free (uris);
+ }
+
+ gtk_widget_destroy (chooser);
+}
+
+static void
+eom_window_update_fullscreen_action (EomWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->actions_image,
+ "ViewFullscreen");
+
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (eom_window_cmd_fullscreen), window);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ window->priv->mode == EOM_WINDOW_MODE_FULLSCREEN);
+
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (eom_window_cmd_fullscreen), window);
+}
+
+static void
+eom_window_update_slideshow_action (EomWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->actions_collection,
+ "ViewSlideshow");
+
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (eom_window_cmd_slideshow), window);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ window->priv->mode == EOM_WINDOW_MODE_SLIDESHOW);
+
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (eom_window_cmd_slideshow), window);
+}
+
+static void
+eom_window_update_pause_slideshow_action (EomWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->actions_image,
+ "PauseSlideshow");
+
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (eom_window_cmd_pause_slideshow), window);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ window->priv->mode != EOM_WINDOW_MODE_SLIDESHOW);
+
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (eom_window_cmd_pause_slideshow), window);
+}
+
+static void
+eom_window_update_fullscreen_popup (EomWindow *window)
+{
+ GtkWidget *popup = window->priv->fullscreen_popup;
+ GdkRectangle screen_rect;
+ GdkScreen *screen;
+
+ g_return_if_fail (popup != NULL);
+
+ if (gtk_widget_get_window (GTK_WIDGET (window)) == NULL) return;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (window));
+
+ gdk_screen_get_monitor_geometry (screen,
+ gdk_screen_get_monitor_at_window
+ (screen,
+ gtk_widget_get_window (GTK_WIDGET (window))),
+ &screen_rect);
+
+ gtk_widget_set_size_request (popup,
+ screen_rect.width,
+ -1);
+
+ gtk_window_move (GTK_WINDOW (popup), screen_rect.x, screen_rect.y);
+}
+
+static void
+screen_size_changed_cb (GdkScreen *screen, EomWindow *window)
+{
+ eom_window_update_fullscreen_popup (window);
+}
+
+static void
+fullscreen_popup_size_request_cb (GtkWidget *popup,
+ GtkRequisition *req,
+ EomWindow *window)
+{
+ eom_window_update_fullscreen_popup (window);
+}
+
+static gboolean
+fullscreen_timeout_cb (gpointer data)
+{
+ EomWindow *window = EOM_WINDOW (data);
+
+ gtk_widget_hide_all (window->priv->fullscreen_popup);
+
+ eom_scroll_view_hide_cursor (EOM_SCROLL_VIEW (window->priv->view));
+
+ fullscreen_clear_timeout (window);
+
+ return FALSE;
+}
+
+static gboolean
+slideshow_is_loop_end (EomWindow *window)
+{
+ EomWindowPrivate *priv = window->priv;
+ EomImage *image = NULL;
+ gint pos;
+
+ image = eom_thumb_view_get_first_selected_image (EOM_THUMB_VIEW (priv->thumbview));
+
+ pos = eom_list_store_get_pos_by_image (priv->store, image);
+
+ return (pos == (eom_list_store_length (priv->store) - 1));
+}
+
+static gboolean
+slideshow_switch_cb (gpointer data)
+{
+ EomWindow *window = EOM_WINDOW (data);
+ EomWindowPrivate *priv = window->priv;
+
+ eom_debug (DEBUG_WINDOW);
+
+ if (!priv->slideshow_loop && slideshow_is_loop_end (window)) {
+ eom_window_stop_fullscreen (window, TRUE);
+ return FALSE;
+ }
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_RIGHT);
+
+ return TRUE;
+}
+
+static void
+fullscreen_clear_timeout (EomWindow *window)
+{
+ eom_debug (DEBUG_WINDOW);
+
+ if (window->priv->fullscreen_timeout_source != NULL) {
+ g_source_unref (window->priv->fullscreen_timeout_source);
+ g_source_destroy (window->priv->fullscreen_timeout_source);
+ }
+
+ window->priv->fullscreen_timeout_source = NULL;
+}
+
+static void
+fullscreen_set_timeout (EomWindow *window)
+{
+ GSource *source;
+
+ eom_debug (DEBUG_WINDOW);
+
+ fullscreen_clear_timeout (window);
+
+ source = g_timeout_source_new (EOM_WINDOW_FULLSCREEN_TIMEOUT);
+ g_source_set_callback (source, fullscreen_timeout_cb, window, NULL);
+
+ g_source_attach (source, NULL);
+
+ window->priv->fullscreen_timeout_source = source;
+
+ eom_scroll_view_show_cursor (EOM_SCROLL_VIEW (window->priv->view));
+}
+
+static void
+slideshow_clear_timeout (EomWindow *window)
+{
+ eom_debug (DEBUG_WINDOW);
+
+ if (window->priv->slideshow_switch_source != NULL) {
+ g_source_unref (window->priv->slideshow_switch_source);
+ g_source_destroy (window->priv->slideshow_switch_source);
+ }
+
+ window->priv->slideshow_switch_source = NULL;
+}
+
+static void
+slideshow_set_timeout (EomWindow *window)
+{
+ GSource *source;
+
+ eom_debug (DEBUG_WINDOW);
+
+ slideshow_clear_timeout (window);
+
+ if (window->priv->slideshow_switch_timeout <= 0)
+ return;
+
+ source = g_timeout_source_new (window->priv->slideshow_switch_timeout * 1000);
+ g_source_set_callback (source, slideshow_switch_cb, window, NULL);
+
+ g_source_attach (source, NULL);
+
+ window->priv->slideshow_switch_source = source;
+}
+
+static void
+show_fullscreen_popup (EomWindow *window)
+{
+ eom_debug (DEBUG_WINDOW);
+
+ if (!gtk_widget_get_visible (window->priv->fullscreen_popup)) {
+ gtk_widget_show_all (GTK_WIDGET (window->priv->fullscreen_popup));
+ }
+
+ fullscreen_set_timeout (window);
+}
+
+static gboolean
+fullscreen_motion_notify_cb (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ eom_debug (DEBUG_WINDOW);
+
+ if (event->y < EOM_WINDOW_FULLSCREEN_POPUP_THRESHOLD) {
+ show_fullscreen_popup (window);
+ } else {
+ fullscreen_set_timeout (window);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+fullscreen_leave_notify_cb (GtkWidget *widget,
+ GdkEventCrossing *event,
+ gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ eom_debug (DEBUG_WINDOW);
+
+ fullscreen_clear_timeout (window);
+
+ return FALSE;
+}
+
+static void
+exit_fullscreen_button_clicked_cb (GtkWidget *button, EomWindow *window)
+{
+ GtkAction *action;
+
+ eom_debug (DEBUG_WINDOW);
+
+ if (window->priv->mode == EOM_WINDOW_MODE_SLIDESHOW) {
+ action = gtk_action_group_get_action (window->priv->actions_collection,
+ "ViewSlideshow");
+ } else {
+ action = gtk_action_group_get_action (window->priv->actions_image,
+ "ViewFullscreen");
+ }
+ g_return_if_fail (action != NULL);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+}
+
+static GtkWidget *
+eom_window_get_exit_fullscreen_button (EomWindow *window)
+{
+ GtkWidget *button;
+
+ button = gtk_button_new_from_stock (GTK_STOCK_LEAVE_FULLSCREEN);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (exit_fullscreen_button_clicked_cb),
+ window);
+
+ return button;
+}
+
+static GtkWidget *
+eom_window_create_fullscreen_popup (EomWindow *window)
+{
+ GtkWidget *popup;
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *toolbar;
+ GdkScreen *screen;
+
+ eom_debug (DEBUG_WINDOW);
+
+ popup = gtk_window_new (GTK_WINDOW_POPUP);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (popup), hbox);
+
+ toolbar = gtk_ui_manager_get_widget (window->priv->ui_mgr,
+ "/FullscreenToolbar");
+ g_assert (GTK_IS_WIDGET (toolbar));
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_box_pack_start (GTK_BOX (hbox), toolbar, TRUE, TRUE, 0);
+
+ button = eom_window_get_exit_fullscreen_button (window);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (window));
+
+ g_signal_connect_object (screen, "size-changed",
+ G_CALLBACK (screen_size_changed_cb),
+ window, 0);
+
+ g_signal_connect_object (popup, "size_request",
+ G_CALLBACK (fullscreen_popup_size_request_cb),
+ window, 0);
+
+ g_signal_connect (popup,
+ "enter-notify-event",
+ G_CALLBACK (fullscreen_leave_notify_cb),
+ window);
+
+ gtk_window_set_screen (GTK_WINDOW (popup), screen);
+
+ return popup;
+}
+
+static void
+update_ui_visibility (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+
+ GtkAction *action;
+ GtkWidget *menubar;
+ GError *error = NULL;
+
+ gboolean fullscreen_mode, visible;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = window->priv;
+
+ fullscreen_mode = priv->mode == EOM_WINDOW_MODE_FULLSCREEN ||
+ priv->mode == EOM_WINDOW_MODE_SLIDESHOW;
+
+ menubar = gtk_ui_manager_get_widget (priv->ui_mgr, "/MainMenu");
+ g_assert (GTK_IS_WIDGET (menubar));
+
+ visible = mateconf_client_get_bool (priv->client, EOM_CONF_UI_TOOLBAR, &error);
+ visible = visible && !fullscreen_mode;
+ if (error) {
+ g_error_free (error);
+ error = NULL;
+ visible = !fullscreen_mode;
+ }
+ action = gtk_ui_manager_get_action (priv->ui_mgr, "/MainMenu/View/ToolbarToggle");
+ g_assert (action != NULL);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+ g_object_set (G_OBJECT (priv->toolbar), "visible", visible, NULL);
+
+ visible = mateconf_client_get_bool (priv->client, EOM_CONF_UI_STATUSBAR, &error);
+ visible = visible && !fullscreen_mode;
+ if (error) {
+ g_error_free (error);
+ error = NULL;
+ visible = !fullscreen_mode;
+ }
+ action = gtk_ui_manager_get_action (priv->ui_mgr, "/MainMenu/View/StatusbarToggle");
+ g_assert (action != NULL);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+ g_object_set (G_OBJECT (priv->statusbar), "visible", visible, NULL);
+
+ if (priv->status != EOM_WINDOW_STATUS_INIT) {
+ visible = mateconf_client_get_bool (priv->client, EOM_CONF_UI_IMAGE_COLLECTION, NULL);
+ visible = visible && priv->mode != EOM_WINDOW_MODE_SLIDESHOW;
+ action = gtk_ui_manager_get_action (priv->ui_mgr, "/MainMenu/View/ImageCollectionToggle");
+ g_assert (action != NULL);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+ if (visible) {
+ gtk_widget_show (priv->nav);
+ } else {
+ gtk_widget_hide (priv->nav);
+ }
+ }
+
+ visible = mateconf_client_get_bool (priv->client, EOM_CONF_UI_SIDEBAR, NULL);
+ visible = visible && !fullscreen_mode;
+ action = gtk_ui_manager_get_action (priv->ui_mgr, "/MainMenu/View/SidebarToggle");
+ g_assert (action != NULL);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+ if (visible) {
+ gtk_widget_show (priv->sidebar);
+ } else {
+ gtk_widget_hide (priv->sidebar);
+ }
+
+ if (priv->fullscreen_popup != NULL) {
+ gtk_widget_hide_all (priv->fullscreen_popup);
+ }
+}
+
+static void
+eom_window_run_fullscreen (EomWindow *window, gboolean slideshow)
+{
+ EomWindowPrivate *priv;
+ GtkWidget *menubar;
+ gboolean upscale;
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = window->priv;
+
+ if (slideshow) {
+ priv->mode = EOM_WINDOW_MODE_SLIDESHOW;
+ } else {
+ /* Stop the timer if we come from slideshowing */
+ if (priv->mode == EOM_WINDOW_MODE_SLIDESHOW)
+ slideshow_clear_timeout (window);
+
+ priv->mode = EOM_WINDOW_MODE_FULLSCREEN;
+ }
+
+ if (window->priv->fullscreen_popup == NULL)
+ priv->fullscreen_popup
+ = eom_window_create_fullscreen_popup (window);
+
+ update_ui_visibility (window);
+
+ menubar = gtk_ui_manager_get_widget (priv->ui_mgr, "/MainMenu");
+ g_assert (GTK_IS_WIDGET (menubar));
+ gtk_widget_hide (menubar);
+
+ g_signal_connect (priv->view,
+ "motion-notify-event",
+ G_CALLBACK (fullscreen_motion_notify_cb),
+ window);
+
+ g_signal_connect (priv->view,
+ "leave-notify-event",
+ G_CALLBACK (fullscreen_leave_notify_cb),
+ window);
+
+ g_signal_connect (priv->thumbview,
+ "motion-notify-event",
+ G_CALLBACK (fullscreen_motion_notify_cb),
+ window);
+
+ g_signal_connect (priv->thumbview,
+ "leave-notify-event",
+ G_CALLBACK (fullscreen_leave_notify_cb),
+ window);
+
+ fullscreen_set_timeout (window);
+
+ if (slideshow) {
+ priv->slideshow_loop =
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_FULLSCREEN_LOOP,
+ NULL);
+
+ priv->slideshow_switch_timeout =
+ mateconf_client_get_int (priv->client,
+ EOM_CONF_FULLSCREEN_SECONDS,
+ NULL);
+
+ slideshow_set_timeout (window);
+ }
+
+ upscale = mateconf_client_get_bool (priv->client,
+ EOM_CONF_FULLSCREEN_UPSCALE,
+ NULL);
+
+ eom_scroll_view_set_zoom_upscale (EOM_SCROLL_VIEW (priv->view),
+ upscale);
+
+ gtk_widget_grab_focus (priv->view);
+
+ eom_scroll_view_override_bg_color (EOM_SCROLL_VIEW (window->priv->view),
+ &(gtk_widget_get_style (GTK_WIDGET (window))->black));
+
+ {
+ GtkStyle *style;
+
+ style = gtk_style_copy (gtk_widget_get_style (gtk_widget_get_parent (priv->view)));
+
+ style->xthickness = 0;
+ style->ythickness = 0;
+
+ gtk_widget_set_style (gtk_widget_get_parent (priv->view),
+ style);
+
+ g_object_unref (style);
+ }
+
+ gtk_window_fullscreen (GTK_WINDOW (window));
+ eom_window_update_fullscreen_popup (window);
+
+#ifdef HAVE_DBUS
+ eom_application_screensaver_disable (EOM_APP);
+#endif
+
+ /* Update both actions as we could've already been in one those modes */
+ eom_window_update_slideshow_action (window);
+ eom_window_update_fullscreen_action (window);
+ eom_window_update_pause_slideshow_action (window);
+}
+
+static void
+eom_window_stop_fullscreen (EomWindow *window, gboolean slideshow)
+{
+ EomWindowPrivate *priv;
+ GtkWidget *menubar;
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = window->priv;
+
+ if (priv->mode != EOM_WINDOW_MODE_SLIDESHOW &&
+ priv->mode != EOM_WINDOW_MODE_FULLSCREEN) return;
+
+ priv->mode = EOM_WINDOW_MODE_NORMAL;
+
+ fullscreen_clear_timeout (window);
+
+ if (slideshow) {
+ slideshow_clear_timeout (window);
+ }
+
+ g_signal_handlers_disconnect_by_func (priv->view,
+ (gpointer) fullscreen_motion_notify_cb,
+ window);
+
+ g_signal_handlers_disconnect_by_func (priv->view,
+ (gpointer) fullscreen_leave_notify_cb,
+ window);
+
+ g_signal_handlers_disconnect_by_func (priv->thumbview,
+ (gpointer) fullscreen_motion_notify_cb,
+ window);
+
+ g_signal_handlers_disconnect_by_func (priv->thumbview,
+ (gpointer) fullscreen_leave_notify_cb,
+ window);
+
+ update_ui_visibility (window);
+
+ menubar = gtk_ui_manager_get_widget (priv->ui_mgr, "/MainMenu");
+ g_assert (GTK_IS_WIDGET (menubar));
+ gtk_widget_show (menubar);
+
+ eom_scroll_view_set_zoom_upscale (EOM_SCROLL_VIEW (priv->view), FALSE);
+
+ eom_scroll_view_override_bg_color (EOM_SCROLL_VIEW (window->priv->view),
+ NULL);
+ gtk_widget_set_style (gtk_widget_get_parent (window->priv->view), NULL);
+ gtk_window_unfullscreen (GTK_WINDOW (window));
+
+ if (slideshow) {
+ eom_window_update_slideshow_action (window);
+ } else {
+ eom_window_update_fullscreen_action (window);
+ }
+
+ eom_scroll_view_show_cursor (EOM_SCROLL_VIEW (priv->view));
+
+#ifdef HAVE_DBUS
+ eom_application_screensaver_enable (EOM_APP);
+#endif
+}
+
+static void
+eom_window_print (EomWindow *window)
+{
+ GtkWidget *dialog;
+ GError *error = NULL;
+ GtkPrintOperation *print;
+ GtkPrintOperationResult res;
+ GtkPageSetup *page_setup;
+ GtkPrintSettings *print_settings;
+
+ eom_debug (DEBUG_PRINTING);
+
+ print_settings = eom_print_get_print_settings ();
+ page_setup = eom_print_get_page_setup ();
+
+ /* Make sure the window stays valid while printing */
+ g_object_ref (window);
+
+ print = eom_print_operation_new (window->priv->image,
+ print_settings,
+ page_setup);
+
+ res = gtk_print_operation_run (print,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ GTK_WINDOW (window), &error);
+
+ if (res == GTK_PRINT_OPERATION_RESULT_ERROR) {
+ dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Error printing file:\n%s"),
+ error->message);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_widget_show (dialog);
+ g_error_free (error);
+ } else if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
+ eom_print_set_print_settings (gtk_print_operation_get_print_settings (print));
+ eom_print_set_page_setup (gtk_print_operation_get_default_page_setup (print));
+ }
+
+ g_object_unref (page_setup);
+ g_object_unref (print_settings);
+ g_object_unref (window);
+}
+
+static void
+eom_window_cmd_file_open (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+ EomImage *current;
+ GtkWidget *dlg;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+
+ priv = window->priv;
+
+ dlg = eom_file_chooser_new (GTK_FILE_CHOOSER_ACTION_OPEN);
+
+ current = eom_thumb_view_get_first_selected_image (EOM_THUMB_VIEW (priv->thumbview));
+
+ if (current != NULL) {
+ gchar *dir_uri, *file_uri;
+
+ file_uri = eom_image_get_uri_for_display (current);
+ dir_uri = g_path_get_dirname (file_uri);
+
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg),
+ dir_uri);
+ g_free (file_uri);
+ g_free (dir_uri);
+ g_object_unref (current);
+ } else {
+ /* If desired by the user,
+ fallback to the XDG_PICTURES_DIR (if available) */
+ const gchar *pics_dir;
+ gboolean use_fallback;
+
+ use_fallback = mateconf_client_get_bool (priv->client,
+ EOM_CONF_UI_FILECHOOSER_XDG_FALLBACK,
+ NULL);
+ pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
+ if (use_fallback && pics_dir) {
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg),
+ pics_dir);
+ }
+ }
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (file_open_dialog_response_cb),
+ window);
+
+ gtk_widget_show_all (dlg);
+}
+
+static void
+eom_job_close_save_cb (EomJobSave *job, gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ g_signal_handlers_disconnect_by_func (job,
+ eom_job_close_save_cb,
+ window);
+
+ gtk_widget_destroy (GTK_WIDGET (window));
+}
+
+static void
+close_confirmation_dialog_response_handler (EomCloseConfirmationDialog *dlg,
+ gint response_id,
+ EomWindow *window)
+{
+ GList *selected_images;
+ EomWindowPrivate *priv;
+
+ priv = window->priv;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_YES:
+ /* save selected images */
+ selected_images = eom_close_confirmation_dialog_get_selected_images (dlg);
+ eom_close_confirmation_dialog_set_sensitive (dlg, FALSE);
+ if (eom_window_save_images (window, selected_images)) {
+ g_signal_connect (priv->save_job,
+ "finished",
+ G_CALLBACK (eom_job_close_save_cb),
+ window);
+
+ eom_job_queue_add_job (priv->save_job);
+ }
+
+ break;
+
+ case GTK_RESPONSE_NO:
+ /* dont save */
+ gtk_widget_destroy (GTK_WIDGET (window));
+ break;
+
+ default:
+ /* Cancel */
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+ break;
+ }
+}
+
+static gboolean
+eom_window_unsaved_images_confirm (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ GtkWidget *dialog;
+ GList *list;
+ EomImage *image;
+ GtkTreeIter iter;
+
+ priv = window->priv;
+
+ if (window->priv->save_disabled) {
+ return FALSE;
+ }
+
+ list = NULL;
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter)) {
+ do {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+ if (!image)
+ continue;
+
+ if (eom_image_is_modified (image)) {
+ list = g_list_append (list, image);
+ }
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter));
+ }
+
+ if (list) {
+ dialog = eom_close_confirmation_dialog_new (GTK_WINDOW (window),
+ list);
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (close_confirmation_dialog_response_handler),
+ window);
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
+
+ gtk_widget_show (dialog);
+ return TRUE;
+
+ }
+ return FALSE;
+}
+
+static void
+eom_window_cmd_close_window (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+
+ if (priv->save_job != NULL) {
+ eom_window_finish_saving (window);
+ }
+
+ if (!eom_window_unsaved_images_confirm (window)) {
+ gtk_widget_destroy (GTK_WIDGET (user_data));
+ }
+}
+
+static void
+eom_window_cmd_preferences (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ GObject *pref_dlg;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+
+ pref_dlg = eom_preferences_dialog_get_instance (GTK_WINDOW (window),
+ window->priv->client);
+
+ eom_dialog_show (EOM_DIALOG (pref_dlg));
+}
+
+#define EOM_TB_EDITOR_DLG_RESET_RESPONSE 128
+
+static void
+eom_window_cmd_edit_toolbar_cb (GtkDialog *dialog, gint response, gpointer data)
+{
+ EomWindow *window = EOM_WINDOW (data);
+
+ if (response == EOM_TB_EDITOR_DLG_RESET_RESPONSE) {
+ EggToolbarsModel *model;
+ EggToolbarEditor *editor;
+
+ editor = g_object_get_data (G_OBJECT (dialog),
+ "EggToolbarEditor");
+
+ g_return_if_fail (editor != NULL);
+
+ egg_editable_toolbar_set_edit_mode
+ (EGG_EDITABLE_TOOLBAR (window->priv->toolbar), FALSE);
+
+ eom_application_reset_toolbars_model (EOM_APP);
+ model = eom_application_get_toolbars_model (EOM_APP);
+ egg_editable_toolbar_set_model
+ (EGG_EDITABLE_TOOLBAR (window->priv->toolbar), model);
+ egg_toolbar_editor_set_model (editor, model);
+
+ /* Toolbar would be uneditable now otherwise */
+ egg_editable_toolbar_set_edit_mode
+ (EGG_EDITABLE_TOOLBAR (window->priv->toolbar), TRUE);
+ } else if (response == GTK_RESPONSE_HELP) {
+ eom_util_show_help ("eom-toolbareditor", NULL);
+ } else {
+ egg_editable_toolbar_set_edit_mode
+ (EGG_EDITABLE_TOOLBAR (window->priv->toolbar), FALSE);
+
+ eom_application_save_toolbars_model (EOM_APP);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ }
+}
+
+static void
+eom_window_cmd_edit_toolbar (GtkAction *action, gpointer *user_data)
+{
+ EomWindow *window;
+ GtkWidget *dialog;
+ GtkWidget *editor;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+
+ dialog = gtk_dialog_new_with_buttons (_("Toolbar Editor"),
+ GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Reset to Default"),
+ EOM_TB_EDITOR_DLG_RESET_RESPONSE,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_HELP,
+ GTK_RESPONSE_HELP,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_CLOSE);
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 400);
+
+ editor = egg_toolbar_editor_new (window->priv->ui_mgr,
+ eom_application_get_toolbars_model (EOM_APP));
+
+ gtk_container_set_border_width (GTK_CONTAINER (editor), 5);
+
+ gtk_box_set_spacing (GTK_BOX (EGG_TOOLBAR_EDITOR (editor)), 5);
+
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), editor);
+
+ egg_editable_toolbar_set_edit_mode
+ (EGG_EDITABLE_TOOLBAR (window->priv->toolbar), TRUE);
+
+ g_object_set_data (G_OBJECT (dialog), "EggToolbarEditor", editor);
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (eom_window_cmd_edit_toolbar_cb),
+ window);
+
+ gtk_widget_show_all (dialog);
+}
+
+static void
+eom_window_cmd_help (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+
+ eom_util_show_help (NULL, GTK_WINDOW (window));
+}
+
+static void
+eom_window_cmd_about (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ static const char *authors[] = {
+ "Claudio Saavedra <[email protected]> (maintainer)",
+ "Felix Riemann <[email protected]> (maintainer)",
+ "",
+ "Lucas Rocha <[email protected]>",
+ "Tim Gerla <[email protected]>",
+ "Philip Van Hoof <[email protected]>",
+ "Paolo Borelli <[email protected]>",
+ "Jens Finke <[email protected]>",
+ "Martin Baulig <[email protected]>",
+ "Arik Devens <[email protected]>",
+ "Michael Meeks <[email protected]>",
+ "Federico Mena-Quintero <[email protected]>",
+ "Lutz M\xc3\xbcller <[email protected]>",
+ NULL
+ };
+
+ static const char *documenters[] = {
+ "Eliot Landrum <[email protected]>",
+ "Federico Mena-Quintero <[email protected]>",
+ "Sun MATE Documentation Team <[email protected]>",
+ NULL
+ };
+
+ const char *translators;
+
+ translators = _("translator-credits");
+
+ const char *license[] = {
+ N_("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.\n"),
+ N_("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.\n"),
+ N_("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.")
+ };
+
+ char *license_trans;
+
+ license_trans = g_strconcat (_(license[0]), "\n", _(license[1]), "\n",
+ _(license[2]), "\n", NULL);
+
+ window = EOM_WINDOW (user_data);
+
+ gtk_show_about_dialog (GTK_WINDOW (window),
+ "program-name", _("Eye of MATE"),
+ "version", VERSION,
+ "copyright", "Copyright \xc2\xa9 2000-2010 Free Software Foundation, Inc.",
+ "comments",_("The MATE image viewer."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", translators,
+ "website", "http://projects.gnome.org/eom/",
+ "logo-icon-name", "eom",
+ "wrap-license", TRUE,
+ "license", license_trans,
+ NULL);
+
+ g_free (license_trans);
+}
+
+static void
+eom_window_cmd_show_hide_bar (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+ gboolean visible;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+
+ if (priv->mode != EOM_WINDOW_MODE_NORMAL &&
+ priv->mode != EOM_WINDOW_MODE_FULLSCREEN) return;
+
+ visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+ if (g_ascii_strcasecmp (gtk_action_get_name (action), "ViewToolbar") == 0) {
+ g_object_set (G_OBJECT (priv->toolbar), "visible", visible, NULL);
+
+ if (priv->mode == EOM_WINDOW_MODE_NORMAL)
+ mateconf_client_set_bool (priv->client, EOM_CONF_UI_TOOLBAR, visible, NULL);
+
+ } else if (g_ascii_strcasecmp (gtk_action_get_name (action), "ViewStatusbar") == 0) {
+ g_object_set (G_OBJECT (priv->statusbar), "visible", visible, NULL);
+
+ if (priv->mode == EOM_WINDOW_MODE_NORMAL)
+ mateconf_client_set_bool (priv->client, EOM_CONF_UI_STATUSBAR, visible, NULL);
+
+ } else if (g_ascii_strcasecmp (gtk_action_get_name (action), "ViewImageCollection") == 0) {
+ if (visible) {
+ /* Make sure the focus widget is realized to
+ * avoid warnings on keypress events */
+#if GTK_CHECK_VERSION (2, 20, 0)
+ if (!gtk_widget_get_realized (window->priv->thumbview))
+#else
+ if (!GTK_WIDGET_REALIZED (window->priv->thumbview))
+#endif
+ gtk_widget_realize (window->priv->thumbview);
+
+ gtk_widget_show (priv->nav);
+ gtk_widget_grab_focus (priv->thumbview);
+ } else {
+ /* Make sure the focus widget is realized to
+ * avoid warnings on keypress events.
+ * Don't do it during init phase or the view
+ * will get a bogus allocation. */
+#if GTK_CHECK_VERSION (2, 20, 0)
+ if (!gtk_widget_get_realized (priv->view)
+#else
+ if (!GTK_WIDGET_REALIZED (priv->view)
+#endif
+ && priv->status == EOM_WINDOW_STATUS_NORMAL)
+ gtk_widget_realize (priv->view);
+
+ gtk_widget_hide (priv->nav);
+
+#if GTK_CHECK_VERSION (2, 20, 0)
+ if (gtk_widget_get_realized (priv->view))
+#else
+ if (GTK_WIDGET_REALIZED (priv->view))
+#endif
+ gtk_widget_grab_focus (priv->view);
+ }
+ mateconf_client_set_bool (priv->client, EOM_CONF_UI_IMAGE_COLLECTION, visible, NULL);
+
+ } else if (g_ascii_strcasecmp (gtk_action_get_name (action), "ViewSidebar") == 0) {
+ if (visible) {
+ gtk_widget_show (priv->sidebar);
+ } else {
+ gtk_widget_hide (priv->sidebar);
+ }
+ mateconf_client_set_bool (priv->client, EOM_CONF_UI_SIDEBAR, visible, NULL);
+ }
+}
+
+static void
+wallpaper_info_bar_response (GtkInfoBar *bar, gint response, EomWindow *window)
+{
+ if (response == GTK_RESPONSE_YES) {
+ GdkScreen *screen;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (window));
+ gdk_spawn_command_line_on_screen (screen,
+ "mate-appearance-properties"
+ " --show-page=background",
+ NULL);
+ }
+
+ /* Close message area on every response */
+ eom_window_set_message_area (window, NULL);
+}
+
+static void
+eom_window_set_wallpaper (EomWindow *window, const gchar *filename, const gchar *visible_filename)
+{
+ EomWindowPrivate *priv = EOM_WINDOW_GET_PRIVATE (window);
+ GtkWidget *info_bar;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ gchar *markup;
+ gchar *text;
+ gchar *basename;
+
+
+ mateconf_client_set_string (priv->client,
+ EOM_CONF_DESKTOP_WALLPAPER,
+ filename,
+ NULL);
+
+ /* I18N: When setting mnemonics for these strings, watch out to not
+ clash with mnemonics from eom's menubar */
+ info_bar = gtk_info_bar_new_with_buttons (_("_Open Background Preferences"),
+ GTK_RESPONSE_YES,
+ C_("MessageArea","Hi_de"),
+ GTK_RESPONSE_NO, NULL);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
+ GTK_MESSAGE_QUESTION);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION,
+ GTK_ICON_SIZE_DIALOG);
+ label = gtk_label_new (NULL);
+
+ if (!visible_filename)
+ basename = g_path_get_basename (filename);
+
+ /* The newline character is currently necessary due to a problem
+ * with the automatic line break. */
+ text = g_strdup_printf (_("The image \"%s\" has been set as Desktop Background."
+ "\nWould you like to modify its appearance?"),
+ visible_filename ? visible_filename : basename);
+ markup = g_markup_printf_escaped ("<b>%s</b>", text);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+ g_free (text);
+ if (!visible_filename)
+ g_free (basename);
+
+ hbox = gtk_hbox_new (FALSE, 8);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))), hbox, TRUE, TRUE, 0);
+ gtk_widget_show_all (hbox);
+ gtk_widget_show (info_bar);
+
+
+ eom_window_set_message_area (window, info_bar);
+ gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar),
+ GTK_RESPONSE_YES);
+ g_signal_connect (info_bar, "response",
+ G_CALLBACK (wallpaper_info_bar_response), window);
+}
+
+static void
+eom_job_save_cb (EomJobSave *job, gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+ GtkAction *action_save;
+
+ g_signal_handlers_disconnect_by_func (job,
+ eom_job_save_cb,
+ window);
+
+ g_signal_handlers_disconnect_by_func (job,
+ eom_job_save_progress_cb,
+ window);
+
+ g_object_unref (window->priv->save_job);
+ window->priv->save_job = NULL;
+
+ update_status_bar (window);
+ action_save = gtk_action_group_get_action (window->priv->actions_image,
+ "ImageSave");
+ gtk_action_set_sensitive (action_save, FALSE);
+}
+
+static void
+eom_job_copy_cb (EomJobCopy *job, gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+ gchar *filepath, *basename, *filename, *extension;
+ GtkAction *action;
+ GFile *source_file, *dest_file;
+
+ /* Create source GFile */
+ basename = g_file_get_basename (job->images->data);
+ filepath = g_build_filename (job->dest, basename, NULL);
+ source_file = g_file_new_for_path (filepath);
+ g_free (filepath);
+
+ /* Create destination GFile */
+ extension = eom_util_filename_get_extension (basename);
+ filename = g_strdup_printf ("%s.%s", EOM_WALLPAPER_FILENAME, extension);
+ filepath = g_build_filename (job->dest, filename, NULL);
+ dest_file = g_file_new_for_path (filepath);
+ g_free (filename);
+ g_free (extension);
+
+ /* Move the file */
+ g_file_move (source_file, dest_file, G_FILE_COPY_OVERWRITE,
+ NULL, NULL, NULL, NULL);
+
+ /* Set the wallpaper */
+ eom_window_set_wallpaper (window, filepath, basename);
+ g_free (basename);
+ g_free (filepath);
+
+ gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
+ window->priv->copy_file_cid);
+ action = gtk_action_group_get_action (window->priv->actions_image,
+ "ImageSetAsWallpaper");
+ gtk_action_set_sensitive (action, TRUE);
+
+ window->priv->copy_job = NULL;
+
+ g_object_unref (source_file);
+ g_object_unref (dest_file);
+ g_object_unref (G_OBJECT (job->images->data));
+ g_list_free (job->images);
+ g_object_unref (job);
+}
+
+static gboolean
+eom_window_save_images (EomWindow *window, GList *images)
+{
+ EomWindowPrivate *priv;
+
+ priv = window->priv;
+
+ if (window->priv->save_job != NULL)
+ return FALSE;
+
+ priv->save_job = eom_job_save_new (images);
+
+ g_signal_connect (priv->save_job,
+ "finished",
+ G_CALLBACK (eom_job_save_cb),
+ window);
+
+ g_signal_connect (priv->save_job,
+ "progress",
+ G_CALLBACK (eom_job_save_progress_cb),
+ window);
+
+ return TRUE;
+}
+
+static void
+eom_window_cmd_save (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ EomWindow *window;
+ GList *images;
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+
+ if (window->priv->save_job != NULL)
+ return;
+
+ images = eom_thumb_view_get_selected_images (EOM_THUMB_VIEW (priv->thumbview));
+
+ if (eom_window_save_images (window, images)) {
+ eom_job_queue_add_job (priv->save_job);
+ }
+}
+
+static GFile*
+eom_window_retrieve_save_as_file (EomWindow *window, EomImage *image)
+{
+ GtkWidget *dialog;
+ GFile *save_file = NULL;
+ GFile *last_dest_folder;
+ gint response;
+
+ g_assert (image != NULL);
+
+ dialog = eom_file_chooser_new (GTK_FILE_CHOOSER_ACTION_SAVE);
+
+ last_dest_folder = window->priv->last_save_as_folder;
+
+ if (last_dest_folder && g_file_query_exists (last_dest_folder, NULL)) {
+ gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), last_dest_folder, NULL);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
+ eom_image_get_caption (image));
+ } else {
+ GFile *image_file;
+
+ image_file = eom_image_get_file (image);
+ /* Setting the file will also navigate to its parent folder */
+ gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog),
+ image_file, NULL);
+ g_object_unref (image_file);
+ }
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_hide (dialog);
+
+ if (response == GTK_RESPONSE_OK) {
+ save_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+ if (window->priv->last_save_as_folder)
+ g_object_unref (window->priv->last_save_as_folder);
+ window->priv->last_save_as_folder = g_file_get_parent (save_file);
+ }
+ gtk_widget_destroy (dialog);
+
+ return save_file;
+}
+
+static void
+eom_window_cmd_save_as (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+ EomWindow *window;
+ GList *images;
+ guint n_images;
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+
+ if (window->priv->save_job != NULL)
+ return;
+
+ images = eom_thumb_view_get_selected_images (EOM_THUMB_VIEW (priv->thumbview));
+ n_images = g_list_length (images);
+
+ if (n_images == 1) {
+ GFile *file;
+
+ file = eom_window_retrieve_save_as_file (window, images->data);
+
+ if (!file) {
+ g_list_free (images);
+ return;
+ }
+
+ priv->save_job = eom_job_save_as_new (images, NULL, file);
+
+ g_object_unref (file);
+ } else if (n_images > 1) {
+ GFile *base_file;
+ GtkWidget *dialog;
+ gchar *basedir;
+ EomURIConverter *converter;
+
+ basedir = g_get_current_dir ();
+ base_file = g_file_new_for_path (basedir);
+ g_free (basedir);
+
+ dialog = eom_save_as_dialog_new (GTK_WINDOW (window),
+ images,
+ base_file);
+
+ gtk_widget_show_all (dialog);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) {
+ g_object_unref (base_file);
+ g_list_free (images);
+ gtk_widget_destroy (dialog);
+
+ return;
+ }
+
+ converter = eom_save_as_dialog_get_converter (dialog);
+
+ g_assert (converter != NULL);
+
+ priv->save_job = eom_job_save_as_new (images, converter, NULL);
+
+ gtk_widget_destroy (dialog);
+
+ g_object_unref (converter);
+ g_object_unref (base_file);
+ } else {
+ /* n_images = 0 -- No Image selected */
+ return;
+ }
+
+ g_signal_connect (priv->save_job,
+ "finished",
+ G_CALLBACK (eom_job_save_cb),
+ window);
+
+ g_signal_connect (priv->save_job,
+ "progress",
+ G_CALLBACK (eom_job_save_progress_cb),
+ window);
+
+ eom_job_queue_add_job (priv->save_job);
+}
+
+static void
+eom_window_cmd_print (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ eom_window_print (window);
+}
+
+static void
+eom_window_cmd_properties (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+ EomWindowPrivate *priv;
+ GtkAction *next_image_action, *previous_image_action;
+
+ priv = window->priv;
+
+ next_image_action =
+ gtk_action_group_get_action (priv->actions_collection,
+ "GoNext");
+
+ previous_image_action =
+ gtk_action_group_get_action (priv->actions_collection,
+ "GoPrevious");
+
+ if (window->priv->properties_dlg == NULL) {
+ gboolean netbook_mode;
+
+ window->priv->properties_dlg =
+ eom_properties_dialog_new (GTK_WINDOW (window),
+ EOM_THUMB_VIEW (priv->thumbview),
+ next_image_action,
+ previous_image_action);
+
+ eom_properties_dialog_update (EOM_PROPERTIES_DIALOG (priv->properties_dlg),
+ priv->image);
+ netbook_mode =
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_UI_PROPSDIALOG_NETBOOK_MODE,
+ NULL);
+ eom_properties_dialog_set_netbook_mode (EOM_PROPERTIES_DIALOG (priv->properties_dlg),
+ netbook_mode);
+ }
+
+ eom_dialog_show (EOM_DIALOG (window->priv->properties_dlg));
+}
+
+static void
+eom_window_cmd_undo (GtkAction *action, gpointer user_data)
+{
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ apply_transformation (EOM_WINDOW (user_data), NULL);
+}
+
+static void
+eom_window_cmd_flip_horizontal (GtkAction *action, gpointer user_data)
+{
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ apply_transformation (EOM_WINDOW (user_data),
+ eom_transform_flip_new (EOM_TRANSFORM_FLIP_HORIZONTAL));
+}
+
+static void
+eom_window_cmd_flip_vertical (GtkAction *action, gpointer user_data)
+{
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ apply_transformation (EOM_WINDOW (user_data),
+ eom_transform_flip_new (EOM_TRANSFORM_FLIP_VERTICAL));
+}
+
+static void
+eom_window_cmd_rotate_90 (GtkAction *action, gpointer user_data)
+{
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ apply_transformation (EOM_WINDOW (user_data),
+ eom_transform_rotate_new (90));
+}
+
+static void
+eom_window_cmd_rotate_270 (GtkAction *action, gpointer user_data)
+{
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ apply_transformation (EOM_WINDOW (user_data),
+ eom_transform_rotate_new (270));
+}
+
+static void
+eom_window_cmd_wallpaper (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+ EomImage *image;
+ GFile *file;
+ char *filename = NULL;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+
+ /* If currently copying an image to set it as wallpaper, return. */
+ if (priv->copy_job != NULL)
+ return;
+
+ image = eom_thumb_view_get_first_selected_image (EOM_THUMB_VIEW (priv->thumbview));
+
+ g_return_if_fail (EOM_IS_IMAGE (image));
+
+ file = eom_image_get_file (image);
+
+ filename = g_file_get_path (file);
+
+ /* Currently only local files can be set as wallpaper */
+ if (filename == NULL || !eom_util_file_is_persistent (file))
+ {
+ GList *files = NULL;
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->actions_image,
+ "ImageSetAsWallpaper");
+ gtk_action_set_sensitive (action, FALSE);
+
+ priv->copy_file_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
+ "copy_file_cid");
+ gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar),
+ priv->copy_file_cid,
+ _("Saving image locally…"));
+
+ files = g_list_append (files, eom_image_get_file (image));
+ priv->copy_job = eom_job_copy_new (files, g_get_user_data_dir ());
+ g_signal_connect (priv->copy_job,
+ "finished",
+ G_CALLBACK (eom_job_copy_cb),
+ window);
+ g_signal_connect (priv->copy_job,
+ "progress",
+ G_CALLBACK (eom_job_progress_cb),
+ window);
+ eom_job_queue_add_job (priv->copy_job);
+
+ g_object_unref (file);
+ g_free (filename);
+ return;
+ }
+
+ g_object_unref (file);
+
+ eom_window_set_wallpaper (window, filename, NULL);
+
+ g_free (filename);
+}
+
+static gboolean
+eom_window_all_images_trasheable (GList *images)
+{
+ GFile *file;
+ GFileInfo *file_info;
+ GList *iter;
+ EomImage *image;
+ gboolean can_trash = TRUE;
+
+ for (iter = images; iter != NULL; iter = g_list_next (iter)) {
+ image = (EomImage *) iter->data;
+ file = eom_image_get_file (image);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+ 0, NULL, NULL);
+ can_trash = g_file_info_get_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
+
+ g_object_unref (file_info);
+ g_object_unref (file);
+
+ if (can_trash == FALSE)
+ break;
+ }
+
+ return can_trash;
+}
+
+static int
+show_move_to_trash_confirm_dialog (EomWindow *window, GList *images, gboolean can_trash)
+{
+ GtkWidget *dlg;
+ char *prompt;
+ int response;
+ int n_images;
+ EomImage *image;
+ static gboolean dontaskagain = FALSE;
+ gboolean neverask = FALSE;
+ GtkWidget* dontask_cbutton = NULL;
+
+ /* Check if the user never wants to be bugged. Ignore the error as
+ * it returns FALSE for safety anyway */
+ neverask = mateconf_client_get_bool (window->priv->client,
+ EOM_CONF_UI_DISABLE_TRASH_CONFIRMATION,
+ NULL);
+
+ /* Assume agreement, if the user doesn't want to be
+ * asked and the trash is available */
+ if (can_trash && (dontaskagain || neverask))
+ return GTK_RESPONSE_OK;
+
+ n_images = g_list_length (images);
+
+ if (n_images == 1) {
+ image = EOM_IMAGE (images->data);
+ if (can_trash) {
+ prompt = g_strdup_printf (_("Are you sure you want to move\n\"%s\" to the trash?"),
+ eom_image_get_caption (image));
+ } else {
+ prompt = g_strdup_printf (_("A trash for \"%s\" couldn't be found. Do you want to remove "
+ "this image permanently?"), eom_image_get_caption (image));
+ }
+ } else {
+ if (can_trash) {
+ prompt = g_strdup_printf (ngettext("Are you sure you want to move\n"
+ "the selected image to the trash?",
+ "Are you sure you want to move\n"
+ "the %d selected images to the trash?", n_images), n_images);
+ } else {
+ prompt = g_strdup (_("Some of the selected images can't be moved to the trash "
+ "and will be removed permanently. Are you sure you want "
+ "to proceed?"));
+ }
+ }
+
+ dlg = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "<span weight=\"bold\" size=\"larger\">%s</span>",
+ prompt);
+ g_free (prompt);
+
+ gtk_dialog_add_button (GTK_DIALOG (dlg), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+ if (can_trash) {
+ gtk_dialog_add_button (GTK_DIALOG (dlg), _("Move to _Trash"), GTK_RESPONSE_OK);
+
+ dontask_cbutton = gtk_check_button_new_with_mnemonic (_("_Do not ask again during this session"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dontask_cbutton), FALSE);
+
+ gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), dontask_cbutton, TRUE, TRUE, 0);
+ } else {
+ if (n_images == 1) {
+ gtk_dialog_add_button (GTK_DIALOG (dlg), GTK_STOCK_DELETE, GTK_RESPONSE_OK);
+ } else {
+ gtk_dialog_add_button (GTK_DIALOG (dlg), GTK_STOCK_YES, GTK_RESPONSE_OK);
+ }
+ }
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
+ gtk_window_set_title (GTK_WINDOW (dlg), "");
+ gtk_widget_show_all (dlg);
+
+ response = gtk_dialog_run (GTK_DIALOG (dlg));
+
+ /* Only update the property if the user has accepted */
+ if (can_trash && response == GTK_RESPONSE_OK)
+ dontaskagain = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dontask_cbutton));
+
+ /* The checkbutton is destroyed together with the dialog */
+ gtk_widget_destroy (dlg);
+
+ return response;
+}
+
+static gboolean
+move_to_trash_real (EomImage *image, GError **error)
+{
+ GFile *file;
+ GFileInfo *file_info;
+ gboolean can_trash, result;
+
+ g_return_val_if_fail (EOM_IS_IMAGE (image), FALSE);
+
+ file = eom_image_get_file (image);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+ 0, NULL, NULL);
+ if (file_info == NULL) {
+ g_set_error (error,
+ EOM_WINDOW_ERROR,
+ EOM_WINDOW_ERROR_TRASH_NOT_FOUND,
+ _("Couldn't access trash."));
+ return FALSE;
+ }
+
+ can_trash = g_file_info_get_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
+ g_object_unref (file_info);
+ if (can_trash)
+ {
+ result = g_file_trash (file, NULL, NULL);
+ if (result == FALSE) {
+ g_set_error (error,
+ EOM_WINDOW_ERROR,
+ EOM_WINDOW_ERROR_TRASH_NOT_FOUND,
+ _("Couldn't access trash."));
+ }
+ } else {
+ result = g_file_delete (file, NULL, NULL);
+ if (result == FALSE) {
+ g_set_error (error,
+ EOM_WINDOW_ERROR,
+ EOM_WINDOW_ERROR_IO,
+ _("Couldn't delete file"));
+ }
+ }
+
+ g_object_unref (file);
+
+ return result;
+}
+
+static void
+eom_window_cmd_move_to_trash (GtkAction *action, gpointer user_data)
+{
+ GList *images;
+ GList *it;
+ EomWindowPrivate *priv;
+ EomListStore *list;
+ int pos;
+ EomImage *img;
+ EomWindow *window;
+ int response;
+ int n_images;
+ gboolean success;
+ gboolean can_trash;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ window = EOM_WINDOW (user_data);
+ priv = window->priv;
+ list = priv->store;
+
+ n_images = eom_thumb_view_get_n_selected (EOM_THUMB_VIEW (priv->thumbview));
+
+ if (n_images < 1) return;
+
+ /* save position of selected image after the deletion */
+ images = eom_thumb_view_get_selected_images (EOM_THUMB_VIEW (priv->thumbview));
+
+ g_assert (images != NULL);
+
+ /* HACK: eom_list_store_get_n_selected return list in reverse order */
+ images = g_list_reverse (images);
+
+ can_trash = eom_window_all_images_trasheable (images);
+
+ if (g_ascii_strcasecmp (gtk_action_get_name (action), "Delete") == 0 ||
+ can_trash == FALSE) {
+ response = show_move_to_trash_confirm_dialog (window, images, can_trash);
+
+ if (response != GTK_RESPONSE_OK) return;
+ }
+
+ pos = eom_list_store_get_pos_by_image (list, EOM_IMAGE (images->data));
+
+ /* FIXME: make a nice progress dialog */
+ /* Do the work actually. First try to delete the image from the disk. If this
+ * is successfull, remove it from the screen. Otherwise show error dialog.
+ */
+ for (it = images; it != NULL; it = it->next) {
+ GError *error = NULL;
+ EomImage *image;
+
+ image = EOM_IMAGE (it->data);
+
+ success = move_to_trash_real (image, &error);
+
+ if (success) {
+ eom_list_store_remove_image (list, image);
+ } else {
+ char *header;
+ GtkWidget *dlg;
+
+ header = g_strdup_printf (_("Error on deleting image %s"),
+ eom_image_get_caption (image));
+
+ dlg = gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s", header);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg),
+ "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dlg));
+
+ gtk_widget_destroy (dlg);
+
+ g_free (header);
+ }
+ }
+
+ /* free list */
+ g_list_foreach (images, (GFunc) g_object_unref, NULL);
+ g_list_free (images);
+
+ /* select image at previously saved position */
+ pos = MIN (pos, eom_list_store_length (list) - 1);
+
+ if (pos >= 0) {
+ img = eom_list_store_get_image_by_pos (list, pos);
+
+ eom_thumb_view_set_current_image (EOM_THUMB_VIEW (priv->thumbview),
+ img,
+ TRUE);
+
+ if (img != NULL) {
+ g_object_unref (img);
+ }
+ }
+}
+
+static void
+eom_window_cmd_fullscreen (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ gboolean fullscreen;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ window = EOM_WINDOW (user_data);
+
+ fullscreen = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+ if (fullscreen) {
+ eom_window_run_fullscreen (window, FALSE);
+ } else {
+ eom_window_stop_fullscreen (window, FALSE);
+ }
+}
+
+static void
+eom_window_cmd_slideshow (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ gboolean slideshow;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ window = EOM_WINDOW (user_data);
+
+ slideshow = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+ if (slideshow) {
+ eom_window_run_fullscreen (window, TRUE);
+ } else {
+ eom_window_stop_fullscreen (window, TRUE);
+ }
+}
+
+static void
+eom_window_cmd_pause_slideshow (GtkAction *action, gpointer user_data)
+{
+ EomWindow *window;
+ gboolean slideshow;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ window = EOM_WINDOW (user_data);
+
+ slideshow = window->priv->mode == EOM_WINDOW_MODE_SLIDESHOW;
+
+ if (!slideshow && window->priv->mode != EOM_WINDOW_MODE_FULLSCREEN)
+ return;
+
+ eom_window_run_fullscreen (window, !slideshow);
+}
+
+static void
+eom_window_cmd_zoom_in (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ if (priv->view) {
+ eom_scroll_view_zoom_in (EOM_SCROLL_VIEW (priv->view), FALSE);
+ }
+}
+
+static void
+eom_window_cmd_zoom_out (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ if (priv->view) {
+ eom_scroll_view_zoom_out (EOM_SCROLL_VIEW (priv->view), FALSE);
+ }
+}
+
+static void
+eom_window_cmd_zoom_normal (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ if (priv->view) {
+ eom_scroll_view_set_zoom (EOM_SCROLL_VIEW (priv->view), 1.0);
+ }
+}
+
+static void
+eom_window_cmd_zoom_fit (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ if (priv->view) {
+ eom_scroll_view_zoom_fit (EOM_SCROLL_VIEW (priv->view));
+ }
+}
+
+static void
+eom_window_cmd_go_prev (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_LEFT);
+}
+
+static void
+eom_window_cmd_go_next (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_RIGHT);
+}
+
+static void
+eom_window_cmd_go_first (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_FIRST);
+}
+
+static void
+eom_window_cmd_go_last (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_LAST);
+}
+
+static void
+eom_window_cmd_go_random (GtkAction *action, gpointer user_data)
+{
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (user_data));
+
+ eom_debug (DEBUG_WINDOW);
+
+ priv = EOM_WINDOW (user_data)->priv;
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_RANDOM);
+}
+
+static const GtkActionEntry action_entries_window[] = {
+ { "Image", NULL, N_("_Image") },
+ { "Edit", NULL, N_("_Edit") },
+ { "View", NULL, N_("_View") },
+ { "Go", NULL, N_("_Go") },
+ { "Tools", NULL, N_("_Tools") },
+ { "Help", NULL, N_("_Help") },
+
+ { "ImageOpen", GTK_STOCK_OPEN, N_("_Open…"), "<control>O",
+ N_("Open a file"),
+ G_CALLBACK (eom_window_cmd_file_open) },
+ { "ImageClose", GTK_STOCK_CLOSE, N_("_Close"), "<control>W",
+ N_("Close window"),
+ G_CALLBACK (eom_window_cmd_close_window) },
+ { "EditToolbar", NULL, N_("T_oolbar"), NULL,
+ N_("Edit the application toolbar"),
+ G_CALLBACK (eom_window_cmd_edit_toolbar) },
+ { "EditPreferences", GTK_STOCK_PREFERENCES, N_("Prefere_nces"), NULL,
+ N_("Preferences for Eye of MATE"),
+ G_CALLBACK (eom_window_cmd_preferences) },
+ { "HelpManual", GTK_STOCK_HELP, N_("_Contents"), "F1",
+ N_("Help on this application"),
+ G_CALLBACK (eom_window_cmd_help) },
+ { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
+ N_("About this application"),
+ G_CALLBACK (eom_window_cmd_about) }
+};
+
+static const GtkToggleActionEntry toggle_entries_window[] = {
+ { "ViewToolbar", NULL, N_("_Toolbar"), NULL,
+ N_("Changes the visibility of the toolbar in the current window"),
+ G_CALLBACK (eom_window_cmd_show_hide_bar), TRUE },
+ { "ViewStatusbar", NULL, N_("_Statusbar"), NULL,
+ N_("Changes the visibility of the statusbar in the current window"),
+ G_CALLBACK (eom_window_cmd_show_hide_bar), TRUE },
+ { "ViewImageCollection", "eom-image-collection", N_("_Image Collection"), "F9",
+ N_("Changes the visibility of the image collection pane in the current window"),
+ G_CALLBACK (eom_window_cmd_show_hide_bar), TRUE },
+ { "ViewSidebar", NULL, N_("Side _Pane"), "<control>F9",
+ N_("Changes the visibility of the side pane in the current window"),
+ G_CALLBACK (eom_window_cmd_show_hide_bar), TRUE },
+};
+
+static const GtkActionEntry action_entries_image[] = {
+ { "ImageSave", GTK_STOCK_SAVE, N_("_Save"), "<control>s",
+ N_("Save changes in currently selected images"),
+ G_CALLBACK (eom_window_cmd_save) },
+ { "ImageOpenWith", NULL, N_("Open _with"), NULL,
+ N_("Open the selected image with a different application"),
+ NULL},
+ { "ImageSaveAs", GTK_STOCK_SAVE_AS, N_("Save _As…"), "<control><shift>s",
+ N_("Save the selected images with a different name"),
+ G_CALLBACK (eom_window_cmd_save_as) },
+ { "ImagePrint", GTK_STOCK_PRINT, N_("_Print…"), "<control>p",
+ N_("Print the selected image"),
+ G_CALLBACK (eom_window_cmd_print) },
+ { "ImageProperties", GTK_STOCK_PROPERTIES, N_("Prope_rties"), "<alt>Return",
+ N_("Show the properties and metadata of the selected image"),
+ G_CALLBACK (eom_window_cmd_properties) },
+ { "EditUndo", GTK_STOCK_UNDO, N_("_Undo"), "<control>z",
+ N_("Undo the last change in the image"),
+ G_CALLBACK (eom_window_cmd_undo) },
+ { "EditFlipHorizontal", "object-flip-horizontal", N_("Flip _Horizontal"), NULL,
+ N_("Mirror the image horizontally"),
+ G_CALLBACK (eom_window_cmd_flip_horizontal) },
+ { "EditFlipVertical", "object-flip-vertical", N_("Flip _Vertical"), NULL,
+ N_("Mirror the image vertically"),
+ G_CALLBACK (eom_window_cmd_flip_vertical) },
+ { "EditRotate90", "object-rotate-right", N_("_Rotate Clockwise"), "<control>r",
+ N_("Rotate the image 90 degrees to the right"),
+ G_CALLBACK (eom_window_cmd_rotate_90) },
+ { "EditRotate270", "object-rotate-left", N_("Rotate Counterc_lockwise"), "<ctrl><shift>r",
+ N_("Rotate the image 90 degrees to the left"),
+ G_CALLBACK (eom_window_cmd_rotate_270) },
+ { "ImageSetAsWallpaper", NULL, N_("Set as _Desktop Background"),
+ "<control>F8", N_("Set the selected image as the desktop background"),
+ G_CALLBACK (eom_window_cmd_wallpaper) },
+ { "EditMoveToTrash", "user-trash", N_("Move to _Trash"), NULL,
+ N_("Move the selected image to the trash folder"),
+ G_CALLBACK (eom_window_cmd_move_to_trash) },
+ { "ViewZoomIn", GTK_STOCK_ZOOM_IN, N_("_Zoom In"), "<control>plus",
+ N_("Enlarge the image"),
+ G_CALLBACK (eom_window_cmd_zoom_in) },
+ { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus",
+ N_("Shrink the image"),
+ G_CALLBACK (eom_window_cmd_zoom_out) },
+ { "ViewZoomNormal", GTK_STOCK_ZOOM_100, N_("_Normal Size"), "<control>0",
+ N_("Show the image at its normal size"),
+ G_CALLBACK (eom_window_cmd_zoom_normal) },
+ { "ViewZoomFit", GTK_STOCK_ZOOM_FIT, N_("Best _Fit"), "F",
+ N_("Fit the image to the window"),
+ G_CALLBACK (eom_window_cmd_zoom_fit) },
+ { "ControlEqual", GTK_STOCK_ZOOM_IN, N_("_Zoom In"), "<control>equal",
+ N_("Enlarge the image"),
+ G_CALLBACK (eom_window_cmd_zoom_in) },
+ { "ControlKpAdd", GTK_STOCK_ZOOM_IN, N_("_Zoom In"), "<control>KP_Add",
+ N_("Shrink the image"),
+ G_CALLBACK (eom_window_cmd_zoom_in) },
+ { "ControlKpSub", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>KP_Subtract",
+ N_("Shrink the image"),
+ G_CALLBACK (eom_window_cmd_zoom_out) },
+ { "Delete", NULL, N_("Move to _Trash"), "Delete",
+ NULL,
+ G_CALLBACK (eom_window_cmd_move_to_trash) },
+};
+
+static const GtkToggleActionEntry toggle_entries_image[] = {
+ { "ViewFullscreen", GTK_STOCK_FULLSCREEN, N_("_Fullscreen"), "F11",
+ N_("Show the current image in fullscreen mode"),
+ G_CALLBACK (eom_window_cmd_fullscreen), FALSE },
+ { "PauseSlideshow", "media-playback-pause", N_("Pause Slideshow"),
+ NULL, N_("Pause or resume the slideshow"),
+ G_CALLBACK (eom_window_cmd_pause_slideshow), FALSE },
+};
+
+static const GtkActionEntry action_entries_collection[] = {
+ { "GoPrevious", GTK_STOCK_GO_BACK, N_("_Previous Image"), "<Alt>Left",
+ N_("Go to the previous image of the collection"),
+ G_CALLBACK (eom_window_cmd_go_prev) },
+ { "GoNext", GTK_STOCK_GO_FORWARD, N_("_Next Image"), "<Alt>Right",
+ N_("Go to the next image of the collection"),
+ G_CALLBACK (eom_window_cmd_go_next) },
+ { "GoFirst", GTK_STOCK_GOTO_FIRST, N_("_First Image"), "<Alt>Home",
+ N_("Go to the first image of the collection"),
+ G_CALLBACK (eom_window_cmd_go_first) },
+ { "GoLast", GTK_STOCK_GOTO_LAST, N_("_Last Image"), "<Alt>End",
+ N_("Go to the last image of the collection"),
+ G_CALLBACK (eom_window_cmd_go_last) },
+ { "GoRandom", NULL, N_("_Random Image"), "<control>M",
+ N_("Go to a random image of the collection"),
+ G_CALLBACK (eom_window_cmd_go_random) },
+ { "BackSpace", NULL, N_("_Previous Image"), "BackSpace",
+ NULL,
+ G_CALLBACK (eom_window_cmd_go_prev) },
+ { "Home", NULL, N_("_First Image"), "Home",
+ NULL,
+ G_CALLBACK (eom_window_cmd_go_first) },
+ { "End", NULL, N_("_Last Image"), "End",
+ NULL,
+ G_CALLBACK (eom_window_cmd_go_last) },
+};
+
+static const GtkToggleActionEntry toggle_entries_collection[] = {
+ { "ViewSlideshow", "slideshow-play", N_("_Slideshow"), "F5",
+ N_("Start a slideshow view of the images"),
+ G_CALLBACK (eom_window_cmd_slideshow), FALSE },
+};
+
+static void
+menu_item_select_cb (GtkMenuItem *proxy, EomWindow *window)
+{
+ GtkAction *action;
+ char *message;
+
+ action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy));
+
+ g_return_if_fail (action != NULL);
+
+ g_object_get (G_OBJECT (action), "tooltip", &message, NULL);
+
+ if (message) {
+ gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar),
+ window->priv->tip_message_cid, message);
+ g_free (message);
+ }
+}
+
+static void
+menu_item_deselect_cb (GtkMenuItem *proxy, EomWindow *window)
+{
+ gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
+ window->priv->tip_message_cid);
+}
+
+static void
+connect_proxy_cb (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ EomWindow *window)
+{
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ g_signal_connect (proxy, "select",
+ G_CALLBACK (menu_item_select_cb), window);
+ g_signal_connect (proxy, "deselect",
+ G_CALLBACK (menu_item_deselect_cb), window);
+ }
+}
+
+static void
+disconnect_proxy_cb (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ EomWindow *window)
+{
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ g_signal_handlers_disconnect_by_func
+ (proxy, G_CALLBACK (menu_item_select_cb), window);
+ g_signal_handlers_disconnect_by_func
+ (proxy, G_CALLBACK (menu_item_deselect_cb), window);
+ }
+}
+
+static void
+set_action_properties (GtkActionGroup *window_group,
+ GtkActionGroup *image_group,
+ GtkActionGroup *collection_group)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (collection_group, "GoPrevious");
+ g_object_set (action, "short_label", _("Previous"), NULL);
+ g_object_set (action, "is-important", TRUE, NULL);
+
+ action = gtk_action_group_get_action (collection_group, "GoNext");
+ g_object_set (action, "short_label", _("Next"), NULL);
+ g_object_set (action, "is-important", TRUE, NULL);
+
+ action = gtk_action_group_get_action (image_group, "EditRotate90");
+ g_object_set (action, "short_label", _("Right"), NULL);
+
+ action = gtk_action_group_get_action (image_group, "EditRotate270");
+ g_object_set (action, "short_label", _("Left"), NULL);
+
+ action = gtk_action_group_get_action (image_group, "ViewZoomIn");
+ g_object_set (action, "short_label", _("In"), NULL);
+
+ action = gtk_action_group_get_action (image_group, "ViewZoomOut");
+ g_object_set (action, "short_label", _("Out"), NULL);
+
+ action = gtk_action_group_get_action (image_group, "ViewZoomNormal");
+ g_object_set (action, "short_label", _("Normal"), NULL);
+
+ action = gtk_action_group_get_action (image_group, "ViewZoomFit");
+ g_object_set (action, "short_label", _("Fit"), NULL);
+
+ action = gtk_action_group_get_action (window_group, "ViewImageCollection");
+ g_object_set (action, "short_label", _("Collection"), NULL);
+
+ action = gtk_action_group_get_action (image_group, "EditMoveToTrash");
+ g_object_set (action, "short_label", C_("action (to trash)", "Trash"), NULL);
+}
+
+static gint
+sort_recents_mru (GtkRecentInfo *a, GtkRecentInfo *b)
+{
+ gboolean has_eom_a, has_eom_b;
+
+ /* We need to check this first as gtk_recent_info_get_application_info
+ * will treat it as a non-fatal error when the GtkRecentInfo doesn't
+ * have the application registered. */
+ has_eom_a = gtk_recent_info_has_application (a,
+ EOM_RECENT_FILES_APP_NAME);
+ has_eom_b = gtk_recent_info_has_application (b,
+ EOM_RECENT_FILES_APP_NAME);
+ if (has_eom_a && has_eom_b) {
+ time_t time_a, time_b;
+
+ /* These should not fail as we already checked that
+ * the application is registered with the info objects */
+ gtk_recent_info_get_application_info (a,
+ EOM_RECENT_FILES_APP_NAME,
+ NULL,
+ NULL,
+ &time_a);
+ gtk_recent_info_get_application_info (b,
+ EOM_RECENT_FILES_APP_NAME,
+ NULL,
+ NULL,
+ &time_b);
+
+ return (time_b - time_a);
+ } else if (has_eom_a) {
+ return -1;
+ } else if (has_eom_b) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+eom_window_update_recent_files_menu (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ GList *actions = NULL, *li = NULL, *items = NULL;
+ guint count_recent = 0;
+
+ priv = window->priv;
+
+ if (priv->recent_menu_id != 0)
+ gtk_ui_manager_remove_ui (priv->ui_mgr, priv->recent_menu_id);
+
+ actions = gtk_action_group_list_actions (priv->actions_recent);
+
+ for (li = actions; li != NULL; li = li->next) {
+ g_signal_handlers_disconnect_by_func (GTK_ACTION (li->data),
+ G_CALLBACK(eom_window_open_recent_cb),
+ window);
+
+ gtk_action_group_remove_action (priv->actions_recent,
+ GTK_ACTION (li->data));
+ }
+
+ g_list_free (actions);
+
+ priv->recent_menu_id = gtk_ui_manager_new_merge_id (priv->ui_mgr);
+ items = gtk_recent_manager_get_items (gtk_recent_manager_get_default());
+ items = g_list_sort (items, (GCompareFunc) sort_recents_mru);
+
+ for (li = items; li != NULL && count_recent < EOM_RECENT_FILES_LIMIT; li = li->next) {
+ gchar *action_name;
+ gchar *label;
+ gchar *tip;
+ gchar **display_name;
+ gchar *label_filename;
+ GtkAction *action;
+ GtkRecentInfo *info = li->data;
+
+ /* Sorting moves non-EOM files to the end of the list.
+ * So no file of interest will follow if this test fails */
+ if (!gtk_recent_info_has_application (info, EOM_RECENT_FILES_APP_NAME))
+ break;
+
+ count_recent++;
+
+ action_name = g_strdup_printf ("recent-info-%d", count_recent);
+ display_name = g_strsplit (gtk_recent_info_get_display_name (info), "_", -1);
+ label_filename = g_strjoinv ("__", display_name);
+ label = g_strdup_printf ("%s_%d. %s",
+ (is_rtl ? "\xE2\x80\x8F" : ""), count_recent, label_filename);
+ g_free (label_filename);
+ g_strfreev (display_name);
+
+ tip = gtk_recent_info_get_uri_display (info);
+
+ /* This is a workaround for a bug (#351945) regarding
+ * gtk_recent_info_get_uri_display() and remote URIs.
+ * mate_vfs_format_uri_for_display is sufficient here
+ * since the password gets stripped when adding the
+ * file to the recently used list. */
+ if (tip == NULL)
+ tip = g_uri_unescape_string (gtk_recent_info_get_uri (info), NULL);
+
+ action = gtk_action_new (action_name, label, tip, NULL);
+ gtk_action_set_always_show_image (action, TRUE);
+
+ g_object_set_data_full (G_OBJECT (action), "gtk-recent-info",
+ gtk_recent_info_ref (info),
+ (GDestroyNotify) gtk_recent_info_unref);
+
+ g_object_set (G_OBJECT (action), "icon-name", "image-x-generic", NULL);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (eom_window_open_recent_cb),
+ window);
+
+ gtk_action_group_add_action (priv->actions_recent, action);
+
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (priv->ui_mgr, priv->recent_menu_id,
+ "/MainMenu/Image/RecentDocuments",
+ action_name, action_name,
+ GTK_UI_MANAGER_AUTO, FALSE);
+
+ g_free (action_name);
+ g_free (label);
+ g_free (tip);
+ }
+
+ g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (items);
+}
+
+static void
+eom_window_recent_manager_changed_cb (GtkRecentManager *manager, EomWindow *window)
+{
+ eom_window_update_recent_files_menu (window);
+}
+
+static void
+eom_window_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x, gint y,
+ GtkSelectionData *selection_data,
+ guint info, guint time)
+{
+ GSList *file_list;
+ EomWindow *window;
+ GdkAtom target;
+
+ target = gtk_selection_data_get_target (selection_data);
+
+ if (!gtk_targets_include_uri (&target, 1))
+ return;
+
+ if (context->suggested_action == GDK_ACTION_COPY) {
+ window = EOM_WINDOW (widget);
+
+ file_list = eom_util_parse_uri_string_list_to_file_list ((const gchar *) gtk_selection_data_get_data (selection_data));
+
+ eom_window_open_file_list (window, file_list);
+ }
+}
+
+static void
+eom_window_set_drag_dest (EomWindow *window)
+{
+ gtk_drag_dest_set (GTK_WIDGET (window),
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ NULL, 0,
+ GDK_ACTION_COPY | GDK_ACTION_ASK);
+ gtk_drag_dest_add_uri_targets (GTK_WIDGET (window));
+}
+
+static void
+eom_window_sidebar_visibility_changed (GtkWidget *widget, EomWindow *window)
+{
+ GtkAction *action;
+ gboolean visible;
+
+ visible = gtk_widget_get_visible (window->priv->sidebar);
+
+ mateconf_client_set_bool (window->priv->client,
+ EOM_CONF_UI_SIDEBAR,
+ visible,
+ NULL);
+
+ action = gtk_action_group_get_action (window->priv->actions_window,
+ "ViewSidebar");
+
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+
+ /* Focus the image */
+ if (!visible && window->priv->image != NULL)
+ gtk_widget_grab_focus (window->priv->view);
+}
+
+static void
+eom_window_sidebar_page_added (EomSidebar *sidebar,
+ GtkWidget *main_widget,
+ EomWindow *window)
+{
+ if (eom_sidebar_get_n_pages (sidebar) == 1) {
+ GtkAction *action;
+ gboolean show;
+
+ action = gtk_action_group_get_action (window->priv->actions_window,
+ "ViewSidebar");
+
+ gtk_action_set_sensitive (action, TRUE);
+
+ show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+
+ if (show)
+ gtk_widget_show (GTK_WIDGET (sidebar));
+ }
+}
+static void
+eom_window_sidebar_page_removed (EomSidebar *sidebar,
+ GtkWidget *main_widget,
+ EomWindow *window)
+{
+ if (eom_sidebar_is_empty (sidebar)) {
+ GtkAction *action;
+
+ gtk_widget_hide (GTK_WIDGET (sidebar));
+
+ action = gtk_action_group_get_action (window->priv->actions_window,
+ "ViewSidebar");
+
+ gtk_action_set_sensitive (action, FALSE);
+ }
+}
+
+static void
+eom_window_finish_saving (EomWindow *window)
+{
+ EomWindowPrivate *priv = window->priv;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (window), FALSE);
+
+ do {
+ gtk_main_iteration ();
+ } while (priv->save_job != NULL);
+}
+
+static void
+eom_window_construct_ui (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+
+ GError *error = NULL;
+
+ GtkWidget *menubar;
+ GtkWidget *thumb_popup;
+ GtkWidget *view_popup;
+ GtkWidget *hpaned;
+ GtkWidget *menuitem;
+
+ MateConfEntry *entry;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ priv = window->priv;
+
+ priv->box = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), priv->box);
+ gtk_widget_show (priv->box);
+
+ priv->ui_mgr = gtk_ui_manager_new ();
+
+ priv->actions_window = gtk_action_group_new ("MenuActionsWindow");
+
+ gtk_action_group_set_translation_domain (priv->actions_window,
+ GETTEXT_PACKAGE);
+
+ gtk_action_group_add_actions (priv->actions_window,
+ action_entries_window,
+ G_N_ELEMENTS (action_entries_window),
+ window);
+
+ gtk_action_group_add_toggle_actions (priv->actions_window,
+ toggle_entries_window,
+ G_N_ELEMENTS (toggle_entries_window),
+ window);
+
+ gtk_ui_manager_insert_action_group (priv->ui_mgr, priv->actions_window, 0);
+
+ priv->actions_image = gtk_action_group_new ("MenuActionsImage");
+ gtk_action_group_set_translation_domain (priv->actions_image,
+ GETTEXT_PACKAGE);
+
+ gtk_action_group_add_actions (priv->actions_image,
+ action_entries_image,
+ G_N_ELEMENTS (action_entries_image),
+ window);
+
+ gtk_action_group_add_toggle_actions (priv->actions_image,
+ toggle_entries_image,
+ G_N_ELEMENTS (toggle_entries_image),
+ window);
+
+ gtk_ui_manager_insert_action_group (priv->ui_mgr, priv->actions_image, 0);
+
+ priv->actions_collection = gtk_action_group_new ("MenuActionsCollection");
+ gtk_action_group_set_translation_domain (priv->actions_collection,
+ GETTEXT_PACKAGE);
+
+ gtk_action_group_add_actions (priv->actions_collection,
+ action_entries_collection,
+ G_N_ELEMENTS (action_entries_collection),
+ window);
+
+ gtk_action_group_add_toggle_actions (priv->actions_collection,
+ toggle_entries_collection,
+ G_N_ELEMENTS (toggle_entries_collection),
+ window);
+
+ set_action_properties (priv->actions_window,
+ priv->actions_image,
+ priv->actions_collection);
+
+ gtk_ui_manager_insert_action_group (priv->ui_mgr, priv->actions_collection, 0);
+
+ if (!gtk_ui_manager_add_ui_from_file (priv->ui_mgr,
+ EOM_DATA_DIR"/eom-ui.xml",
+ &error)) {
+ g_warning ("building menus failed: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_signal_connect (priv->ui_mgr, "connect_proxy",
+ G_CALLBACK (connect_proxy_cb), window);
+ g_signal_connect (priv->ui_mgr, "disconnect_proxy",
+ G_CALLBACK (disconnect_proxy_cb), window);
+
+ menubar = gtk_ui_manager_get_widget (priv->ui_mgr, "/MainMenu");
+ g_assert (GTK_IS_WIDGET (menubar));
+ gtk_box_pack_start (GTK_BOX (priv->box), menubar, FALSE, FALSE, 0);
+ gtk_widget_show (menubar);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr,
+ "/MainMenu/Edit/EditFlipHorizontal");
+ gtk_image_menu_item_set_always_show_image (
+ GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr,
+ "/MainMenu/Edit/EditFlipVertical");
+ gtk_image_menu_item_set_always_show_image (
+ GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr,
+ "/MainMenu/Edit/EditRotate90");
+ gtk_image_menu_item_set_always_show_image (
+ GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+
+ menuitem = gtk_ui_manager_get_widget (priv->ui_mgr,
+ "/MainMenu/Edit/EditRotate270");
+ gtk_image_menu_item_set_always_show_image (
+ GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+
+ priv->toolbar = GTK_WIDGET
+ (g_object_new (EGG_TYPE_EDITABLE_TOOLBAR,
+ "ui-manager", priv->ui_mgr,
+ "model", eom_application_get_toolbars_model (EOM_APP),
+ NULL));
+
+ egg_editable_toolbar_show (EGG_EDITABLE_TOOLBAR (priv->toolbar),
+ "Toolbar");
+
+ gtk_box_pack_start (GTK_BOX (priv->box),
+ priv->toolbar,
+ FALSE,
+ FALSE,
+ 0);
+
+ gtk_widget_show (priv->toolbar);
+
+ gtk_window_add_accel_group (GTK_WINDOW (window),
+ gtk_ui_manager_get_accel_group (priv->ui_mgr));
+
+ priv->actions_recent = gtk_action_group_new ("RecentFilesActions");
+ gtk_action_group_set_translation_domain (priv->actions_recent,
+ GETTEXT_PACKAGE);
+
+ g_signal_connect (gtk_recent_manager_get_default (), "changed",
+ G_CALLBACK (eom_window_recent_manager_changed_cb),
+ window);
+
+ eom_window_update_recent_files_menu (window);
+
+ gtk_ui_manager_insert_action_group (priv->ui_mgr, priv->actions_recent, 0);
+
+ priv->cbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (priv->box), priv->cbox, TRUE, TRUE, 0);
+ gtk_widget_show (priv->cbox);
+
+ priv->statusbar = eom_statusbar_new ();
+ gtk_box_pack_end (GTK_BOX (priv->box),
+ GTK_WIDGET (priv->statusbar),
+ FALSE, FALSE, 0);
+ gtk_widget_show (priv->statusbar);
+
+ priv->image_info_message_cid =
+ gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
+ "image_info_message");
+ priv->tip_message_cid =
+ gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
+ "tip_message");
+
+ priv->layout = gtk_vbox_new (FALSE, 2);
+
+ hpaned = gtk_hpaned_new ();
+
+ priv->sidebar = eom_sidebar_new ();
+ /* The sidebar shouldn't be shown automatically on show_all(),
+ but only when the user actually wants it. */
+ gtk_widget_set_no_show_all (priv->sidebar, TRUE);
+
+ gtk_widget_set_size_request (priv->sidebar, 210, -1);
+
+ g_signal_connect_after (priv->sidebar,
+ "show",
+ G_CALLBACK (eom_window_sidebar_visibility_changed),
+ window);
+
+ g_signal_connect_after (priv->sidebar,
+ "hide",
+ G_CALLBACK (eom_window_sidebar_visibility_changed),
+ window);
+
+ g_signal_connect_after (priv->sidebar,
+ "page-added",
+ G_CALLBACK (eom_window_sidebar_page_added),
+ window);
+
+ g_signal_connect_after (priv->sidebar,
+ "page-removed",
+ G_CALLBACK (eom_window_sidebar_page_removed),
+ window);
+
+ priv->view = eom_scroll_view_new ();
+ gtk_widget_set_size_request (GTK_WIDGET (priv->view), 100, 100);
+ g_signal_connect (G_OBJECT (priv->view),
+ "zoom_changed",
+ G_CALLBACK (view_zoom_changed_cb),
+ window);
+
+ view_popup = gtk_ui_manager_get_widget (priv->ui_mgr, "/ViewPopup");
+ eom_scroll_view_set_popup (EOM_SCROLL_VIEW (priv->view),
+ GTK_MENU (view_popup));
+
+ gtk_paned_pack1 (GTK_PANED (hpaned),
+ priv->sidebar,
+ FALSE,
+ FALSE);
+
+ gtk_paned_pack2 (GTK_PANED (hpaned),
+ priv->view,
+ TRUE,
+ FALSE);
+
+ gtk_widget_show_all (hpaned);
+
+ gtk_box_pack_start (GTK_BOX (priv->layout), hpaned, TRUE, TRUE, 0);
+
+ priv->thumbview = eom_thumb_view_new ();
+
+ /* giving shape to the view */
+ gtk_icon_view_set_margin (GTK_ICON_VIEW (priv->thumbview), 4);
+ gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (priv->thumbview), 0);
+
+ g_signal_connect (G_OBJECT (priv->thumbview), "selection_changed",
+ G_CALLBACK (handle_image_selection_changed_cb), window);
+
+ priv->nav = eom_thumb_nav_new (priv->thumbview,
+ EOM_THUMB_NAV_MODE_ONE_ROW,
+ mateconf_client_get_bool (priv->client,
+ EOM_CONF_UI_SCROLL_BUTTONS,
+ NULL));
+
+ thumb_popup = gtk_ui_manager_get_widget (priv->ui_mgr, "/ThumbnailPopup");
+ eom_thumb_view_set_thumbnail_popup (EOM_THUMB_VIEW (priv->thumbview),
+ GTK_MENU (thumb_popup));
+
+ gtk_box_pack_start (GTK_BOX (priv->layout), priv->nav, FALSE, FALSE, 0);
+
+ gtk_box_pack_end (GTK_BOX (priv->cbox), priv->layout, TRUE, TRUE, 0);
+
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_EXTRAPOLATE,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_interp_in_type_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_INTERPOLATE,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_interp_out_type_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_SCROLL_WHEEL_ZOOM,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_scroll_wheel_zoom_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_ZOOM_MULTIPLIER,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_zoom_multiplier_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_BACKGROUND_COLOR,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_bg_color_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_USE_BG_COLOR,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_use_bg_color_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_TRANSPARENCY,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_transparency_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_VIEW_TRANS_COLOR,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_trans_color_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION_POSITION,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_collection_mode_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ entry = mateconf_client_get_entry (priv->client,
+ EOM_CONF_DESKTOP_CAN_SAVE,
+ NULL, TRUE, NULL);
+ if (entry != NULL) {
+ eom_window_can_save_changed_cb (priv->client, 0, entry, window);
+ mateconf_entry_unref (entry);
+ entry = NULL;
+ }
+
+ if ((priv->flags & EOM_STARTUP_FULLSCREEN) ||
+ (priv->flags & EOM_STARTUP_SLIDE_SHOW)) {
+ eom_window_run_fullscreen (window, (priv->flags & EOM_STARTUP_SLIDE_SHOW));
+ } else {
+ priv->mode = EOM_WINDOW_MODE_NORMAL;
+ update_ui_visibility (window);
+ }
+
+ eom_window_set_drag_dest (window);
+}
+
+static void
+eom_window_init (EomWindow *window)
+{
+ GdkGeometry hints;
+ GdkScreen *screen;
+ EomWindowPrivate *priv;
+
+ eom_debug (DEBUG_WINDOW);
+
+ hints.min_width = EOM_WINDOW_MIN_WIDTH;
+ hints.min_height = EOM_WINDOW_MIN_HEIGHT;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (window));
+
+ priv = window->priv = EOM_WINDOW_GET_PRIVATE (window);
+
+ priv->client = mateconf_client_get_default ();
+
+ mateconf_client_add_dir (window->priv->client, EOM_CONF_DIR,
+ MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_EXTRAPOLATE] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_EXTRAPOLATE,
+ eom_window_interp_in_type_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_INTERPOLATE] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_INTERPOLATE,
+ eom_window_interp_out_type_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_SCROLLWHEEL_ZOOM] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_SCROLL_WHEEL_ZOOM,
+ eom_window_scroll_wheel_zoom_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_ZOOM_MULTIPLIER] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_ZOOM_MULTIPLIER,
+ eom_window_zoom_multiplier_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_BACKGROUND_COLOR] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_BACKGROUND_COLOR,
+ eom_window_bg_color_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_USE_BG_COLOR] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_USE_BG_COLOR,
+ eom_window_use_bg_color_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_TRANSPARENCY] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_TRANSPARENCY,
+ eom_window_transparency_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_TRANS_COLOR] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_VIEW_TRANS_COLOR,
+ eom_window_trans_color_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_SCROLL_BUTTONS] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_UI_SCROLL_BUTTONS,
+ eom_window_scroll_buttons_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_COLLECTION_POS] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION_POSITION,
+ eom_window_collection_mode_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_COLLECTION_RESIZABLE] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_UI_IMAGE_COLLECTION_RESIZABLE,
+ eom_window_collection_mode_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_CAN_SAVE] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_DESKTOP_CAN_SAVE,
+ eom_window_can_save_changed_cb,
+ window, NULL, NULL);
+
+ priv->client_notifications[EOM_WINDOW_NOTIFY_PROPSDIALOG_NETBOOK_MODE] =
+ mateconf_client_notify_add (window->priv->client,
+ EOM_CONF_UI_PROPSDIALOG_NETBOOK_MODE,
+ eom_window_pd_nbmode_changed_cb,
+ window, NULL, NULL);
+
+ window->priv->store = NULL;
+ window->priv->image = NULL;
+
+ window->priv->fullscreen_popup = NULL;
+ window->priv->fullscreen_timeout_source = NULL;
+ window->priv->slideshow_loop = FALSE;
+ window->priv->slideshow_switch_timeout = 0;
+ window->priv->slideshow_switch_source = NULL;
+
+ gtk_window_set_geometry_hints (GTK_WINDOW (window),
+ GTK_WIDGET (window),
+ &hints,
+ GDK_HINT_MIN_SIZE);
+
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ EOM_WINDOW_DEFAULT_WIDTH,
+ EOM_WINDOW_DEFAULT_HEIGHT);
+
+ gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
+
+ window->priv->mode = EOM_WINDOW_MODE_UNKNOWN;
+ window->priv->status = EOM_WINDOW_STATUS_UNKNOWN;
+
+#ifdef HAVE_LCMS
+ window->priv->display_profile =
+ eom_window_get_display_profile (screen);
+#endif
+
+ window->priv->recent_menu_id = 0;
+
+ window->priv->collection_position = 0;
+ window->priv->collection_resizable = FALSE;
+
+ window->priv->save_disabled = FALSE;
+}
+
+static void
+eom_window_dispose (GObject *object)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (EOM_IS_WINDOW (object));
+
+ eom_debug (DEBUG_WINDOW);
+
+ window = EOM_WINDOW (object);
+ priv = window->priv;
+
+ eom_plugin_engine_garbage_collect ();
+
+ if (priv->store != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->store,
+ eom_window_list_store_image_added,
+ window);
+ g_signal_handlers_disconnect_by_func (priv->store,
+ eom_window_list_store_image_removed,
+ window);
+ g_object_unref (priv->store);
+ priv->store = NULL;
+ }
+
+ if (priv->image != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->image,
+ image_thumb_changed_cb,
+ window);
+ g_signal_handlers_disconnect_by_func (priv->image,
+ image_file_changed_cb,
+ window);
+ g_object_unref (priv->image);
+ priv->image = NULL;
+ }
+
+ if (priv->actions_window != NULL) {
+ g_object_unref (priv->actions_window);
+ priv->actions_window = NULL;
+ }
+
+ if (priv->actions_image != NULL) {
+ g_object_unref (priv->actions_image);
+ priv->actions_image = NULL;
+ }
+
+ if (priv->actions_collection != NULL) {
+ g_object_unref (priv->actions_collection);
+ priv->actions_collection = NULL;
+ }
+
+ if (priv->actions_recent != NULL) {
+ g_object_unref (priv->actions_recent);
+ priv->actions_recent = NULL;
+ }
+
+ if (priv->actions_open_with != NULL) {
+ g_object_unref (priv->actions_open_with);
+ priv->actions_open_with = NULL;
+ }
+
+ fullscreen_clear_timeout (window);
+
+ if (window->priv->fullscreen_popup != NULL) {
+ gtk_widget_destroy (priv->fullscreen_popup);
+ priv->fullscreen_popup = NULL;
+ }
+
+ slideshow_clear_timeout (window);
+
+ g_signal_handlers_disconnect_by_func (gtk_recent_manager_get_default (),
+ G_CALLBACK (eom_window_recent_manager_changed_cb),
+ window);
+
+ priv->recent_menu_id = 0;
+
+ eom_window_clear_load_job (window);
+
+ eom_window_clear_transform_job (window);
+
+ if (priv->client) {
+ int i;
+
+ for (i = 0; i < EOM_WINDOW_NOTIFY_LENGTH; ++i) {
+ mateconf_client_notify_remove (priv->client,
+ priv->client_notifications[i]);
+ }
+ mateconf_client_remove_dir (priv->client, EOM_CONF_DIR, NULL);
+ g_object_unref (priv->client);
+ priv->client = NULL;
+ }
+
+ if (priv->file_list != NULL) {
+ g_slist_foreach (priv->file_list, (GFunc) g_object_unref, NULL);
+ g_slist_free (priv->file_list);
+ priv->file_list = NULL;
+ }
+
+#ifdef HAVE_LCMS
+ if (priv->display_profile != NULL) {
+ cmsCloseProfile (priv->display_profile);
+ priv->display_profile = NULL;
+ }
+#endif
+
+ if (priv->last_save_as_folder != NULL) {
+ g_object_unref (priv->last_save_as_folder);
+ priv->last_save_as_folder = NULL;
+ }
+
+ eom_plugin_engine_garbage_collect ();
+
+ G_OBJECT_CLASS (eom_window_parent_class)->dispose (object);
+}
+
+static void
+eom_window_finalize (GObject *object)
+{
+ GList *windows = eom_application_get_windows (EOM_APP);
+
+ g_return_if_fail (EOM_IS_WINDOW (object));
+
+ eom_debug (DEBUG_WINDOW);
+
+ if (windows == NULL) {
+ eom_application_shutdown (EOM_APP);
+ } else {
+ g_list_free (windows);
+ }
+
+ G_OBJECT_CLASS (eom_window_parent_class)->finalize (object);
+}
+
+static gint
+eom_window_delete (GtkWidget *widget, GdkEventAny *event)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+
+ g_return_val_if_fail (EOM_IS_WINDOW (widget), FALSE);
+
+ window = EOM_WINDOW (widget);
+ priv = window->priv;
+
+ if (priv->save_job != NULL) {
+ eom_window_finish_saving (window);
+ }
+
+ if (eom_window_unsaved_images_confirm (window)) {
+ return TRUE;
+ }
+
+ gtk_widget_destroy (widget);
+
+ return TRUE;
+}
+
+static gint
+eom_window_key_press (GtkWidget *widget, GdkEventKey *event)
+{
+ GtkContainer *tbcontainer = GTK_CONTAINER ((EOM_WINDOW (widget)->priv->toolbar));
+ gint result = FALSE;
+ gboolean handle_selection = FALSE;
+
+ switch (event->keyval) {
+ case GDK_space:
+ if (event->state & GDK_CONTROL_MASK) {
+ handle_selection = TRUE;
+ break;
+ }
+ case GDK_Return:
+ if (gtk_container_get_focus_child (tbcontainer) == NULL) {
+ /* Image properties dialog case */
+ if (event->state & GDK_MOD1_MASK) {
+ result = FALSE;
+ break;
+ }
+
+ if (event->state & GDK_SHIFT_MASK) {
+ eom_window_cmd_go_prev (NULL, EOM_WINDOW (widget));
+ } else {
+ eom_window_cmd_go_next (NULL, EOM_WINDOW (widget));
+ }
+ result = TRUE;
+ }
+ break;
+ case GDK_p:
+ case GDK_P:
+ if (EOM_WINDOW (widget)->priv->mode == EOM_WINDOW_MODE_FULLSCREEN || EOM_WINDOW (widget)->priv->mode == EOM_WINDOW_MODE_SLIDESHOW) {
+ gboolean slideshow;
+
+ slideshow = EOM_WINDOW (widget)->priv->mode == EOM_WINDOW_MODE_SLIDESHOW;
+ eom_window_run_fullscreen (EOM_WINDOW (widget), !slideshow);
+ }
+ break;
+ case GDK_Q:
+ case GDK_q:
+ case GDK_Escape:
+ if (EOM_WINDOW (widget)->priv->mode == EOM_WINDOW_MODE_FULLSCREEN) {
+ eom_window_stop_fullscreen (EOM_WINDOW (widget), FALSE);
+ } else if (EOM_WINDOW (widget)->priv->mode == EOM_WINDOW_MODE_SLIDESHOW) {
+ eom_window_stop_fullscreen (EOM_WINDOW (widget), TRUE);
+ } else {
+ eom_window_cmd_close_window (NULL, EOM_WINDOW (widget));
+ return TRUE;
+ }
+ break;
+ case GDK_Left:
+ if (event->state & GDK_MOD1_MASK) {
+ /* Alt+Left moves to previous image */
+ if (is_rtl) { /* move to next in RTL mode */
+ eom_window_cmd_go_next (NULL, EOM_WINDOW (widget));
+ } else {
+ eom_window_cmd_go_prev (NULL, EOM_WINDOW (widget));
+ }
+ result = TRUE;
+ break;
+ } /* else fall-trough is intended */
+ case GDK_Up:
+ if (eom_scroll_view_scrollbars_visible (EOM_SCROLL_VIEW (EOM_WINDOW (widget)->priv->view))) {
+ /* break to let scrollview handle the key */
+ break;
+ }
+ if (gtk_container_get_focus_child (tbcontainer) != NULL)
+ break;
+ if (!gtk_widget_get_visible (EOM_WINDOW (widget)->priv->nav)) {
+ if (is_rtl && event->keyval == GDK_Left) {
+ /* handle RTL fall-through,
+ * need to behave like GDK_Down then */
+ eom_window_cmd_go_next (NULL,
+ EOM_WINDOW (widget));
+ } else {
+ eom_window_cmd_go_prev (NULL,
+ EOM_WINDOW (widget));
+ }
+ result = TRUE;
+ break;
+ }
+ case GDK_Right:
+ if (event->state & GDK_MOD1_MASK) {
+ /* Alt+Right moves to next image */
+ if (is_rtl) { /* move to previous in RTL mode */
+ eom_window_cmd_go_prev (NULL, EOM_WINDOW (widget));
+ } else {
+ eom_window_cmd_go_next (NULL, EOM_WINDOW (widget));
+ }
+ result = TRUE;
+ break;
+ } /* else fall-trough is intended */
+ case GDK_Down:
+ if (eom_scroll_view_scrollbars_visible (EOM_SCROLL_VIEW (EOM_WINDOW (widget)->priv->view))) {
+ /* break to let scrollview handle the key */
+ break;
+ }
+ if (gtk_container_get_focus_child (tbcontainer) != NULL)
+ break;
+ if (!gtk_widget_get_visible (EOM_WINDOW (widget)->priv->nav)) {
+ if (is_rtl && event->keyval == GDK_Right) {
+ /* handle RTL fall-through,
+ * need to behave like GDK_Up then */
+ eom_window_cmd_go_prev (NULL,
+ EOM_WINDOW (widget));
+ } else {
+ eom_window_cmd_go_next (NULL,
+ EOM_WINDOW (widget));
+ }
+ result = TRUE;
+ break;
+ }
+ case GDK_Page_Up:
+ if (!eom_scroll_view_scrollbars_visible (EOM_SCROLL_VIEW (EOM_WINDOW (widget)->priv->view))) {
+ if (!gtk_widget_get_visible (EOM_WINDOW (widget)->priv->nav)) {
+ /* If the iconview is not visible skip to the
+ * previous image manually as it won't handle
+ * the keypress then. */
+ eom_window_cmd_go_prev (NULL,
+ EOM_WINDOW (widget));
+ result = TRUE;
+ } else
+ handle_selection = TRUE;
+ }
+ break;
+ case GDK_Page_Down:
+ if (!eom_scroll_view_scrollbars_visible (EOM_SCROLL_VIEW (EOM_WINDOW (widget)->priv->view))) {
+ if (!gtk_widget_get_visible (EOM_WINDOW (widget)->priv->nav)) {
+ /* If the iconview is not visible skip to the
+ * next image manually as it won't handle
+ * the keypress then. */
+ eom_window_cmd_go_next (NULL,
+ EOM_WINDOW (widget));
+ result = TRUE;
+ } else
+ handle_selection = TRUE;
+ }
+ break;
+ }
+
+ /* Update slideshow timeout */
+ if (result && (EOM_WINDOW (widget)->priv->mode == EOM_WINDOW_MODE_SLIDESHOW)) {
+ slideshow_set_timeout (EOM_WINDOW (widget));
+ }
+
+ if (handle_selection == TRUE && result == FALSE) {
+ gtk_widget_grab_focus (GTK_WIDGET (EOM_WINDOW (widget)->priv->thumbview));
+
+ result = gtk_widget_event (GTK_WIDGET (EOM_WINDOW (widget)->priv->thumbview),
+ (GdkEvent *) event);
+ }
+
+ /* If the focus is not in the toolbar and we still haven't handled the
+ event, give the scrollview a chance to do it. */
+ if (!gtk_container_get_focus_child (tbcontainer) && result == FALSE &&
+#if GTK_CHECK_VERSION (2, 20, 0)
+ gtk_widget_get_realized (GTK_WIDGET (EOM_WINDOW (widget)->priv->view))) {
+#else
+ GTK_WIDGET_REALIZED(GTK_WIDGET (EOM_WINDOW (widget)->priv->view))) {
+#endif
+ result = gtk_widget_event (GTK_WIDGET (EOM_WINDOW (widget)->priv->view),
+ (GdkEvent *) event);
+ }
+
+ if (result == FALSE && GTK_WIDGET_CLASS (eom_window_parent_class)->key_press_event) {
+ result = (* GTK_WIDGET_CLASS (eom_window_parent_class)->key_press_event) (widget, event);
+ }
+
+ return result;
+}
+
+static gint
+eom_window_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+ EomWindow *window = EOM_WINDOW (widget);
+ gint result = FALSE;
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ switch (event->button) {
+ case 6:
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (window->priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_LEFT);
+ result = TRUE;
+ break;
+ case 7:
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (window->priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_RIGHT);
+ result = TRUE;
+ break;
+ }
+ }
+
+ if (result == FALSE && GTK_WIDGET_CLASS (eom_window_parent_class)->button_press_event) {
+ result = (* GTK_WIDGET_CLASS (eom_window_parent_class)->button_press_event) (widget, event);
+ }
+
+ return result;
+}
+
+static gboolean
+eom_window_configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ EomWindow *window;
+
+ g_return_val_if_fail (EOM_IS_WINDOW (widget), TRUE);
+
+ window = EOM_WINDOW (widget);
+
+ GTK_WIDGET_CLASS (eom_window_parent_class)->configure_event (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+eom_window_window_state_event (GtkWidget *widget,
+ GdkEventWindowState *event)
+{
+ EomWindow *window;
+
+ g_return_val_if_fail (EOM_IS_WINDOW (widget), TRUE);
+
+ window = EOM_WINDOW (widget);
+
+ if (event->changed_mask &
+ (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) {
+ gboolean show;
+
+ show = !(event->new_window_state &
+ (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN));
+
+ eom_statusbar_set_has_resize_grip (EOM_STATUSBAR (window->priv->statusbar),
+ show);
+ }
+
+ return FALSE;
+}
+
+static void
+eom_window_unrealize (GtkWidget *widget)
+{
+ EomWindow *window;
+
+ g_return_if_fail (EOM_IS_WINDOW (widget));
+
+ window = EOM_WINDOW (widget);
+
+ GTK_WIDGET_CLASS (eom_window_parent_class)->unrealize (widget);
+}
+
+
+static gboolean
+eom_window_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
+{
+ EomWindow *window = EOM_WINDOW (widget);
+ EomWindowPrivate *priv = window->priv;
+ gboolean fullscreen;
+
+ eom_debug (DEBUG_WINDOW);
+
+ fullscreen = priv->mode == EOM_WINDOW_MODE_FULLSCREEN ||
+ priv->mode == EOM_WINDOW_MODE_SLIDESHOW;
+
+ if (fullscreen) {
+ gtk_widget_hide_all (priv->fullscreen_popup);
+ }
+
+ return GTK_WIDGET_CLASS (eom_window_parent_class)->focus_out_event (widget, event);
+}
+
+static void
+eom_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (object));
+
+ window = EOM_WINDOW (object);
+ priv = window->priv;
+
+ switch (property_id) {
+ case PROP_STARTUP_FLAGS:
+ priv->flags = g_value_get_flags (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+eom_window_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+
+ g_return_if_fail (EOM_IS_WINDOW (object));
+
+ window = EOM_WINDOW (object);
+ priv = window->priv;
+
+ switch (property_id) {
+ case PROP_STARTUP_FLAGS:
+ g_value_set_flags (value, priv->flags);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GObject *
+eom_window_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+
+ object = G_OBJECT_CLASS (eom_window_parent_class)->constructor
+ (type, n_construct_properties, construct_params);
+
+ eom_window_construct_ui (EOM_WINDOW (object));
+
+ eom_plugin_engine_update_plugins_ui (EOM_WINDOW (object), TRUE);
+
+ return object;
+}
+
+static void
+eom_window_class_init (EomWindowClass *class)
+{
+ GObjectClass *g_object_class = (GObjectClass *) class;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
+
+ g_object_class->constructor = eom_window_constructor;
+ g_object_class->dispose = eom_window_dispose;
+ g_object_class->finalize = eom_window_finalize;
+ g_object_class->set_property = eom_window_set_property;
+ g_object_class->get_property = eom_window_get_property;
+
+ widget_class->delete_event = eom_window_delete;
+ widget_class->key_press_event = eom_window_key_press;
+ widget_class->button_press_event = eom_window_button_press;
+ widget_class->drag_data_received = eom_window_drag_data_received;
+ widget_class->configure_event = eom_window_configure_event;
+ widget_class->window_state_event = eom_window_window_state_event;
+ widget_class->unrealize = eom_window_unrealize;
+ widget_class->focus_out_event = eom_window_focus_out_event;
+
+/**
+ * EomWindow:startup-flags:
+ *
+ * A bitwise OR of #EomStartupFlags elements, indicating how the window
+ * should behave upon creation.
+ */
+ g_object_class_install_property (g_object_class,
+ PROP_STARTUP_FLAGS,
+ g_param_spec_flags ("startup-flags",
+ NULL,
+ NULL,
+ EOM_TYPE_STARTUP_FLAGS,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+/**
+ * EomWindow::prepared:
+ * @window: the object which received the signal.
+ *
+ * The #EomWindow::prepared signal is emitted when the @window is ready
+ * to be shown.
+ */
+ signals [SIGNAL_PREPARED] =
+ g_signal_new ("prepared",
+ EOM_TYPE_WINDOW,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EomWindowClass, prepared),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (g_object_class, sizeof (EomWindowPrivate));
+}
+
+/**
+ * eom_window_new:
+ * @flags: the initialization parameters for the new window.
+ *
+ *
+ * Creates a new and empty #EomWindow. Use @flags to indicate
+ * if the window should be initialized fullscreen, in slideshow mode,
+ * and/or without the thumbnails collection visible. See #EomStartupFlags.
+ *
+ * Returns: a newly created #EomWindow.
+ **/
+GtkWidget*
+eom_window_new (EomStartupFlags flags)
+{
+ EomWindow *window;
+
+ eom_debug (DEBUG_WINDOW);
+
+ window = EOM_WINDOW (g_object_new (EOM_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "startup-flags", flags,
+ NULL));
+
+ return GTK_WIDGET (window);
+}
+
+static void
+eom_window_list_store_image_added (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ update_image_pos (window);
+ update_action_groups_state (window);
+}
+
+static void
+eom_window_list_store_image_removed (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ EomWindow *window = EOM_WINDOW (user_data);
+
+ update_image_pos (window);
+ update_action_groups_state (window);
+}
+
+static void
+eom_job_model_cb (EomJobModel *job, gpointer data)
+{
+ EomWindow *window;
+ EomWindowPrivate *priv;
+ gint n_images;
+
+ eom_debug (DEBUG_WINDOW);
+
+#ifdef HAVE_EXIF
+ int i;
+ EomImage *image;
+#endif
+
+ g_return_if_fail (EOM_IS_WINDOW (data));
+
+ window = EOM_WINDOW (data);
+ priv = window->priv;
+
+ if (priv->store != NULL) {
+ g_object_unref (priv->store);
+ priv->store = NULL;
+ }
+
+ priv->store = g_object_ref (job->store);
+
+ n_images = eom_list_store_length (EOM_LIST_STORE (priv->store));
+
+#ifdef HAVE_EXIF
+ if (mateconf_client_get_bool (priv->client, EOM_CONF_VIEW_AUTOROTATE, NULL)) {
+ for (i = 0; i < n_images; i++) {
+ image = eom_list_store_get_image_by_pos (priv->store, i);
+ eom_image_autorotate (image);
+ g_object_unref (image);
+ }
+ }
+#endif
+
+ eom_thumb_view_set_model (EOM_THUMB_VIEW (priv->thumbview), priv->store);
+
+ g_signal_connect (G_OBJECT (priv->store),
+ "row-inserted",
+ G_CALLBACK (eom_window_list_store_image_added),
+ window);
+
+ g_signal_connect (G_OBJECT (priv->store),
+ "row-deleted",
+ G_CALLBACK (eom_window_list_store_image_removed),
+ window);
+
+ if (n_images == 0) {
+ gint n_files;
+
+ priv->status = EOM_WINDOW_STATUS_NORMAL;
+ update_action_groups_state (window);
+
+ n_files = g_slist_length (priv->file_list);
+
+ if (n_files > 0) {
+ GtkWidget *message_area;
+ GFile *file = NULL;
+
+ if (n_files == 1) {
+ file = (GFile *) priv->file_list->data;
+ }
+
+ message_area = eom_no_images_error_message_area_new (file);
+
+ eom_window_set_message_area (window, message_area);
+
+ gtk_widget_show (message_area);
+ }
+
+ g_signal_emit (window, signals[SIGNAL_PREPARED], 0);
+ }
+}
+
+/**
+ * eom_window_open_file_list:
+ * @window: An #EomWindow.
+ * @file_list: A %NULL-terminated list of #GFile's.
+ *
+ * Opens a list of files, adding them to the collection in @window.
+ * Files will be checked to be readable and later filtered according
+ * with eom_list_store_add_files().
+ **/
+void
+eom_window_open_file_list (EomWindow *window, GSList *file_list)
+{
+ EomJob *job;
+
+ eom_debug (DEBUG_WINDOW);
+
+ window->priv->status = EOM_WINDOW_STATUS_INIT;
+
+ g_slist_foreach (file_list, (GFunc) g_object_ref, NULL);
+ window->priv->file_list = file_list;
+
+ job = eom_job_model_new (file_list);
+
+ g_signal_connect (job,
+ "finished",
+ G_CALLBACK (eom_job_model_cb),
+ window);
+
+ eom_job_queue_add_job (job);
+ g_object_unref (job);
+}
+
+/**
+ * eom_window_get_ui_manager:
+ * @window: An #EomWindow.
+ *
+ * Gets the #GtkUIManager that describes the UI of @window.
+ *
+ * Returns: A #GtkUIManager.
+ **/
+GtkUIManager *
+eom_window_get_ui_manager (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->ui_mgr;
+}
+
+/**
+ * eom_window_get_mode:
+ * @window: An #EomWindow.
+ *
+ * Gets the mode of @window. See #EomWindowMode for details.
+ *
+ * Returns: An #EomWindowMode.
+ **/
+EomWindowMode
+eom_window_get_mode (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), EOM_WINDOW_MODE_UNKNOWN);
+
+ return window->priv->mode;
+}
+
+/**
+ * eom_window_set_mode:
+ * @window: an #EomWindow.
+ * @mode: an #EomWindowMode value.
+ *
+ * Changes the mode of @window to normal, fullscreen, or slideshow.
+ * See #EomWindowMode for details.
+ **/
+void
+eom_window_set_mode (EomWindow *window, EomWindowMode mode)
+{
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ if (window->priv->mode == mode)
+ return;
+
+ switch (mode) {
+ case EOM_WINDOW_MODE_NORMAL:
+ eom_window_stop_fullscreen (window,
+ window->priv->mode == EOM_WINDOW_MODE_SLIDESHOW);
+ break;
+ case EOM_WINDOW_MODE_FULLSCREEN:
+ eom_window_run_fullscreen (window, FALSE);
+ break;
+ case EOM_WINDOW_MODE_SLIDESHOW:
+ eom_window_run_fullscreen (window, TRUE);
+ break;
+ case EOM_WINDOW_MODE_UNKNOWN:
+ break;
+ }
+}
+
+/**
+ * eom_window_get_store:
+ * @window: An #EomWindow.
+ *
+ * Gets the #EomListStore that contains the images in the collection
+ * of @window.
+ *
+ * Returns: an #EomListStore.
+ **/
+EomListStore *
+eom_window_get_store (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return EOM_LIST_STORE (window->priv->store);
+}
+
+/**
+ * eom_window_get_view:
+ * @window: An #EomWindow.
+ *
+ * Gets the #EomScrollView in the window.
+ *
+ * Returns: the #EomScrollView.
+ **/
+GtkWidget *
+eom_window_get_view (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->view;
+}
+
+/**
+ * eom_window_get_sidebar:
+ * @window: An #EomWindow.
+ *
+ * Gets the sidebar widget of @window.
+ *
+ * Returns: the #EomSidebar.
+ **/
+GtkWidget *
+eom_window_get_sidebar (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->sidebar;
+}
+
+/**
+ * eom_window_get_thumb_view:
+ * @window: an #EomWindow.
+ *
+ * Gets the thumbnails view in @window.
+ *
+ * Returns: an #EomThumbView.
+ **/
+GtkWidget *
+eom_window_get_thumb_view (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->thumbview;
+}
+
+/**
+ * eom_window_get_thumb_nav:
+ * @window: an #EomWindow.
+ *
+ * Gets the thumbnails navigation pane in @window.
+ *
+ * Returns: an #EomThumbNav.
+ **/
+GtkWidget *
+eom_window_get_thumb_nav (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->nav;
+}
+
+/**
+ * eom_window_get_statusbar:
+ * @window: an #EomWindow.
+ *
+ * Gets the statusbar in @window.
+ *
+ * Returns: a #EomStatusBar.
+ **/
+GtkWidget *
+eom_window_get_statusbar (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->statusbar;
+}
+
+/**
+ * eom_window_get_image:
+ * @window: an #EomWindow.
+ *
+ * Gets the image currently displayed in @window or %NULL if
+ * no image is being displayed.
+ *
+ * Returns: an #EomImage.
+ **/
+EomImage *
+eom_window_get_image (EomWindow *window)
+{
+ g_return_val_if_fail (EOM_IS_WINDOW (window), NULL);
+
+ return window->priv->image;
+}
+
+/**
+ * eom_window_is_empty:
+ * @window: an #EomWindow.
+ *
+ * Tells whether @window is currently empty or not.
+ *
+ * Returns: %TRUE if @window has no images, %FALSE otherwise.
+ **/
+gboolean
+eom_window_is_empty (EomWindow *window)
+{
+ EomWindowPrivate *priv;
+ gboolean empty = TRUE;
+
+ eom_debug (DEBUG_WINDOW);
+
+ g_return_val_if_fail (EOM_IS_WINDOW (window), FALSE);
+
+ priv = window->priv;
+
+ if (priv->store != NULL) {
+ empty = (eom_list_store_length (EOM_LIST_STORE (priv->store)) == 0);
+ }
+
+ return empty;
+}
+
+void
+eom_window_reload_image (EomWindow *window)
+{
+ GtkWidget *view;
+
+ g_return_if_fail (EOM_IS_WINDOW (window));
+
+ if (window->priv->image == NULL)
+ return;
+
+ g_object_unref (window->priv->image);
+ window->priv->image = NULL;
+
+ view = eom_window_get_view (window);
+ eom_scroll_view_set_image (EOM_SCROLL_VIEW (view), NULL);
+
+ eom_thumb_view_select_single (EOM_THUMB_VIEW (window->priv->thumbview),
+ EOM_THUMB_VIEW_SELECT_CURRENT);
+}
diff --git a/src/eom-window.h b/src/eom-window.h
new file mode 100644
index 0000000..5e507c6
--- /dev/null
+++ b/src/eom-window.h
@@ -0,0 +1,122 @@
+/* Eye of Mate - Main Window
+ *
+ * Copyright (C) 2000-2008 The Free Software Foundation
+ *
+ * Author: Lucas Rocha <[email protected]>
+ *
+ * Based on code by:
+ * - Federico Mena-Quintero <[email protected]>
+ * - Jens Finke <[email protected]>
+ * Based on evince code (shell/ev-window.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.
+ */
+
+#ifndef __EOM_WINDOW_H__
+#define __EOM_WINDOW_H__
+
+#include "eom-list-store.h"
+#include "eom-image.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EomWindow EomWindow;
+typedef struct _EomWindowClass EomWindowClass;
+typedef struct _EomWindowPrivate EomWindowPrivate;
+
+#define EOM_TYPE_WINDOW (eom_window_get_type ())
+#define EOM_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_WINDOW, EomWindow))
+#define EOM_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_WINDOW, EomWindowClass))
+#define EOM_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_WINDOW))
+#define EOM_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_WINDOW))
+#define EOM_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EOM_TYPE_WINDOW, EomWindowClass))
+
+#define EOM_WINDOW_ERROR (eom_window_error_quark ())
+
+typedef enum {
+ EOM_WINDOW_MODE_UNKNOWN,
+ EOM_WINDOW_MODE_NORMAL,
+ EOM_WINDOW_MODE_FULLSCREEN,
+ EOM_WINDOW_MODE_SLIDESHOW
+} EomWindowMode;
+
+//TODO
+typedef enum {
+ EOM_WINDOW_ERROR_CONTROL_NOT_FOUND,
+ EOM_WINDOW_ERROR_UI_NOT_FOUND,
+ EOM_WINDOW_ERROR_NO_PERSIST_FILE_INTERFACE,
+ EOM_WINDOW_ERROR_IO,
+ EOM_WINDOW_ERROR_TRASH_NOT_FOUND,
+ EOM_WINDOW_ERROR_GENERIC,
+ EOM_WINDOW_ERROR_UNKNOWN
+} EomWindowError;
+
+typedef enum {
+ EOM_STARTUP_FULLSCREEN = 1 << 0,
+ EOM_STARTUP_SLIDE_SHOW = 1 << 1,
+ EOM_STARTUP_DISABLE_COLLECTION = 1 << 2
+} EomStartupFlags;
+
+struct _EomWindow {
+ GtkWindow win;
+
+ EomWindowPrivate *priv;
+};
+
+struct _EomWindowClass {
+ GtkWindowClass parent_class;
+
+ void (* prepared) (EomWindow *window);
+};
+
+GType eom_window_get_type (void) G_GNUC_CONST;
+
+GtkWidget *eom_window_new (EomStartupFlags flags);
+
+EomWindowMode eom_window_get_mode (EomWindow *window);
+
+void eom_window_set_mode (EomWindow *window,
+ EomWindowMode mode);
+
+GtkUIManager *eom_window_get_ui_manager (EomWindow *window);
+
+EomListStore *eom_window_get_store (EomWindow *window);
+
+GtkWidget *eom_window_get_view (EomWindow *window);
+
+GtkWidget *eom_window_get_sidebar (EomWindow *window);
+
+GtkWidget *eom_window_get_thumb_view (EomWindow *window);
+
+GtkWidget *eom_window_get_thumb_nav (EomWindow *window);
+
+GtkWidget *eom_window_get_statusbar (EomWindow *window);
+
+EomImage *eom_window_get_image (EomWindow *window);
+
+void eom_window_open_file_list (EomWindow *window,
+ GSList *file_list);
+
+gboolean eom_window_is_empty (EomWindow *window);
+
+void eom_window_reload_image (EomWindow *window);
+G_END_DECLS
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..f4232ef
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,270 @@
+/* Eye Of Mate - Main
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_DBUS
+#include <dbus/dbus-glib-bindings.h>
+#include <gdk/gdkx.h>
+#endif
+
+#include "eom-session.h"
+#include "eom-debug.h"
+#include "eom-thumbnail.h"
+#include "eom-job-queue.h"
+#include "eom-application.h"
+#include "eom-plugin-engine.h"
+#include "eom-util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#if HAVE_EXEMPI
+#include <exempi/xmp.h>
+#endif
+
+static EomStartupFlags flags;
+
+static gboolean fullscreen = FALSE;
+static gboolean slide_show = FALSE;
+static gboolean disable_collection = FALSE;
+#if HAVE_DBUS
+static gboolean force_new_instance = FALSE;
+#endif
+static gchar **startup_files = NULL;
+
+static gboolean
+_print_version_and_exit (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ g_print("%s %s\n", _("Eye of MATE Image Viewer"), VERSION);
+ exit (EXIT_SUCCESS);
+ return TRUE;
+}
+
+static const GOptionEntry goption_options[] =
+{
+ { "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &fullscreen, N_("Open in fullscreen mode"), NULL },
+ { "disable-image-collection", 'c', 0, G_OPTION_ARG_NONE, &disable_collection, N_("Disable image collection"), NULL },
+ { "slide-show", 's', 0, G_OPTION_ARG_NONE, &slide_show, N_("Open in slideshow mode"), NULL },
+#if HAVE_DBUS
+ { "new-instance", 'n', 0, G_OPTION_ARG_NONE, &force_new_instance, N_("Start a new instance instead of reusing an existing one"), NULL },
+#endif
+ { "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ _print_version_and_exit, N_("Show the application's version"), NULL},
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &startup_files, NULL, N_("[FILE…]") },
+ { NULL }
+};
+
+static void
+set_startup_flags (void)
+{
+ if (fullscreen)
+ flags |= EOM_STARTUP_FULLSCREEN;
+
+ if (disable_collection)
+ flags |= EOM_STARTUP_DISABLE_COLLECTION;
+
+ if (slide_show)
+ flags |= EOM_STARTUP_SLIDE_SHOW;
+}
+
+static void
+load_files (void)
+{
+ GSList *files = NULL;
+
+ files = eom_util_string_array_to_list ((const gchar **) startup_files, TRUE);
+
+ eom_application_open_uri_list (EOM_APP,
+ files,
+ GDK_CURRENT_TIME,
+ flags,
+ NULL);
+
+ g_slist_foreach (files, (GFunc) g_free, NULL);
+ g_slist_free (files);
+}
+
+#ifdef HAVE_DBUS
+static gboolean
+load_files_remote (void)
+{
+ GError *error = NULL;
+ DBusGConnection *connection;
+ DBusGProxy *remote_object;
+ gboolean result = TRUE;
+ GdkDisplay *display;
+ guint32 timestamp;
+ gchar **files;
+
+ display = gdk_display_get_default ();
+
+ timestamp = gdk_x11_display_get_user_time (display);
+ connection = dbus_g_bus_get (DBUS_BUS_STARTER, &error);
+
+ if (connection == NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ files = eom_util_string_array_make_absolute (startup_files);
+
+ remote_object = dbus_g_proxy_new_for_name (connection,
+ "org.mate.eom.ApplicationService",
+ "/org/mate/eom/Eom",
+ "org.mate.eom.Application");
+
+ if (!files) {
+ if (!dbus_g_proxy_call (remote_object, "OpenWindow", &error,
+ G_TYPE_UINT, timestamp,
+ G_TYPE_UCHAR, flags,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID)) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+
+ result = FALSE;
+ }
+ } else {
+ if (!dbus_g_proxy_call (remote_object, "OpenUris", &error,
+ G_TYPE_STRV, files,
+ G_TYPE_UINT, timestamp,
+ G_TYPE_UCHAR, flags,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID)) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+
+ result = FALSE;
+ }
+
+ g_strfreev (files);
+ }
+
+ g_object_unref (remote_object);
+ dbus_g_connection_unref (connection);
+
+ gdk_notify_startup_complete ();
+
+ return result;
+}
+#endif /* HAVE_DBUS */
+
+int
+main (int argc, char **argv)
+{
+ GError *error = NULL;
+ GOptionContext *ctx;
+
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
+ bindtextdomain (PACKAGE, EOM_LOCALE_DIR);
+ bind_textdomain_codeset (PACKAGE, "UTF-8");
+ textdomain (PACKAGE);
+
+ gtk_rc_parse (EOM_DATA_DIR G_DIR_SEPARATOR_S "gtkrc");
+
+ ctx = g_option_context_new (NULL);
+ g_option_context_add_main_entries (ctx, goption_options, PACKAGE);
+ /* Option groups are free'd together with the context
+ * Using gtk_get_option_group here initializes gtk during parsing */
+ g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
+
+ if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
+ gchar *help_msg;
+
+ /* I18N: The '%s' is replaced with eom's command name. */
+ help_msg = g_strdup_printf (_("Run '%s --help' to see a full "
+ "list of available command line "
+ "options."), argv[0]);
+ g_printerr ("%s\n%s\n", error->message, help_msg);
+ g_error_free (error);
+ g_free (help_msg);
+ g_option_context_free (ctx);
+
+ return 1;
+ }
+ g_option_context_free (ctx);
+
+
+ set_startup_flags ();
+
+#ifdef HAVE_DBUS
+ if (!force_new_instance &&
+ !eom_application_register_service (EOM_APP)) {
+ if (load_files_remote ()) {
+ return 0;
+ }
+ }
+#endif /* HAVE_DBUS */
+
+#ifdef HAVE_EXEMPI
+ xmp_init();
+#endif
+#ifdef HAVE_RSVG
+ rsvg_init();
+#endif
+ eom_debug_init ();
+ eom_job_queue_init ();
+ gdk_threads_init ();
+ eom_thumbnail_init ();
+ eom_plugin_engine_init ();
+
+ /* Add application specific icons to search path */
+ gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
+ EOM_DATA_DIR G_DIR_SEPARATOR_S "icons");
+
+ gtk_window_set_default_icon_name ("eom");
+ g_set_application_name (_("Eye of MATE Image Viewer"));
+
+ load_files ();
+
+ gdk_threads_enter ();
+
+ gtk_main ();
+
+ gdk_threads_leave ();
+
+ if (startup_files)
+ g_strfreev (startup_files);
+
+ eom_plugin_engine_shutdown ();
+
+#ifdef HAVE_RSVG
+ rsvg_term();
+#endif
+#ifdef HAVE_EXEMPI
+ xmp_terminate();
+#endif
+ return 0;
+}
diff --git a/src/uta.c b/src/uta.c
new file mode 100644
index 0000000..8df97bc
--- /dev/null
+++ b/src/uta.c
@@ -0,0 +1,1116 @@
+/* Eye of Mate image viewer - Microtile array utilities
+ *
+ * Copyright (C) 2000-2009 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <[email protected]>
+ *
+ * Portions based on code from libart_lgpl by Raph Levien.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include "uta.h"
+
+#define EOM_UTA_BBOX_CONS(x0, y0, x1, y1) (((x0) << 24) | ((y0) << 16) | \
+ ((x1) << 8) | (y1))
+
+#define EOM_UTA_BBOX_X0(ub) ((ub) >> 24)
+#define EOM_UTA_BBOX_Y0(ub) (((ub) >> 16) & 0xff)
+#define EOM_UTA_BBOX_X1(ub) (((ub) >> 8) & 0xff)
+#define EOM_UTA_BBOX_Y1(ub) ((ub) & 0xff)
+
+#define eom_renew(p, type, n) ((type *)g_realloc (p, (n) * sizeof(type)))
+/* This one must be used carefully - in particular, p and max should
+ be variables. They can also be pstruct->el lvalues. */
+#define eom_expand(p, type, max) do { if(max) { p = eom_renew (p, type, max <<= 1); } else { max = 1; p = g_new(type, 1); } } while (0)
+
+
+
+/**
+ * eom_uta_new: Allocate a new uta.
+ * @x0: Left coordinate of uta.
+ * @y0: Top coordinate of uta.
+ * @x1: Right coordinate of uta.
+ * @y1: Bottom coordinate of uta.
+ *
+ * Allocates a new microtile array. The arguments are in units of
+ * tiles, not pixels.
+ *
+ * Returns: the newly allocated #EomUta.
+ **/
+static EomUta *
+eom_uta_new (int x0, int y0, int x1, int y1)
+{
+ EomUta *uta;
+
+ uta = g_new (EomUta, 1);
+ uta->x0 = x0;
+ uta->y0 = y0;
+ uta->width = x1 - x0;
+ uta->height = y1 - y0;
+
+ uta->utiles = g_new0 (EomUtaBbox, uta->width * uta->height);
+
+ return uta;
+}
+
+/**
+ * eom_uta_free: Free a uta.
+ * @uta: The uta to free.
+ *
+ * Frees the microtile array structure, including the actual microtile
+ * data.
+ **/
+void
+eom_uta_free (EomUta *uta)
+{
+ g_free (uta->utiles);
+ g_free (uta);
+}
+
+/**
+ * eom_irect_intersect: Find intersection of two integer rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the intersection of @src1 and @src2.
+ **/
+void
+eom_irect_intersect (EomIRect *dest, const EomIRect *src1, const EomIRect *src2) {
+ dest->x0 = MAX (src1->x0, src2->x0);
+ dest->y0 = MAX (src1->y0, src2->y0);
+ dest->x1 = MIN (src1->x1, src2->x1);
+ dest->y1 = MIN (src1->y1, src2->y1);
+}
+
+/**
+ * eom_irect_empty: Determine whether integer rectangle is empty.
+ * @src: The source rectangle.
+ *
+ * Return value: TRUE if @src is an empty rectangle, FALSE otherwise.
+ **/
+int
+eom_irect_empty (const EomIRect *src) {
+ return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+/**
+ * eom_uta_from_irect: Generate uta covering a rectangle.
+ * @bbox: The source rectangle.
+ *
+ * Generates a uta exactly covering @bbox. Please do not call this
+ * function with a @bbox with zero height or width.
+ *
+ * Return value: the new uta.
+ **/
+static EomUta *
+eom_uta_from_irect (EomIRect *bbox)
+{
+ EomUta *uta;
+ EomUtaBbox *utiles;
+ EomUtaBbox bb;
+ int width, height;
+ int x, y;
+ int xf0, yf0, xf1, yf1;
+ int ix;
+
+ uta = g_new (EomUta, 1);
+ uta->x0 = bbox->x0 >> EOM_UTILE_SHIFT;
+ uta->y0 = bbox->y0 >> EOM_UTILE_SHIFT;
+ width = ((bbox->x1 + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT) - uta->x0;
+ height = ((bbox->y1 + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT) - uta->y0;
+ utiles = g_new (EomUtaBbox, width * height);
+
+ uta->width = width;
+ uta->height = height;
+ uta->utiles = utiles;
+
+ xf0 = bbox->x0 & (EOM_UTILE_SIZE - 1);
+ yf0 = bbox->y0 & (EOM_UTILE_SIZE - 1);
+ xf1 = ((bbox->x1 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+ yf1 = ((bbox->y1 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+ if (height == 1)
+ {
+ if (width == 1)
+ utiles[0] = EOM_UTA_BBOX_CONS (xf0, yf0, xf1, yf1);
+ else
+ {
+ utiles[0] = EOM_UTA_BBOX_CONS (xf0, yf0, EOM_UTILE_SIZE, yf1);
+ bb = EOM_UTA_BBOX_CONS (0, yf0, EOM_UTILE_SIZE, yf1);
+ for (x = 1; x < width - 1; x++)
+ utiles[x] = bb;
+ utiles[x] = EOM_UTA_BBOX_CONS (0, yf0, xf1, yf1);
+ }
+ }
+ else
+ {
+ if (width == 1)
+ {
+ utiles[0] = EOM_UTA_BBOX_CONS (xf0, yf0, xf1, EOM_UTILE_SIZE);
+ bb = EOM_UTA_BBOX_CONS (xf0, 0, xf1, EOM_UTILE_SIZE);
+ for (y = 1; y < height - 1; y++)
+ utiles[y] = bb;
+ utiles[y] = EOM_UTA_BBOX_CONS (xf0, 0, xf1, yf1);
+ }
+ else
+ {
+ utiles[0] =
+ EOM_UTA_BBOX_CONS (xf0, yf0, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ bb = EOM_UTA_BBOX_CONS (0, yf0, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ for (x = 1; x < width - 1; x++)
+ utiles[x] = bb;
+ utiles[x] = EOM_UTA_BBOX_CONS (0, yf0, xf1, EOM_UTILE_SIZE);
+ ix = width;
+ for (y = 1; y < height - 1; y++)
+ {
+ utiles[ix++] =
+ EOM_UTA_BBOX_CONS (xf0, 0, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ bb = EOM_UTA_BBOX_CONS (0, 0, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ for (x = 1; x < width - 1; x++)
+ utiles[ix++] = bb;
+ utiles[ix++] = EOM_UTA_BBOX_CONS (0, 0, xf1, EOM_UTILE_SIZE);
+ }
+ utiles[ix++] = EOM_UTA_BBOX_CONS (xf0, 0, EOM_UTILE_SIZE, yf1);
+ bb = EOM_UTA_BBOX_CONS (0, 0, EOM_UTILE_SIZE, yf1);
+ for (x = 1; x < width - 1; x++)
+ utiles[ix++] = bb;
+ utiles[ix++] = EOM_UTA_BBOX_CONS (0, 0, xf1, yf1);
+ }
+ }
+ return uta;
+}
+
+
+/**
+ * uta_ensure_size:
+ * @uta: A microtile array.
+ * @x1: Left microtile coordinate that must fit in new array.
+ * @y1: Top microtile coordinate that must fit in new array.
+ * @x2: Right microtile coordinate that must fit in new array.
+ * @y2: Bottom microtile coordinate that must fit in new array.
+ *
+ * Ensures that the size of a microtile array is big enough to fit the specified
+ * microtile coordinates. If it is not big enough, the specified @uta will be
+ * freed and a new one will be returned. Otherwise, the original @uta will be
+ * returned. If a new microtile array needs to be created, this function will
+ * copy the @uta's contents to the new array.
+ *
+ * Note that the specified coordinates must have already been scaled down by the
+ * ART_UTILE_SHIFT factor.
+ *
+ * Return value: The same value as @uta if the original microtile array was
+ * big enough to fit the specified microtile coordinates, or a new array if
+ * it needed to be grown. In the second case, the original @uta will be
+ * freed automatically.
+ **/
+EomUta *
+uta_ensure_size (EomUta *uta, int x1, int y1, int x2, int y2)
+{
+ EomUta *new_uta;
+ EomUtaBbox *utiles, *new_utiles;
+ int new_ofs, ofs;
+ int x, y;
+
+ g_return_val_if_fail (x1 < x2, NULL);
+ g_return_val_if_fail (y1 < y2, NULL);
+
+ if (!uta)
+ return eom_uta_new (x1, y1, x2, y2);
+
+ if (x1 >= uta->x0
+ && y1 >= uta->y0
+ && x2 <= uta->x0 + uta->width
+ && y2 <= uta->y0 + uta->height)
+ return uta;
+
+ new_uta = g_new (EomUta, 1);
+
+ new_uta->x0 = MIN (uta->x0, x1);
+ new_uta->y0 = MIN (uta->y0, y1);
+ new_uta->width = MAX (uta->x0 + uta->width, x2) - new_uta->x0;
+ new_uta->height = MAX (uta->y0 + uta->height, y2) - new_uta->y0;
+ new_uta->utiles = g_new (EomUtaBbox, new_uta->width * new_uta->height);
+
+ utiles = uta->utiles;
+ new_utiles = new_uta->utiles;
+
+ new_ofs = 0;
+
+ for (y = new_uta->y0; y < new_uta->y0 + new_uta->height; y++) {
+ if (y < uta->y0 || y >= uta->y0 + uta->height)
+ for (x = 0; x < new_uta->width; x++)
+ new_utiles[new_ofs++] = 0;
+ else {
+ ofs = (y - uta->y0) * uta->width;
+
+ for (x = new_uta->x0; x < new_uta->x0 + new_uta->width; x++)
+ if (x < uta->x0 || x >= uta->x0 + uta->width)
+ new_utiles[new_ofs++] = 0;
+ else
+ new_utiles[new_ofs++] = utiles[ofs++];
+ }
+ }
+
+ eom_uta_free (uta);
+ return new_uta;
+}
+
+/**
+ * uta_add_rect:
+ * @uta: A microtile array, or NULL if a new array should be created.
+ * @x1: Left coordinate of rectangle.
+ * @y1: Top coordinate of rectangle.
+ * @x2: Right coordinate of rectangle.
+ * @y2: Bottom coordinate of rectangle.
+ *
+ * Adds the specified rectangle to a microtile array. The array is
+ * grown to fit the rectangle if necessary.
+ *
+ * Return value: The original @uta, or a new microtile array if the original one
+ * needed to be grown to fit the specified rectangle. In the second case, the
+ * original @uta will be freed automatically.
+ **/
+EomUta *
+uta_add_rect (EomUta *uta, int x1, int y1, int x2, int y2)
+{
+ EomUtaBbox *utiles;
+ EomUtaBbox bb;
+ int rect_x1, rect_y1, rect_x2, rect_y2;
+ int xf1, yf1, xf2, yf2;
+ int x, y;
+ int ofs;
+
+ g_return_val_if_fail (x1 < x2, NULL);
+ g_return_val_if_fail (y1 < y2, NULL);
+
+ /* Empty uta */
+
+ if (!uta) {
+ EomIRect r;
+
+ r.x0 = x1;
+ r.y0 = y1;
+ r.x1 = x2;
+ r.y1 = y2;
+
+ return eom_uta_from_irect (&r);
+ }
+
+ /* Grow the uta if necessary */
+
+ rect_x1 = x1 >> EOM_UTILE_SHIFT;
+ rect_y1 = y1 >> EOM_UTILE_SHIFT;
+ rect_x2 = (x2 + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+ rect_y2 = (y2 + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+
+ uta = uta_ensure_size (uta, rect_x1, rect_y1, rect_x2, rect_y2);
+
+ /* Add the rectangle */
+
+ xf1 = x1 & (EOM_UTILE_SIZE - 1);
+ yf1 = y1 & (EOM_UTILE_SIZE - 1);
+ xf2 = ((x2 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+ yf2 = ((y2 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+
+ utiles = uta->utiles;
+
+ ofs = (rect_y1 - uta->y0) * uta->width + rect_x1 - uta->x0;
+
+ if (rect_y2 - rect_y1 == 1) {
+ if (rect_x2 - rect_x1 == 1) {
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ xf1, yf1, xf2, yf2);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+ } else {
+ /* Leftmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ xf1, yf1, EOM_UTILE_SIZE, yf2);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ EOM_UTILE_SIZE,
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+
+ /* Tiles in between */
+ for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ 0, yf1, EOM_UTILE_SIZE, yf2);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ 0,
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ EOM_UTILE_SIZE,
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+ }
+
+ /* Rightmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0, yf1, xf2, yf2);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+ }
+ } else {
+ if (rect_x2 - rect_x1 == 1) {
+ /* Topmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ xf1, yf1, xf2, EOM_UTILE_SIZE);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ EOM_UTILE_SIZE);
+ ofs += uta->width;
+
+ /* Tiles in between */
+ for (y = rect_y1 + 1; y < rect_y2 - 1; y++) {
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ xf1, 0, xf2, EOM_UTILE_SIZE);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ 0,
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ EOM_UTILE_SIZE);
+ ofs += uta->width;
+ }
+
+ /* Bottommost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ xf1, 0, xf2, yf2);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ 0,
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+ } else {
+ /* Top row, leftmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ xf1, yf1, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ EOM_UTILE_SIZE,
+ EOM_UTILE_SIZE);
+
+ /* Top row, in between */
+ for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ 0, yf1, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ 0,
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ EOM_UTILE_SIZE,
+ EOM_UTILE_SIZE);
+ }
+
+ /* Top row, rightmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0, yf1, xf2, EOM_UTILE_SIZE);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ MIN (EOM_UTA_BBOX_Y0 (bb), yf1),
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ EOM_UTILE_SIZE);
+
+ ofs += uta->width - (rect_x2 - rect_x1 - 1);
+
+ /* Rows in between */
+ for (y = rect_y1 + 1; y < rect_y2 - 1; y++) {
+ /* Leftmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ xf1, 0, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ 0,
+ EOM_UTILE_SIZE,
+ EOM_UTILE_SIZE);
+
+ /* Tiles in between */
+ bb = EOM_UTA_BBOX_CONS (0, 0, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ for (x = rect_x1 + 1; x < rect_x2 - 1; x++)
+ utiles[ofs++] = bb;
+
+ /* Rightmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0, 0, xf2, EOM_UTILE_SIZE);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ 0,
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ EOM_UTILE_SIZE);
+
+ ofs += uta->width - (rect_x2 - rect_x1 - 1);
+ }
+
+ /* Bottom row, leftmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ xf1, 0, EOM_UTILE_SIZE, yf2);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (bb), xf1),
+ 0,
+ EOM_UTILE_SIZE,
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+
+ /* Bottom row, tiles in between */
+ for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ 0, 0, EOM_UTILE_SIZE, yf2);
+ else
+ utiles[ofs++] = EOM_UTA_BBOX_CONS (
+ 0,
+ 0,
+ EOM_UTILE_SIZE,
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+ }
+
+ /* Bottom row, rightmost tile */
+ bb = utiles[ofs];
+ if (bb == 0)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0, 0, xf2, yf2);
+ else
+ utiles[ofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ 0,
+ MAX (EOM_UTA_BBOX_X1 (bb), xf2),
+ MAX (EOM_UTA_BBOX_Y1 (bb), yf2));
+ }
+ }
+
+ return uta;
+}
+
+/**
+ * uta_remove_rect:
+ * @uta: A microtile array.
+ * @x1: Left coordinate of rectangle.
+ * @y1: Top coordinate of rectangle.
+ * @x2: Right coordinate of rectangle.
+ * @y2: Bottom coordinate of rectangle.
+ *
+ * Removes a rectangular region from the specified microtile array. Due to the
+ * way microtile arrays represent regions, the tiles at the edge of the
+ * rectangle may not be clipped exactly.
+ **/
+void
+uta_remove_rect (EomUta *uta, int x1, int y1, int x2, int y2)
+{
+ EomUtaBbox *utiles;
+ int rect_x1, rect_y1, rect_x2, rect_y2;
+ int clip_x1, clip_y1, clip_x2, clip_y2;
+ int xf1, yf1, xf2, yf2;
+ int ofs;
+ int x, y;
+
+ g_return_if_fail (uta != NULL);
+ g_return_if_fail (x1 <= x2);
+ g_return_if_fail (y1 <= y2);
+
+ if (x1 == x2 || y1 == y2)
+ return;
+
+ rect_x1 = x1 >> EOM_UTILE_SHIFT;
+ rect_y1 = y1 >> EOM_UTILE_SHIFT;
+ rect_x2 = (x2 + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+ rect_y2 = (y2 + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+
+ clip_x1 = MAX (rect_x1, uta->x0);
+ clip_y1 = MAX (rect_y1, uta->y0);
+ clip_x2 = MIN (rect_x2, uta->x0 + uta->width);
+ clip_y2 = MIN (rect_y2, uta->y0 + uta->height);
+
+ if (clip_x1 >= clip_x2 || clip_y1 >= clip_y2)
+ return;
+
+ xf1 = x1 & (EOM_UTILE_SIZE - 1);
+ yf1 = y1 & (EOM_UTILE_SIZE - 1);
+ xf2 = ((x2 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+ yf2 = ((y2 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+
+ utiles = uta->utiles;
+
+ ofs = (clip_y1 - uta->y0) * uta->width + clip_x1 - uta->x0;
+
+ for (y = clip_y1; y < clip_y2; y++) {
+ int cy1, cy2;
+
+ if (y == rect_y1)
+ cy1 = yf1;
+ else
+ cy1 = 0;
+
+ if (y == rect_y2 - 1)
+ cy2 = yf2;
+ else
+ cy2 = EOM_UTILE_SIZE;
+
+ for (x = clip_x1; x < clip_x2; x++) {
+ int cx1, cx2;
+ EomUtaBbox bb;
+ int bb_x1, bb_y1, bb_x2, bb_y2;
+ int bb_cx1, bb_cy1, bb_cx2, bb_cy2;
+
+ bb = utiles[ofs];
+ bb_x1 = EOM_UTA_BBOX_X0 (bb);
+ bb_y1 = EOM_UTA_BBOX_Y0 (bb);
+ bb_x2 = EOM_UTA_BBOX_X1 (bb);
+ bb_y2 = EOM_UTA_BBOX_Y1 (bb);
+
+ if (x == rect_x1)
+ cx1 = xf1;
+ else
+ cx1 = 0;
+
+ if (x == rect_x2 - 1)
+ cx2 = xf2;
+ else
+ cx2 = EOM_UTILE_SIZE;
+
+ /* Clip top and bottom */
+
+ if (cx1 <= bb_x1 && cx2 >= bb_x2) {
+ if (cy1 <= bb_y1 && cy2 > bb_y1)
+ bb_cy1 = cy2;
+ else
+ bb_cy1 = bb_y1;
+
+ if (cy1 < bb_y2 && cy2 >= bb_y2)
+ bb_cy2 = cy1;
+ else
+ bb_cy2 = bb_y2;
+ } else {
+ bb_cy1 = bb_y1;
+ bb_cy2 = bb_y2;
+ }
+
+ /* Clip left and right */
+
+ if (cy1 <= bb_y1 && cy2 >= bb_y2) {
+ if (cx1 <= bb_x1 && cx2 > bb_x1)
+ bb_cx1 = cx2;
+ else
+ bb_cx1 = bb_x1;
+
+ if (cx1 < bb_x2 && cx2 >= bb_x2)
+ bb_cx2 = cx1;
+ else
+ bb_cx2 = bb_x2;
+ } else {
+ bb_cx1 = bb_x1;
+ bb_cx2 = bb_x2;
+ }
+
+ if (bb_cx1 < bb_cx2 && bb_cy1 < bb_cy2)
+ utiles[ofs] = EOM_UTA_BBOX_CONS (bb_cx1, bb_cy1,
+ bb_cx2, bb_cy2);
+ else
+ utiles[ofs] = 0;
+
+ ofs++;
+ }
+
+ ofs += uta->width - (clip_x2 - clip_x1);
+ }
+}
+
+void
+uta_find_first_glom_rect (EomUta *uta, EomIRect *rect, int max_width, int max_height)
+{
+ EomIRect *rects;
+ int n_rects, n_rects_max;
+ int x, y;
+ int width, height;
+ int ix;
+ int left_ix;
+ EomUtaBbox *utiles;
+ EomUtaBbox bb;
+ int x0, y0, x1, y1;
+ int *glom;
+ int glom_rect;
+
+ n_rects = 0;
+ n_rects_max = 1;
+ rects = g_new (EomIRect, n_rects_max);
+
+ width = uta->width;
+ height = uta->height;
+ utiles = uta->utiles;
+
+ glom = g_new (int, width * height);
+ for (ix = 0; ix < width * height; ix++)
+ glom[ix] = -1;
+
+ ix = 0;
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ bb = utiles[ix];
+ if (bb)
+ {
+ x0 = ((uta->x0 + x) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_X0(bb);
+ y0 = ((uta->y0 + y) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_Y0(bb);
+ y1 = ((uta->y0 + y) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_Y1(bb);
+
+ left_ix = ix;
+ /* now try to extend to the right */
+ while (x != width - 1 &&
+ EOM_UTA_BBOX_X1(bb) == EOM_UTILE_SIZE &&
+ (((bb & 0xffffff) ^ utiles[ix + 1]) & 0xffff00ff) == 0 &&
+ (((uta->x0 + x + 1) << EOM_UTILE_SHIFT) +
+ EOM_UTA_BBOX_X1(utiles[ix + 1]) -
+ x0) <= max_width)
+ {
+ bb = utiles[ix + 1];
+ ix++;
+ x++;
+ }
+ x1 = ((uta->x0 + x) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_X1(bb);
+
+
+ /* if rectangle nonempty */
+ if ((x1 ^ x0) || (y1 ^ y0))
+ {
+ /* try to glom onto an existing rectangle */
+ glom_rect = glom[left_ix];
+ if (glom_rect != -1 &&
+ x0 == rects[glom_rect].x0 &&
+ x1 == rects[glom_rect].x1 &&
+ y0 == rects[glom_rect].y1 &&
+ y1 - rects[glom_rect].y0 <= max_height)
+ {
+ rects[glom_rect].y1 = y1;
+ }
+ else
+ {
+ if (n_rects == n_rects_max)
+ eom_expand (rects, EomIRect, n_rects_max);
+ rects[n_rects].x0 = x0;
+ rects[n_rects].y0 = y0;
+ rects[n_rects].x1 = x1;
+ rects[n_rects].y1 = y1;
+ glom_rect = n_rects;
+ n_rects++;
+ }
+ if (y != height - 1)
+ glom[left_ix + width] = glom_rect;
+ }
+ }
+ ix++;
+ }
+
+ if (n_rects > 0) {
+ rect->x0 = rects[0].x0;
+ rect->y0 = rects[0].y0;
+ rect->x1 = rects[0].x1;
+ rect->y1 = rects[0].y1;
+ } else
+ rect->x0 = rect->y0 = rect->x1 = rect->y1 = 0;
+
+ g_free (glom);
+ g_free (rects);
+}
+
+#if 0
+
+void
+uta_find_first_glom_rect (EomUta *uta, EomIRect *rect, int max_width, int max_height)
+{
+ EomUtaBbox *utiles;
+ EomUtaBbox bb;
+ int width, height;
+ int ofs;
+ int x, y;
+ int x1, y1, x2, y2;
+
+ g_return_if_fail (uta != NULL);
+ g_return_if_fail (rect != NULL);
+ g_return_if_fail (max_width > 0 && max_height > 0);
+
+ utiles = uta->utiles;
+ width = uta->width;
+ height = uta->height;
+
+ ofs = 0;
+
+ /* We find the first nonempty tile, and then grow the rectangle to the
+ * right and then down.
+ */
+
+ x1 = y1 = x2 = y2 = 0;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ bb = utiles[ofs];
+
+ if (!bb) {
+ ofs++;
+ continue;
+ }
+
+ x1 = ((uta->x0 + x) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_X0 (bb);
+ y1 = ((uta->y0 + y) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_Y0 (bb);
+ y2 = ((uta->y0 + y) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_Y1 (bb);
+
+ /* Grow to the right */
+
+ while (x != width - 1
+ && EOM_UTA_BBOX_X1 (bb) == EOM_UTILE_SIZE
+ && (((bb & 0xffffff) ^ utiles[ofs + 1]) & 0xffff00ff) == 0
+ && (((uta->x0 + x + 1) << EOM_UTILE_SHIFT)
+ + EOM_UTA_BBOX_X1 (utiles[ofs + 1])
+ - x1) <= max_width) {
+ ofs++;
+ bb = utiles[ofs];
+ x++;
+ }
+
+ x2 = ((uta->x0 + x) << EOM_UTILE_SHIFT) + EOM_UTA_BBOX_X1 (bb);
+ goto grow_down;
+ }
+ }
+
+ grow_down:
+
+}
+
+#endif
+
+/* Copies a single microtile to another location in the UTA, offsetted by the
+ * specified distance. A microtile can thus end up being added in a single part
+ * to another microtile, in two parts to two horizontally or vertically adjacent
+ * microtiles, or in four parts to a 2x2 square of microtiles.
+ *
+ * This is basically a normal BitBlt but with copying-forwards-to-the-destination
+ * instead of fetching-backwards-from-the-source.
+ */
+static void
+copy_tile (EomUta *uta, int x, int y, int xofs, int yofs)
+{
+ EomUtaBbox *utiles;
+ EomUtaBbox bb, dbb;
+ int t_x1, t_y1, t_x2, t_y2;
+ int d_x1, d_y1, d_x2, d_y2;
+ int d_tx1, d_ty1;
+ int d_xf1, d_yf1, d_xf2, d_yf2;
+ int dofs;
+
+ utiles = uta->utiles;
+
+ bb = utiles[(y - uta->y0) * uta->width + x - uta->x0];
+
+ if (bb == 0)
+ return;
+
+ t_x1 = EOM_UTA_BBOX_X0 (bb) + (x << EOM_UTILE_SHIFT);
+ t_y1 = EOM_UTA_BBOX_Y0 (bb) + (y << EOM_UTILE_SHIFT);
+ t_x2 = EOM_UTA_BBOX_X1 (bb) + (x << EOM_UTILE_SHIFT);
+ t_y2 = EOM_UTA_BBOX_Y1 (bb) + (y << EOM_UTILE_SHIFT);
+
+ d_x1 = t_x1 + xofs;
+ d_y1 = t_y1 + yofs;
+ d_x2 = t_x2 + xofs;
+ d_y2 = t_y2 + yofs;
+
+ d_tx1 = d_x1 >> EOM_UTILE_SHIFT;
+ d_ty1 = d_y1 >> EOM_UTILE_SHIFT;
+
+ dofs = (d_ty1 - uta->y0) * uta->width + d_tx1 - uta->x0;
+
+ d_xf1 = d_x1 & (EOM_UTILE_SIZE - 1);
+ d_yf1 = d_y1 & (EOM_UTILE_SIZE - 1);
+ d_xf2 = ((d_x2 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+ d_yf2 = ((d_y2 - 1) & (EOM_UTILE_SIZE - 1)) + 1;
+
+ if (d_x2 - d_x1 <= EOM_UTILE_SIZE - d_xf1) {
+ if (d_y2 - d_y1 <= EOM_UTILE_SIZE - d_yf1) {
+ if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
+ && d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ d_xf1, d_yf1, d_xf2, d_yf2);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (dbb), d_xf1),
+ MIN (EOM_UTA_BBOX_Y0 (dbb), d_yf1),
+ MAX (EOM_UTA_BBOX_X1 (dbb), d_xf2),
+ MAX (EOM_UTA_BBOX_Y1 (dbb), d_yf2));
+ }
+ } else {
+ /* Top tile */
+ if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
+ && d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ d_xf1, d_yf1, d_xf2, EOM_UTILE_SIZE);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (dbb), d_xf1),
+ MIN (EOM_UTA_BBOX_Y0 (dbb), d_yf1),
+ MAX (EOM_UTA_BBOX_X1 (dbb), d_xf2),
+ EOM_UTILE_SIZE);
+ }
+
+ dofs += uta->width;
+
+ /* Bottom tile */
+ if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
+ && d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ d_xf1, 0, d_xf2, d_yf2);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (dbb), d_xf1),
+ 0,
+ MAX (EOM_UTA_BBOX_X1 (dbb), d_xf2),
+ MAX (EOM_UTA_BBOX_Y1 (dbb), d_yf2));
+ }
+ }
+ } else {
+ if (d_y2 - d_y1 <= EOM_UTILE_SIZE - d_yf1) {
+ /* Left tile */
+ if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
+ && d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ d_xf1, d_yf1, EOM_UTILE_SIZE, d_yf2);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (dbb), d_xf1),
+ MIN (EOM_UTA_BBOX_Y0 (dbb), d_yf1),
+ EOM_UTILE_SIZE,
+ MAX (EOM_UTA_BBOX_Y1 (dbb), d_yf2));
+ }
+
+ dofs++;
+
+ /* Right tile */
+ if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
+ && d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ 0, d_yf1, d_xf2, d_yf2);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ MIN (EOM_UTA_BBOX_Y0 (dbb), d_yf1),
+ MAX (EOM_UTA_BBOX_X1 (dbb), d_xf2),
+ MAX (EOM_UTA_BBOX_Y1 (dbb), d_yf2));
+ }
+ } else {
+ /* Top left tile */
+ if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
+ && d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ d_xf1, d_yf1, EOM_UTILE_SIZE, EOM_UTILE_SIZE);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (dbb), d_xf1),
+ MIN (EOM_UTA_BBOX_Y0 (dbb), d_yf1),
+ EOM_UTILE_SIZE,
+ EOM_UTILE_SIZE);
+ }
+
+ dofs++;
+
+ /* Top right tile */
+ if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
+ && d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ 0, d_yf1, d_xf2, EOM_UTILE_SIZE);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ MIN (EOM_UTA_BBOX_Y0 (dbb), d_yf1),
+ MAX (EOM_UTA_BBOX_X1 (dbb), d_xf2),
+ EOM_UTILE_SIZE);
+ }
+
+ dofs += uta->width - 1;
+
+ /* Bottom left tile */
+ if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
+ && d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ d_xf1, 0, EOM_UTILE_SIZE, d_yf2);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ MIN (EOM_UTA_BBOX_X0 (dbb), d_xf1),
+ 0,
+ EOM_UTILE_SIZE,
+ MAX (EOM_UTA_BBOX_Y1 (dbb), d_yf2));
+ }
+
+ dofs++;
+
+ /* Bottom right tile */
+ if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
+ && d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
+ dbb = utiles[dofs];
+ if (dbb == 0)
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ 0, 0, d_xf2, d_yf2);
+ else
+ utiles[dofs] = EOM_UTA_BBOX_CONS (
+ 0,
+ 0,
+ MAX (EOM_UTA_BBOX_X1 (dbb), d_xf2),
+ MAX (EOM_UTA_BBOX_Y1 (dbb), d_yf2));
+ }
+ }
+ }
+}
+
+/**
+ * uta_copy_area:
+ * @uta: A microtile array.
+ * @src_x: Left coordinate of source rectangle.
+ * @src_y: Top coordinate of source rectangle.
+ * @dest_x: Left coordinate of destination.
+ * @dest_y: Top coordinate of destination.
+ * @width: Width of region to copy.
+ * @height: Height of region to copy.
+ *
+ * Copies a rectangular region within a microtile array. The array will not be
+ * expanded if the destination area does not fit within it; rather only the area
+ * that fits will be copied. The source rectangle must be completely contained
+ * within the microtile array.
+ **/
+void
+uta_copy_area (EomUta *uta, int src_x, int src_y, int dest_x, int dest_y, int width, int height)
+{
+ int rect_x1, rect_y1, rect_x2, rect_y2;
+ gboolean top_to_bottom, left_to_right;
+ int xofs, yofs;
+ int x, y;
+
+ g_return_if_fail (uta != NULL);
+ g_return_if_fail (width >= 0 && height >= 0);
+ g_return_if_fail (src_x >= uta->x0 << EOM_UTILE_SHIFT);
+ g_return_if_fail (src_y >= uta->y0 << EOM_UTILE_SHIFT);
+ g_return_if_fail (src_x + width <= (uta->x0 + uta->width) << EOM_UTILE_SHIFT);
+ g_return_if_fail (src_y + height <= (uta->y0 + uta->height) << EOM_UTILE_SHIFT);
+
+ if ((src_x == dest_x && src_y == dest_y) || width == 0 || height == 0)
+ return;
+
+ /* FIXME: This function is not perfect. It *adds* the copied/offsetted
+ * area to the original contents of the microtile array, thus growing
+ * the region more than needed. The effect should be to "overwrite" the
+ * original contents, just like XCopyArea() does. Care needs to be
+ * taken when the edges of the rectangle do not fall on microtile
+ * boundaries, because tiles may need to be "split".
+ *
+ * Maybe this will work:
+ *
+ * 1. Copy the rectangular array of tiles that form the region to a
+ * temporary buffer.
+ *
+ * 2. uta_remove_rect() the *destination* rectangle from the original
+ * microtile array.
+ *
+ * 3. Copy back the temporary buffer to the original array while
+ * offsetting it in the same way as copy_tile() does.
+ */
+
+ rect_x1 = src_x >> EOM_UTILE_SHIFT;
+ rect_y1 = src_y >> EOM_UTILE_SHIFT;
+ rect_x2 = (src_x + width + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+ rect_y2 = (src_y + height + EOM_UTILE_SIZE - 1) >> EOM_UTILE_SHIFT;
+
+ xofs = dest_x - src_x;
+ yofs = dest_y - src_y;
+
+ left_to_right = xofs < 0;
+ top_to_bottom = yofs < 0;
+
+ if (top_to_bottom && left_to_right) {
+ for (y = rect_y1; y < rect_y2; y++)
+ for (x = rect_x1; x < rect_x2; x++)
+ copy_tile (uta, x, y, xofs, yofs);
+ } else if (top_to_bottom && !left_to_right) {
+ for (y = rect_y1; y < rect_y2; y++)
+ for (x = rect_x2 - 1; x >= rect_x1; x--)
+ copy_tile (uta, x, y, xofs, yofs);
+ } else if (!top_to_bottom && left_to_right) {
+ for (y = rect_y2 - 1; y >= rect_y1; y--)
+ for (x = rect_x1; x < rect_x2; x++)
+ copy_tile (uta, x, y, xofs, yofs);
+ } else if (!top_to_bottom && !left_to_right) {
+ for (y = rect_y2 - 1; y >= rect_y1; y--)
+ for (x = rect_x2 - 1; x >= rect_x1; x--)
+ copy_tile (uta, x, y, xofs, yofs);
+ }
+}
diff --git a/src/uta.h b/src/uta.h
new file mode 100644
index 0000000..4152136
--- /dev/null
+++ b/src/uta.h
@@ -0,0 +1,75 @@
+/* Eye of Mate image viewer - Microtile array utilities
+ *
+ * Copyright (C) 2000-2009 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <[email protected]>
+ *
+ * Portions based on code from libart_lgpl by Raph Levien.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef UTA_H
+#define UTA_H
+
+#define EOM_UTILE_SHIFT 5
+#define EOM_UTILE_SIZE (1 << EOM_UTILE_SHIFT)
+
+typedef guint32 EomUtaBbox;
+
+struct _EomIRect {
+ int x0, y0, x1, y1;
+};
+
+struct _EomUta {
+ int x0;
+ int y0;
+ int width;
+ int height;
+ EomUtaBbox *utiles;
+};
+
+typedef struct _EomIRect EomIRect;
+typedef struct _EomUta EomUta;
+
+
+
+G_GNUC_INTERNAL
+void eom_uta_free (EomUta *uta);
+
+G_GNUC_INTERNAL
+void eom_irect_intersect (EomIRect *dest,
+ const EomIRect *src1, const EomIRect *src2);
+G_GNUC_INTERNAL
+int eom_irect_empty (const EomIRect *src);
+
+G_GNUC_INTERNAL
+EomUta *uta_ensure_size (EomUta *uta, int x1, int y1, int x2, int y2);
+
+G_GNUC_INTERNAL
+EomUta *uta_add_rect (EomUta *uta, int x1, int y1, int x2, int y2);
+
+G_GNUC_INTERNAL
+void uta_remove_rect (EomUta *uta, int x1, int y1, int x2, int y2);
+
+G_GNUC_INTERNAL
+void uta_find_first_glom_rect (EomUta *uta, EomIRect *rect, int max_width, int max_height);
+
+G_GNUC_INTERNAL
+void uta_copy_area (EomUta *uta, int src_x, int src_y, int dest_x, int dest_y, int width, int height);
+
+
+
+#endif
diff --git a/src/zoom.c b/src/zoom.c
new file mode 100644
index 0000000..aa027de
--- /dev/null
+++ b/src/zoom.c
@@ -0,0 +1,118 @@
+/* Eye of Mate image viewer - utility functions for computing zoom factors
+ *
+ * Copyright (C) 2000 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <[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 <config.h>
+#include <math.h>
+#include "zoom.h"
+
+
+
+/**
+ * zoom_fit_size:
+ * @dest_width: Width of destination area.
+ * @dest_height: Height of destination area.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @upscale_smaller: Whether to scale up images smaller than the destination size.
+ * @width: Return value for image width.
+ * @height: Return value for image height.
+ *
+ * Computes the final dimensions of an image that is to be scaled to fit to a
+ * certain size. If @upscale_smaller is TRUE, then images smaller than the
+ * destination size will be scaled up; otherwise, they will be left at their
+ * original size.
+ **/
+void
+zoom_fit_size (guint dest_width, guint dest_height,
+ guint src_width, guint src_height,
+ gboolean upscale_smaller,
+ guint *width, guint *height)
+{
+ guint w, h;
+
+ g_return_if_fail (width != NULL);
+ g_return_if_fail (height != NULL);
+
+ if (src_width == 0 || src_height == 0) {
+ *width = 0;
+ *height = 0;
+ return;
+ }
+
+ if (src_width <= dest_width && src_height <= dest_height && !upscale_smaller) {
+ *width = src_width;
+ *height = src_height;
+ return;
+ }
+
+ w = dest_width;
+ h = floor ((double) (src_height * w) / src_width + 0.5);
+
+ if (h > dest_height) {
+ h = dest_height;
+ w = floor ((double) (src_width * h) / src_height + 0.5);
+ }
+
+ g_assert (w <= dest_width);
+ g_assert (h <= dest_height);
+
+ *width = w;
+ *height = h;
+}
+
+/**
+ * zoom_fit_scale:
+ * @dest_width: Width of destination area.
+ * @dest_height: Height of destination area.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @upscale_smaller: Whether to scale up images smaller than the destination size.
+ *
+ * Similar to zoom_fit_size(), but returns the zoom factor of the final image
+ * with respect to the original image's size.
+ *
+ * Return value: Zoom factor with respect to the original size.
+ **/
+double
+zoom_fit_scale (guint dest_width, guint dest_height,
+ guint src_width, guint src_height,
+ gboolean upscale_smaller)
+{
+ guint w, h;
+ double wfactor, hfactor;
+ double factor;
+
+ if (src_width == 0 || src_height == 0)
+ return 1.0;
+
+ if (dest_width == 0 || dest_height == 0)
+ return 0.0;
+
+ zoom_fit_size (dest_width, dest_height, src_width, src_height, upscale_smaller, &w, &h);
+
+ wfactor = (double) w / src_width;
+ hfactor = (double) h / src_height;
+
+ factor = MIN (wfactor, hfactor);
+
+ return factor;
+}
+
diff --git a/src/zoom.h b/src/zoom.h
new file mode 100644
index 0000000..dc91505
--- /dev/null
+++ b/src/zoom.h
@@ -0,0 +1,38 @@
+/* Eye of Mate image viewer - utility functions for computing zoom factors
+ *
+ * Copyright (C) 2000 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <[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 ZOOM_H
+#define ZOOM_H
+
+#include <glib.h>
+
+G_GNUC_INTERNAL
+void zoom_fit_size (guint dest_width, guint dest_height,
+ guint src_width, guint src_height,
+ gboolean upscale_smaller,
+ guint *width, guint *height);
+
+G_GNUC_INTERNAL
+double zoom_fit_scale (guint dest_width, guint dest_height,
+ guint src_width, guint src_height,
+ gboolean upscale_smaller);
+
+#endif