diff options
| author | Perberos <[email protected]> | 2011-11-06 19:30:49 -0300 | 
|---|---|---|
| committer | Perberos <[email protected]> | 2011-11-06 19:30:49 -0300 | 
| commit | a8d28a6ce7e0c56dacba5d527d9134573a008902 (patch) | |
| tree | 8852602004b5a13cc5d1ce3ecd7a314be81d1198 /src | |
| download | eom-a8d28a6ce7e0c56dacba5d527d9134573a008902.tar.bz2 eom-a8d28a6ce7e0c56dacba5d527d9134573a008902.tar.xz  | |
inicial
Diffstat (limited to 'src')
96 files changed, 36749 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8d9e15f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,239 @@ +if ENABLE_JPEG +jpeg_LIB = $(top_builddir)/jpegutils/libeom-jpegutils.la +endif + +toolbar_LIB = $(top_builddir)/cut-n-paste/toolbar-editor/libtoolbareditor.la + +screensaver_LIB = $(top_builddir)/cut-n-paste/totem-screensaver/libtotemscrsaver.la + +noinst_LTLIBRARIES = libeom.la + +bin_PROGRAMS = eom + +headerdir = $(prefix)/include/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 (¬humb_once, (GThreadFunc) eom_close_confirmation_dialog_get_icon, "image-x-generic"); +	return GDK_PIXBUF (g_object_ref (nothumb_once.retval)); +} + +/*  Since we connect in the costructor we are sure this handler will be called  + *  before the user ones + */ +static void +response_cb (EomCloseConfirmationDialog *dlg, +             gint                        response_id, +             gpointer                    data) +{ +	EomCloseConfirmationDialogPrivate *priv; + +	g_return_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg)); + +	priv = dlg->priv; +	 +	if (priv->selected_images != NULL) +		g_list_free (priv->selected_images); + +	if (response_id == GTK_RESPONSE_YES) +	{ +		if (GET_MODE (priv) == SINGLE_IMG_MODE) +		{ +			priv->selected_images =  +				g_list_copy (priv->unsaved_images); +		} +		else +		{ +			g_return_if_fail (priv->list_store); + +			priv->selected_images = +				get_selected_imgs (priv->list_store); +		} +	} +	else +		priv->selected_images = NULL; +} + +static void +add_buttons (EomCloseConfirmationDialog *dlg) +{ +	gtk_dialog_add_button (GTK_DIALOG (dlg), +			       _("Close _without Saving"), +			       GTK_RESPONSE_NO); + +	gtk_dialog_add_button (GTK_DIALOG (dlg), +			       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); +	 +	gtk_dialog_add_button (GTK_DIALOG (dlg), +			       GTK_STOCK_SAVE, +			       GTK_RESPONSE_YES); + +	gtk_dialog_set_default_response	(GTK_DIALOG (dlg),  +					 GTK_RESPONSE_YES); +} + +static void  +eom_close_confirmation_dialog_init (EomCloseConfirmationDialog *dlg) +{ +	AtkObject *atk_obj; + +	dlg->priv = EOM_CLOSE_CONFIRMATION_DIALOG_GET_PRIVATE (dlg); + +	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);		 +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), 14); +	gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); +	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); +	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dlg), TRUE); +	 +	gtk_window_set_title (GTK_WINDOW (dlg), ""); + +	gtk_window_set_modal (GTK_WINDOW (dlg), TRUE); +	gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE); + +	atk_obj = gtk_widget_get_accessible (GTK_WIDGET (dlg)); +	atk_object_set_role (atk_obj, ATK_ROLE_ALERT); +	atk_object_set_name (atk_obj, _("Question")); +	 +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (response_cb), +			  NULL); +} + +static void  +eom_close_confirmation_dialog_finalize (GObject *object) +{ +	EomCloseConfirmationDialogPrivate *priv; + +	priv = EOM_CLOSE_CONFIRMATION_DIALOG (object)->priv; + +	if (priv->unsaved_images != NULL) +		g_list_free (priv->unsaved_images); + +	if (priv->selected_images != NULL) +		g_list_free (priv->selected_images); + +	/* Call the parent's destructor */ +	G_OBJECT_CLASS (eom_close_confirmation_dialog_parent_class)->finalize (object); +} + +static void +eom_close_confirmation_dialog_set_property (GObject      *object,  +					      guint         prop_id,  +					      const GValue *value,  +					      GParamSpec   *pspec) +{ +	EomCloseConfirmationDialog *dlg; + +	dlg = EOM_CLOSE_CONFIRMATION_DIALOG (object); + +	switch (prop_id) +	{ +		case PROP_UNSAVED_IMAGES: +			set_unsaved_image (dlg, g_value_get_pointer (value)); +			break; +			 +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +eom_close_confirmation_dialog_get_property (GObject    *object,  +					      guint       prop_id,  +					      GValue     *value,  +					      GParamSpec *pspec) +{ +	EomCloseConfirmationDialogPrivate *priv; + +	priv = EOM_CLOSE_CONFIRMATION_DIALOG (object)->priv; + +	switch( prop_id ) +	{ +		case PROP_UNSAVED_IMAGES: +			g_value_set_pointer (value, priv->unsaved_images); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void  +eom_close_confirmation_dialog_class_init (EomCloseConfirmationDialogClass *klass) +{ +	GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + +	gobject_class->set_property = eom_close_confirmation_dialog_set_property; +	gobject_class->get_property = eom_close_confirmation_dialog_get_property; +	gobject_class->finalize = eom_close_confirmation_dialog_finalize; + +	g_type_class_add_private (klass, sizeof (EomCloseConfirmationDialogPrivate)); + +	g_object_class_install_property (gobject_class, +					 PROP_UNSAVED_IMAGES, +					 g_param_spec_pointer ("unsaved_images", +						 	       "Unsaved Images", +							       "List of Unsaved Images", +							       (G_PARAM_READWRITE |  +							        G_PARAM_CONSTRUCT_ONLY))); +} + +static GList * +get_selected_imgs (GtkTreeModel *store) +{ +	GList       *list; +	gboolean     valid; +	GtkTreeIter  iter; + +	list = NULL; +	valid = gtk_tree_model_get_iter_first (store, &iter); + +	while (valid) +	{ +		gboolean to_save; +		EomImage *img; + +		gtk_tree_model_get (store, &iter,  +				    SAVE_COLUMN, &to_save, +				    IMG_COLUMN, &img, +				    -1); +		if (to_save) +			list = g_list_prepend (list, img); + +		valid = gtk_tree_model_iter_next (store, &iter); +	} + +	list = g_list_reverse (list); + +	return list; +} + +GList * +eom_close_confirmation_dialog_get_selected_images (EomCloseConfirmationDialog *dlg) +{ +	g_return_val_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL); + +	return g_list_copy (dlg->priv->selected_images); +} + +GtkWidget * +eom_close_confirmation_dialog_new (GtkWindow *parent,  +				   GList     *unsaved_images) +{ +	GtkWidget *dlg; +	GtkWindowGroup *wg; + +	g_return_val_if_fail (unsaved_images != NULL, NULL); + +	dlg = GTK_WIDGET (g_object_new (EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, +				        "unsaved_images", unsaved_images, +				        NULL)); +	g_return_val_if_fail (dlg != NULL, NULL); + +	if (parent != NULL) +	{ +		wg = gtk_window_get_group (parent); + +		/* gtk_window_get_group returns a default group when the given +		 * window doesn't have a group. Explicitly add the window to +		 * the group here to make sure it's actually in the returned +		 * group. It makes no difference if it is already. */ +		gtk_window_group_add_window (wg, parent); +		gtk_window_group_add_window (wg, GTK_WINDOW (dlg)); +		 +		gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);					      +	} + +	return dlg; +} + +GtkWidget * +eom_close_confirmation_dialog_new_single (GtkWindow     *parent,  +					  EomImage	*image) +{ +	GtkWidget *dlg; +	GList *unsaved_images; +	g_return_val_if_fail (image != NULL, NULL); +	 +	unsaved_images = g_list_prepend (NULL, image); + +	dlg = eom_close_confirmation_dialog_new (parent,  +						 unsaved_images); +	 +	g_list_free (unsaved_images); + +	return dlg; +} + +static gchar * +get_text_secondary_label (EomImage *image) +{ +	gchar *secondary_msg; +	 +	secondary_msg = g_strdup (_("If you don't save, your changes will be lost.")); +	 +	return secondary_msg; +} + +static void +build_single_img_dialog (EomCloseConfirmationDialog *dlg) +{ +	GtkWidget     *hbox; +	GtkWidget     *vbox; +	GtkWidget     *primary_label; +	GtkWidget     *secondary_label; +	GtkWidget     *image; +	EomImage      *img; +	const gchar   *image_name; +	gchar         *str; +	gchar         *markup_str; + +	g_return_if_fail (dlg->priv->unsaved_images->data != NULL); +	img = EOM_IMAGE (dlg->priv->unsaved_images->data); + +	/* Image */ +	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,  +					  GTK_ICON_SIZE_DIALOG); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + +	/* Primary label */ +	primary_label = gtk_label_new (NULL); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0.0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	image_name = eom_image_get_caption (img); + +	str = g_markup_printf_escaped (_("Save changes to image \"%s\" before closing?"), +				       image_name); +	markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL); +	g_free (str); + +	gtk_label_set_markup (GTK_LABEL (primary_label), markup_str); +	g_free (markup_str); + +	/* Secondary label */ +	str = get_text_secondary_label (img); + +	secondary_label = gtk_label_new (str); +	g_free (str); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0.0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + +	hbox = gtk_hbox_new (FALSE, 12); +	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + +	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + +	vbox = gtk_vbox_new (FALSE, 12); +	 +	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0); +		       +	gtk_box_pack_start (GTK_BOX (vbox), secondary_label, FALSE, FALSE, 0); + +	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),  +			    hbox,  +	                    FALSE,  +			    FALSE,  +			    0); + +	add_buttons (dlg); + +	gtk_widget_show_all (hbox); +} + +static void +populate_model (GtkTreeModel *store, GList *imgs) +{ +	GtkTreeIter iter; + +	while (imgs != NULL) +	{ +		EomImage *img; +		const gchar *name; +		GdkPixbuf *buf = NULL; +		GdkPixbuf *buf_scaled = NULL; +		int width; +		double ratio; + +		img = EOM_IMAGE (imgs->data); + +		name = eom_image_get_caption (img); +		buf = eom_image_get_thumbnail (img); + +		if (buf) { +			ratio = IMAGE_COLUMN_HEIGHT / (double) gdk_pixbuf_get_height (buf); +			width = (int) (gdk_pixbuf_get_width (buf) * ratio); +			buf_scaled = gdk_pixbuf_scale_simple (buf, width, IMAGE_COLUMN_HEIGHT, GDK_INTERP_BILINEAR); +		} else +			buf_scaled = get_nothumb_pixbuf (); + +		gtk_list_store_append (GTK_LIST_STORE (store), &iter); +		gtk_list_store_set (GTK_LIST_STORE (store), &iter, +				    SAVE_COLUMN, TRUE, +				    IMAGE_COLUMN, buf_scaled, +				    NAME_COLUMN, name, +				    IMG_COLUMN, img, +			            -1); + +		imgs = g_list_next (imgs); +		g_object_unref (buf_scaled); +	} +} + +static void +save_toggled (GtkCellRendererToggle *renderer, gchar *path_str, GtkTreeModel *store) +{ +	GtkTreePath *path = gtk_tree_path_new_from_string (path_str); +	GtkTreeIter iter; +	gboolean active; + +	gtk_tree_model_get_iter (store, &iter, path); +	gtk_tree_model_get (store, &iter, SAVE_COLUMN, &active, -1); + +	active ^= 1; + +	gtk_list_store_set (GTK_LIST_STORE (store), &iter, +			    SAVE_COLUMN, active, -1); + +	gtk_tree_path_free (path); +} + +static GtkWidget * +create_treeview (EomCloseConfirmationDialogPrivate *priv) +{ +	GtkListStore *store; +	GtkWidget *treeview; +	GtkCellRenderer *renderer; +	GtkTreeViewColumn *column; + +	treeview = gtk_tree_view_new (); +	gtk_widget_set_size_request (treeview, 260, 120); +	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); +	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), FALSE); + +	/* Create and populate the model */ +	store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); +	populate_model (GTK_TREE_MODEL (store), priv->unsaved_images); + +	/* Set model to the treeview */ +	gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); +	g_object_unref (store); + +	priv->list_store = GTK_TREE_MODEL (store); +	 +	/* Add columns */ +	priv->toggle_renderer = renderer = gtk_cell_renderer_toggle_new (); +	g_signal_connect (renderer, "toggled", +			  G_CALLBACK (save_toggled), store); + +	column = gtk_tree_view_column_new_with_attributes ("Save?", +							   renderer, +							   "active", +							   SAVE_COLUMN, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + +	renderer = gtk_cell_renderer_pixbuf_new (); + +	column = gtk_tree_view_column_new_with_attributes ("Image", +							   renderer, +							   "pixbuf", +							   IMAGE_COLUMN, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + +	 + +	renderer = gtk_cell_renderer_text_new (); +	column = gtk_tree_view_column_new_with_attributes ("Name", +							   renderer, +							   "text", +							   NAME_COLUMN, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + +	return treeview; +} + +static void +build_multiple_imgs_dialog (EomCloseConfirmationDialog *dlg) +{ +	EomCloseConfirmationDialogPrivate *priv; +	GtkWidget *hbox; +	GtkWidget *image; +	GtkWidget *vbox; +	GtkWidget *primary_label; +	GtkWidget *vbox2; +	GtkWidget *select_label; +	GtkWidget *scrolledwindow; +	GtkWidget *treeview; +	GtkWidget *secondary_label; +	gchar     *str; +	gchar     *markup_str; + +	priv = dlg->priv; + +	hbox = gtk_hbox_new (FALSE, 12); +	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); +  	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),  +			    hbox, TRUE, TRUE, 0); + +	/* Image */ +	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,  +					  GTK_ICON_SIZE_DIALOG); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); +	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + +	vbox = gtk_vbox_new (FALSE, 12); +	gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + +	/* Primary label */ +	primary_label = gtk_label_new (NULL); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0.0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	str = g_strdup_printf ( +			ngettext ("There is %d image with unsaved changes. " +				  "Save changes before closing?", +				  "There are %d images with unsaved changes. " +				  "Save changes before closing?", +				  g_list_length (priv->unsaved_images)), +			g_list_length (priv->unsaved_images)); + +	markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL); +	g_free (str); +	 +	gtk_label_set_markup (GTK_LABEL (primary_label), markup_str); +	g_free (markup_str); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0); +	 +	vbox2 = gtk_vbox_new (FALSE, 8); +	gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + +	select_label = gtk_label_new_with_mnemonic (_("S_elect the images you want to save:")); + +	gtk_box_pack_start (GTK_BOX (vbox2), select_label, FALSE, FALSE, 0); +	gtk_label_set_line_wrap (GTK_LABEL (select_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (select_label), 0.0, 0.5); + +	scrolledwindow = gtk_scrolled_window_new (NULL, NULL); +	gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow, TRUE, TRUE, 0); +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),  +					GTK_POLICY_AUTOMATIC,  +					GTK_POLICY_AUTOMATIC); +	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),  +					     GTK_SHADOW_IN); + +	treeview = create_treeview (priv); +	gtk_container_add (GTK_CONTAINER (scrolledwindow), treeview); + +	/* Secondary label */ +	secondary_label = gtk_label_new (_("If you don't save, " +					   "all your changes will be lost.")); + +	gtk_box_pack_start (GTK_BOX (vbox2), secondary_label, FALSE, FALSE, 0); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (select_label), treeview); + +	add_buttons (dlg); + +	gtk_widget_show_all (hbox); +} + +static void +set_unsaved_image (EomCloseConfirmationDialog *dlg, +		   const GList                *list) +{ +	EomCloseConfirmationDialogPrivate *priv; + +	g_return_if_fail (list != NULL);	 + +	priv = dlg->priv; +	g_return_if_fail (priv->unsaved_images == NULL); + +	priv->unsaved_images = g_list_copy ((GList *)list); + +	if (GET_MODE (priv) == SINGLE_IMG_MODE) +	{ +		build_single_img_dialog (dlg); +	} +	else +	{ +		build_multiple_imgs_dialog (dlg); +	}	 +} + +const GList * +eom_close_confirmation_dialog_get_unsaved_images (EomCloseConfirmationDialog *dlg) +{ +	g_return_val_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL); + +	return dlg->priv->unsaved_images; +} + +void +eom_close_confirmation_dialog_set_sensitive (EomCloseConfirmationDialog *dlg, +					     gboolean value) +{ +	GtkWidget *action_area; + +	g_return_if_fail (EOM_IS_CLOSE_CONFIRMATION_DIALOG (dlg)); + +	action_area = gtk_dialog_get_action_area (GTK_DIALOG (dlg)); +	gtk_widget_set_sensitive (action_area, value); + +	if (dlg->priv->toggle_renderer) +		gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (dlg->priv->toggle_renderer), value); +} diff --git a/src/eom-close-confirmation-dialog.h b/src/eom-close-confirmation-dialog.h new file mode 100644 index 0000000..3c5f44e --- /dev/null +++ b/src/eom-close-confirmation-dialog.h @@ -0,0 +1,79 @@ +/* + * eom-close-confirmation-dialog.h + * This file is part of eom + * + * Author: Marcus Carlson <[email protected]> + * + * Based on gedit code (gedit/gedit-close-confirmation.h) by gedit Team + * + * Copyright (C) 2004-2009 MATE Foundation  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +#ifndef __EOM_CLOSE_CONFIRMATION_DIALOG_H__ +#define __EOM_CLOSE_CONFIRMATION_DIALOG_H__ + +#include <glib.h> +#include <gtk/gtk.h> + +#include <eom-image.h> + +#define EOM_TYPE_CLOSE_CONFIRMATION_DIALOG		(eom_close_confirmation_dialog_get_type ()) +#define EOM_CLOSE_CONFIRMATION_DIALOG(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, EomCloseConfirmationDialog)) +#define EOM_CLOSE_CONFIRMATION_DIALOG_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, EomCloseConfirmationDialogClass)) +#define EOM_IS_CLOSE_CONFIRMATION_DIALOG(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG)) +#define EOM_IS_CLOSE_CONFIRMATION_DIALOG_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_CLOSE_CONFIRMATION_DIALOG)) +#define EOM_CLOSE_CONFIRMATION_DIALOG_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),EOM_TYPE_CLOSE_CONFIRMATION_DIALOG, EomCloseConfirmationDialogClass)) + +typedef struct _EomCloseConfirmationDialog 		EomCloseConfirmationDialog; +typedef struct _EomCloseConfirmationDialogClass 	EomCloseConfirmationDialogClass; +typedef struct _EomCloseConfirmationDialogPrivate 	EomCloseConfirmationDialogPrivate; + +struct _EomCloseConfirmationDialog  +{ +	GtkDialog parent; + +	/*< private > */ +	EomCloseConfirmationDialogPrivate *priv; +}; + +struct _EomCloseConfirmationDialogClass  +{ +	GtkDialogClass parent_class; +}; + +G_GNUC_INTERNAL +GType 		 eom_close_confirmation_dialog_get_type		(void) G_GNUC_CONST; + +G_GNUC_INTERNAL +GtkWidget	*eom_close_confirmation_dialog_new			(GtkWindow     *parent, +									 GList         *unsaved_documents); +G_GNUC_INTERNAL +GtkWidget 	*eom_close_confirmation_dialog_new_single 		(GtkWindow     *parent,  +									 EomImage      *image); + +G_GNUC_INTERNAL +const GList	*eom_close_confirmation_dialog_get_unsaved_images	(EomCloseConfirmationDialog *dlg); + +G_GNUC_INTERNAL +GList		*eom_close_confirmation_dialog_get_selected_images	(EomCloseConfirmationDialog *dlg); + +G_GNUC_INTERNAL +void		 eom_close_confirmation_dialog_set_sensitive		(EomCloseConfirmationDialog *dlg, gboolean value); + +#endif /* __EOM_CLOSE_CONFIRMATION_DIALOG_H__ */ + diff --git a/src/eom-config-keys.h b/src/eom-config-keys.h new file mode 100644 index 0000000..7d7074c --- /dev/null +++ b/src/eom-config-keys.h @@ -0,0 +1,63 @@ +/* Eye Of Mate - MateConf Keys Macros + * + * Copyright (C) 2000-2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on code by: + * 	- Federico Mena-Quintero <[email protected]> + *	- Jens Finke <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_CONFIG_KEYS_H__ +#define __EOM_CONFIG_KEYS_H__ + +#define EOM_CONF_DIR				"/apps/eom" + +#define EOM_CONF_DESKTOP_WALLPAPER		"/desktop/mate/background/picture_filename" +#define EOM_CONF_DESKTOP_CAN_SAVE		"/desktop/mate/lockdown/disable_save_to_disk" +#define EOM_CONF_DESKTOP_CAN_PRINT      	"/desktop/mate/lockdown/disable_printing" +#define EOM_CONF_DESKTOP_CAN_SETUP_PAGE 	"/desktop/mate/lockdown/disable_print_setup" + +#define EOM_CONF_VIEW_BACKGROUND_COLOR		"/apps/eom/view/background-color" +#define EOM_CONF_VIEW_INTERPOLATE		"/apps/eom/view/interpolate" +#define EOM_CONF_VIEW_EXTRAPOLATE		"/apps/eom/view/extrapolate" +#define EOM_CONF_VIEW_SCROLL_WHEEL_ZOOM		"/apps/eom/view/scroll_wheel_zoom" +#define EOM_CONF_VIEW_ZOOM_MULTIPLIER		"/apps/eom/view/zoom_multiplier" +#define EOM_CONF_VIEW_AUTOROTATE                "/apps/eom/view/autorotate" +#define EOM_CONF_VIEW_TRANSPARENCY		"/apps/eom/view/transparency" +#define EOM_CONF_VIEW_TRANS_COLOR		"/apps/eom/view/trans_color" +#define EOM_CONF_VIEW_USE_BG_COLOR		"/apps/eom/view/use-background-color" + +#define EOM_CONF_FULLSCREEN_LOOP		"/apps/eom/full_screen/loop" +#define EOM_CONF_FULLSCREEN_UPSCALE		"/apps/eom/full_screen/upscale" +#define EOM_CONF_FULLSCREEN_SECONDS		"/apps/eom/full_screen/seconds" + +#define EOM_CONF_UI_TOOLBAR			"/apps/eom/ui/toolbar" +#define EOM_CONF_UI_STATUSBAR			"/apps/eom/ui/statusbar" +#define EOM_CONF_UI_IMAGE_COLLECTION		"/apps/eom/ui/image_collection" +#define EOM_CONF_UI_IMAGE_COLLECTION_POSITION	"/apps/eom/ui/image_collection_position" +#define EOM_CONF_UI_IMAGE_COLLECTION_RESIZABLE	"/apps/eom/ui/image_collection_resizable" +#define EOM_CONF_UI_SIDEBAR			"/apps/eom/ui/sidebar" +#define EOM_CONF_UI_SCROLL_BUTTONS		"/apps/eom/ui/scroll_buttons" +#define EOM_CONF_UI_DISABLE_TRASH_CONFIRMATION	"/apps/eom/ui/disable_trash_confirmation" +#define EOM_CONF_UI_FILECHOOSER_XDG_FALLBACK	"/apps/eom/ui/filechooser_xdg_fallback" +#define EOM_CONF_UI_PROPSDIALOG_NETBOOK_MODE	"/apps/eom/ui/propsdialog_netbook_mode" + +#define EOM_CONF_PLUGINS_ACTIVE_PLUGINS         "/apps/eom/plugins/active_plugins" + +#endif /* __EOM_CONFIG_KEYS_H__ */ diff --git a/src/eom-debug.c b/src/eom-debug.c new file mode 100644 index 0000000..6d373af --- /dev/null +++ b/src/eom-debug.c @@ -0,0 +1,160 @@ +/* Eye Of Mate - Debugging + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-debug.h) by: + * 	- Alex Roberts <[email protected]> + *	- Evan Lawrence <[email protected]> + *	- Chema Celorio <[email protected]> + *	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> + +#include "eom-debug.h" + +#define ENABLE_PROFILING + +#ifdef ENABLE_PROFILING +static GTimer *timer = NULL; +static gdouble last = 0.0; +#endif + +static EomDebugSection debug = EOM_NO_DEBUG; + +void +eom_debug_init (void) +{ +	if (g_getenv ("EOM_DEBUG") != NULL) +	{ +		/* Enable all debugging */ +		debug = ~EOM_NO_DEBUG; +		goto out; +	} + +	if (g_getenv ("EOM_DEBUG_WINDOW") != NULL) +		debug = debug | EOM_DEBUG_WINDOW; + +	if (g_getenv ("EOM_DEBUG_VIEW") != NULL) +		debug = debug | EOM_DEBUG_VIEW; + +	if (g_getenv ("EOM_DEBUG_JOBS") != NULL) +		debug = debug | EOM_DEBUG_JOBS; + +	if (g_getenv ("EOM_DEBUG_THUMBNAIL") != NULL) +		debug = debug | EOM_DEBUG_THUMBNAIL; + +	if (g_getenv ("EOM_DEBUG_IMAGE_DATA") != NULL) +		debug = debug | EOM_DEBUG_IMAGE_DATA; + +	if (g_getenv ("EOM_DEBUG_IMAGE_LOAD") != NULL) +		debug = debug | EOM_DEBUG_IMAGE_LOAD; + +	if (g_getenv ("EOM_DEBUG_IMAGE_SAVE") != NULL) +		debug = debug | EOM_DEBUG_IMAGE_SAVE; + +	if (g_getenv ("EOM_DEBUG_LIST_STORE") != NULL) +		debug = debug | EOM_DEBUG_LIST_STORE; + +	if (g_getenv ("EOM_DEBUG_PREFERENCES") != NULL) +		debug = debug | EOM_DEBUG_PREFERENCES; + +	if (g_getenv ("EOM_DEBUG_PRINTING") != NULL) +		debug = debug | EOM_DEBUG_PRINTING; + +	if (g_getenv ("EOM_DEBUG_LCMS") != NULL) +		debug = debug | EOM_DEBUG_LCMS; + +	if (g_getenv ("EOM_DEBUG_PLUGINS") != NULL) +		debug = debug | EOM_DEBUG_PLUGINS; + +out: + +#ifdef ENABLE_PROFILING +	if (debug != EOM_NO_DEBUG) +		timer = g_timer_new (); +#endif +	return; +} + +void +eom_debug_message (EomDebugSection   section, +		   const gchar      *file, +		   gint              line, +		   const gchar      *function, +		   const gchar      *format, ...) +{ +	if (G_UNLIKELY (debug & section)) +	{ +#ifdef ENABLE_PROFILING +		gdouble seconds; +#endif + +		va_list args; +		gchar *msg; + +		g_return_if_fail (format != NULL); + +		va_start (args, format); +		msg = g_strdup_vprintf (format, args); +		va_end (args); + +#ifdef ENABLE_PROFILING +		g_return_if_fail (timer != NULL); + +		seconds = g_timer_elapsed (timer, NULL); +		g_print ("[%f (%f)] %s:%d (%s) %s\n", +			 seconds, seconds - last,  file, line, function, msg); +		last = seconds; +#else +		g_print ("%s:%d (%s) %s\n", file, line, function, msg); +#endif + +		fflush (stdout); + +		g_free (msg); +	} +} + +void eom_debug (EomDebugSection  section, +		  const gchar       *file, +		  gint               line, +		  const gchar       *function) +{ +	if (G_UNLIKELY (debug & section)) +	{ +#ifdef ENABLE_PROFILING +		gdouble seconds; + +		g_return_if_fail (timer != NULL); + +		seconds = g_timer_elapsed (timer, NULL); +		g_print ("[%f (%f)] %s:%d (%s)\n", +			 seconds, seconds - last, file, line, function); +		last = seconds; +#else +		g_print ("%s:%d (%s)\n", file, line, function); +#endif +		fflush (stdout); +	} +} diff --git a/src/eom-debug.h b/src/eom-debug.h new file mode 100644 index 0000000..bde758c --- /dev/null +++ b/src/eom-debug.h @@ -0,0 +1,75 @@ +/* Eye Of Mate - Debugging + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-debug.h) by: + * 	- Alex Roberts <[email protected]> + *	- Evan Lawrence <[email protected]> + *	- Chema Celorio <[email protected]> + *	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_DEBUG_H__ +#define __EOM_DEBUG_H__ + +#include <glib.h> + +typedef enum { +	EOM_NO_DEBUG           = 0, +	EOM_DEBUG_WINDOW       = 1 << 0, +	EOM_DEBUG_VIEW         = 1 << 1, +	EOM_DEBUG_JOBS         = 1 << 2, +	EOM_DEBUG_THUMBNAIL    = 1 << 3, +	EOM_DEBUG_IMAGE_DATA   = 1 << 4, +	EOM_DEBUG_IMAGE_LOAD   = 1 << 5, +	EOM_DEBUG_IMAGE_SAVE   = 1 << 6, +	EOM_DEBUG_LIST_STORE   = 1 << 7, +	EOM_DEBUG_PREFERENCES  = 1 << 8, +	EOM_DEBUG_PRINTING     = 1 << 9, +	EOM_DEBUG_LCMS         = 1 << 10, +	EOM_DEBUG_PLUGINS      = 1 << 11 +} EomDebugSection; + +#define	DEBUG_WINDOW		EOM_DEBUG_WINDOW,      __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_VIEW		EOM_DEBUG_VIEW,        __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_JOBS		EOM_DEBUG_JOBS,        __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_THUMBNAIL		EOM_DEBUG_THUMBNAIL,   __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_IMAGE_DATA	EOM_DEBUG_IMAGE_DATA,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_IMAGE_LOAD	EOM_DEBUG_IMAGE_LOAD,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_IMAGE_SAVE	EOM_DEBUG_IMAGE_SAVE,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_LIST_STORE	EOM_DEBUG_LIST_STORE,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_PREFERENCES	EOM_DEBUG_PREFERENCES, __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_PRINTING		EOM_DEBUG_PRINTING,    __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_LCMS 		EOM_DEBUG_LCMS,        __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_PLUGINS 		EOM_DEBUG_PLUGINS,     __FILE__, __LINE__, G_STRFUNC + +void   eom_debug_init        (void); + +void   eom_debug             (EomDebugSection    section, +          	              const gchar       *file, +          	              gint               line, +          	              const gchar       *function); + +void   eom_debug_message     (EomDebugSection    section, +			      const gchar       *file, +			      gint               line, +			      const gchar       *function, +			      const gchar       *format, ...) G_GNUC_PRINTF(5, 6); + +#endif /* __EOM_DEBUG_H__ */ diff --git a/src/eom-dialog.c b/src/eom-dialog.c new file mode 100644 index 0000000..fac74a9 --- /dev/null +++ b/src/eom-dialog.c @@ -0,0 +1,237 @@ +/* Eye Of Mate - Image  Dialog + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eom-dialog.h" + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#define EOM_DIALOG_GET_PRIVATE(object) \ +	(G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_DIALOG, EomDialogPrivate)) + +G_DEFINE_TYPE (EomDialog, eom_dialog, G_TYPE_OBJECT); + +enum { +        PROP_0, +        PROP_PARENT_WINDOW, +}; + +struct _EomDialogPrivate { +	GtkWidget   *dlg; +	GtkBuilder  *xml; +	GtkWindow   *parent; +}; + +static void +eom_dialog_construct_impl (EomDialog   *dialog, +			   const gchar *glade_file, +			   const gchar *dlg_node) +{ +	EomDialogPrivate *priv; +	gchar *filename; + +	g_return_if_fail (dialog != NULL); +	g_return_if_fail (EOM_IS_DIALOG (dialog)); + +	priv = dialog->priv; + +	filename = g_build_filename (EOM_DATA_DIR, glade_file, NULL); + +	priv->xml = gtk_builder_new (); +	gtk_builder_set_translation_domain (priv->xml, GETTEXT_PACKAGE); +	g_assert (gtk_builder_add_from_file (priv->xml, filename, NULL)); + +	g_free (filename); + +	priv->dlg = GTK_WIDGET (gtk_builder_get_object (priv->xml, dlg_node)); + +	if (priv->parent != NULL) { +		gtk_window_set_transient_for (GTK_WINDOW (priv->dlg), +					      priv->parent); +	} +} + +static void +eom_dialog_show_impl (EomDialog *dialog) +{ +	g_return_if_fail (dialog != NULL); +	g_return_if_fail (EOM_IS_DIALOG (dialog)); + +	gtk_window_present (GTK_WINDOW (dialog->priv->dlg)); +} + +static void +eom_dialog_hide_impl (EomDialog *dialog) +{ +	g_return_if_fail (dialog != NULL); +	g_return_if_fail (EOM_IS_DIALOG (dialog)); + +	gtk_widget_hide (dialog->priv->dlg); +} + +static void +eom_dialog_set_property (GObject      *object, +			 guint         prop_id, +			 const GValue *value, +			 GParamSpec   *pspec) +{ +	EomDialog *dialog = EOM_DIALOG (object); + +	switch (prop_id) { +		case PROP_PARENT_WINDOW: +			dialog->priv->parent = g_value_get_object (value); +			break; +	} +} + +static void +eom_dialog_get_property (GObject    *object, +			 guint       prop_id, +			 GValue     *value, +			 GParamSpec *pspec) +{ +	EomDialog *dialog = EOM_DIALOG (object); + +	switch (prop_id) { +		case PROP_PARENT_WINDOW: +			g_value_set_object (value, dialog->priv->parent); +			break; +	} +} + +static void +eom_dialog_dispose (GObject *object) +{ +	EomDialog *dialog; +	EomDialogPrivate *priv; + +	g_return_if_fail (object != NULL); +	g_return_if_fail (EOM_IS_DIALOG (object)); + +	dialog = EOM_DIALOG (object); +	priv = dialog->priv; + +	if (priv->dlg) { +		gtk_widget_destroy (priv->dlg); +		priv->dlg = NULL; +	} + +	if (priv->xml) { +		g_object_unref (priv->xml); +		priv->xml = NULL; +	} + +	G_OBJECT_CLASS (eom_dialog_parent_class)->dispose (object); +} + +static void +eom_dialog_class_init (EomDialogClass *class) +{ +	GObjectClass *g_object_class = (GObjectClass *) class; + +	g_object_class->dispose = eom_dialog_dispose; +	g_object_class->set_property = eom_dialog_set_property; +	g_object_class->get_property = eom_dialog_get_property; + +	class->construct = eom_dialog_construct_impl; +	class->show = eom_dialog_show_impl; +	class->hide = eom_dialog_hide_impl; + +	g_object_class_install_property (g_object_class, +					 PROP_PARENT_WINDOW, +					 g_param_spec_object ("parent-window", +							      "Parent window", +							      "Parent window", +							      GTK_TYPE_WINDOW, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY | +							      G_PARAM_STATIC_NAME | +							      G_PARAM_STATIC_NICK | +							      G_PARAM_STATIC_BLURB)); + +	g_type_class_add_private (g_object_class, sizeof (EomDialogPrivate)); +} + +static void +eom_dialog_init (EomDialog *dialog) +{ +	dialog->priv = EOM_DIALOG_GET_PRIVATE (dialog); + +	dialog->priv->dlg = NULL; +	dialog->priv->xml = NULL; +	dialog->priv->parent = NULL; +} + +void +eom_dialog_construct (EomDialog   *dialog, +		      const gchar *glade_file, +		      const gchar *dlg_node) +{ +	EomDialogClass *klass = EOM_DIALOG_GET_CLASS (dialog); +	klass->construct (dialog, glade_file, dlg_node); +} + +void +eom_dialog_show (EomDialog *dialog) +{ +	EomDialogClass *klass = EOM_DIALOG_GET_CLASS (dialog); +	klass->show (dialog); +} + +void +eom_dialog_hide (EomDialog *dialog) +{ +	EomDialogClass *klass = EOM_DIALOG_GET_CLASS (dialog); +	klass->hide (dialog); +} + +void +eom_dialog_get_controls (EomDialog   *dialog, +			 const gchar *property_id, +			 ...) +{ +	EomDialogPrivate *priv; +        GtkWidget **wptr; +        va_list varargs; + +	g_return_if_fail (dialog != NULL); +	g_return_if_fail (EOM_IS_DIALOG (dialog)); + +	priv = dialog->priv; + +        va_start (varargs, property_id); + +        while (property_id != NULL) +        { +                wptr = va_arg (varargs, GtkWidget **); +                *wptr = GTK_WIDGET (gtk_builder_get_object (priv->xml, +							    property_id)); + +                property_id = va_arg (varargs, const gchar *); +        } + +        va_end (varargs); +} diff --git a/src/eom-dialog.h b/src/eom-dialog.h new file mode 100644 index 0000000..3022dee --- /dev/null +++ b/src/eom-dialog.h @@ -0,0 +1,76 @@ +/* Eye Of Mate - EOM Dialog + * + * Copyright (C) 2006 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_DIALOG_H__ +#define __EOM_DIALOG_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _EomDialog EomDialog; +typedef struct _EomDialogClass EomDialogClass; +typedef struct _EomDialogPrivate EomDialogPrivate; + +#define EOM_TYPE_DIALOG            (eom_dialog_get_type ()) +#define EOM_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_DIALOG, EomDialog)) +#define EOM_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  EOM_TYPE_DIALOG, EomDialogClass)) +#define EOM_IS_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_DIALOG)) +#define EOM_IS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  EOM_TYPE_DIALOG)) +#define EOM_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  EOM_TYPE_DIALOG, EomDialogClass)) + +struct _EomDialog { +	GObject object; + +	EomDialogPrivate *priv; +}; + +struct _EomDialogClass { +	GObjectClass parent_class; + +	void    (* construct)   (EomDialog   *dialog, +				 const gchar *glade_file, +				 const gchar *dlg_node); + +	void    (* show)        (EomDialog   *dialog); + +	void    (* hide)        (EomDialog   *dialog); +}; + +GType   eom_dialog_get_type      (void) G_GNUC_CONST; + +void    eom_dialog_construct     (EomDialog   *dialog, +				  const gchar *glade_file, +				  const gchar *dlg_node); + +void    eom_dialog_show	         (EomDialog *dialog); + +void    eom_dialog_hide	         (EomDialog *dialog); + +void    eom_dialog_get_controls  (EomDialog   *dialog, +				  const gchar *property_id, +				  ...); + +G_END_DECLS + +#endif /* __EOM_DIALOG_H__ */ diff --git a/src/eom-enum-types.c.template b/src/eom-enum-types.c.template new file mode 100644 index 0000000..a7212e0 --- /dev/null +++ b/src/eom-enum-types.c.template @@ -0,0 +1,39 @@ +/*** BEGIN file-header ***/ +#include "eom-enum-types.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@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 *) ®ister_func)) { +		g_warning ("%s", g_module_error()); +		g_module_close (module->library); + +		return FALSE; +	} + +	/* Symbol can still be NULL even though g_module_symbol +	 * returned TRUE */ +	if (register_func == NULL) { +		g_warning ("Symbol 'register_eom_plugin' should not be NULL"); +		g_module_close (module->library); + +		return FALSE; +	} + +	module->type = register_func (gmodule); + +	if (module->type == 0) { +		g_warning ("Invalid eom plugin contained by module %s", module->path); +		return FALSE; +	} + +	return TRUE; +} + +static void +eom_module_unload (GTypeModule *gmodule) +{ +	EomModule *module = EOM_MODULE (gmodule); + +	eom_debug_message (DEBUG_PLUGINS, "Unloading %s", module->path); + +	g_module_close (module->library); + +	module->library = NULL; +	module->type = 0; +} + +const gchar * +eom_module_get_path (EomModule *module) +{ +	g_return_val_if_fail (EOM_IS_MODULE (module), NULL); + +	return module->path; +} + +GObject * +eom_module_new_object (EomModule *module) +{ +	eom_debug_message (DEBUG_PLUGINS, "Creating object of type %s", g_type_name (module->type)); + +	if (module->type == 0) { +		return NULL; +	} + +	return g_object_new (module->type, NULL); +} + +static void +eom_module_init (EomModule *module) +{ +	eom_debug_message (DEBUG_PLUGINS, "EomModule %p initialising", module); +} + +static void +eom_module_finalize (GObject *object) +{ +	EomModule *module = EOM_MODULE (object); + +	eom_debug_message (DEBUG_PLUGINS, "EomModule %p finalising", module); + +	g_free (module->path); + +	G_OBJECT_CLASS (eom_module_parent_class)->finalize (object); +} + +static void +eom_module_class_init (EomModuleClass *class) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (class); +	GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class); + +	object_class->finalize = eom_module_finalize; + +	module_class->load = eom_module_load; +	module_class->unload = eom_module_unload; +} + +EomModule * +eom_module_new (const gchar *path) +{ +	EomModule *module; + +	if (path == NULL || path[0] == '\0') { +		return NULL; +	} + +	module = g_object_new (EOM_TYPE_MODULE, NULL); + +	g_type_module_set_name (G_TYPE_MODULE (module), path); + +	module->path = g_strdup (path); + +	return module; +} diff --git a/src/eom-module.h b/src/eom-module.h new file mode 100644 index 0000000..241ba28 --- /dev/null +++ b/src/eom-module.h @@ -0,0 +1,72 @@ +/* Eye Of Mate - Main Window + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * 	- Paolo Maggi <[email protected]> + *      - Marco Pesenti Gritti <[email protected]> + *      - Christian Persch <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EOM_MODULE_H +#define EOM_MODULE_H + +#include <glib-object.h> +#include <gmodule.h> + +G_BEGIN_DECLS + +typedef struct _EomModule EomModule; +typedef struct _EomModuleClass EomModuleClass; +typedef struct _EomModulePrivate EomModulePrivate; + +#define EOM_TYPE_MODULE            (eom_module_get_type ()) +#define EOM_MODULE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EOM_TYPE_MODULE, EomModule)) +#define EOM_MODULE_CLASS(klass)	   (G_TYPE_CHECK_CLASS_CAST ((klass),  EOM_TYPE_MODULE, EomModuleClass)) +#define EOM_IS_MODULE(obj)	   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EOM_TYPE_MODULE)) +#define EOM_IS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj),    EOM_TYPE_MODULE)) +#define EOM_MODULE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),   EOM_TYPE_MODULE, EomModuleClass)) + +struct _EomModule { +	GTypeModule parent_instance; + +	GModule *library; +	gchar   *path; +	GType    type; +}; + +struct _EomModuleClass { +	GTypeModuleClass parent_class; +}; + +G_GNUC_INTERNAL +GType		 eom_module_get_type	(void) G_GNUC_CONST; + +G_GNUC_INTERNAL +EomModule	*eom_module_new		(const gchar *path); + +G_GNUC_INTERNAL +const gchar	*eom_module_get_path	(EomModule *module); + +G_GNUC_INTERNAL +GObject		*eom_module_new_object	(EomModule *module); + +G_END_DECLS + +#endif diff --git a/src/eom-pixbuf-util.c b/src/eom-pixbuf-util.c new file mode 100644 index 0000000..837bb42 --- /dev/null +++ b/src/eom-pixbuf-util.c @@ -0,0 +1,136 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include "eom-pixbuf-util.h" + +GSList* +eom_pixbuf_get_savable_formats (void) +{ +	GSList *list; +	GSList *write_list = NULL; +	GSList *it; + +	list = gdk_pixbuf_get_formats (); + +	for (it = list; it != NULL; it = it->next) { +		GdkPixbufFormat *format; + +		format = (GdkPixbufFormat*) it->data; +		if (gdk_pixbuf_format_is_writable (format)) { +			write_list = g_slist_prepend (write_list, format); +		} +	} + +	g_slist_free (list); +	write_list = g_slist_reverse (write_list); + +	return write_list; +} + +GdkPixbufFormat* +eom_pixbuf_get_format_by_suffix (const char *suffix) +{ +	GSList *list; +	GSList *it; +	GdkPixbufFormat *result = NULL; + +	g_return_val_if_fail (suffix != NULL, NULL); + +	list = gdk_pixbuf_get_formats (); + +	for (it = list; (it != NULL) && (result == NULL); it = it->next) { +		GdkPixbufFormat *format; +		gchar **extensions; +		int i; + +		format = (GdkPixbufFormat*) it->data; + +		extensions = gdk_pixbuf_format_get_extensions (format); +		for (i = 0; extensions[i] != NULL; i++) { +			/* g_print ("check extension: %s against %s\n", extensions[i], suffix); */ +			if (g_ascii_strcasecmp (suffix, extensions[i]) == 0) { +				result = format; +				break; +			} +		} + +		g_strfreev (extensions); +	} + +	g_slist_free (list); + +	return result; +} + +char* +eom_pixbuf_get_common_suffix (GdkPixbufFormat *format) +{ +	char **extensions; +	int i; +	char *result = NULL; + +	if (format == NULL) return NULL; + +	extensions = gdk_pixbuf_format_get_extensions (format); +	if (extensions[0] == NULL) return NULL; + +	/* try to find 3-char suffix first, use the last occurence */ +	for (i = 0; extensions [i] != NULL; i++) { +		if (strlen (extensions[i]) <= 3) { +			g_free (result); +			result = g_ascii_strdown (extensions[i], -1); +		} +	} + +	/* otherwise take the first one */ +	if (result == NULL) { +		result = g_ascii_strdown (extensions[0], -1); +	} + +	g_strfreev (extensions); + +	return result; +} + +static char* +get_suffix_from_basename (const char *basename) +{ +	char *suffix; +	char *suffix_start; +	guint len; + +	/* FIXME: does this work for all locales? */ +	suffix_start = g_utf8_strrchr (basename, -1, '.'); + +	if (suffix_start == NULL) +		return NULL; + +	len = strlen (suffix_start) - 1; +	suffix = g_strndup (suffix_start+1, len); + +	return suffix; + +} + +GdkPixbufFormat * +eom_pixbuf_get_format (GFile *file) +{ +	GdkPixbufFormat *format; +	char *path, *basename, *suffix; +	g_return_val_if_fail (file != NULL, NULL); + +	path = g_file_get_path (file); +	basename = g_path_get_basename (path); +	suffix = get_suffix_from_basename (basename); + +	format = eom_pixbuf_get_format_by_suffix (suffix); + +	g_free (path); +	g_free (basename); +	g_free (suffix); + +	return format; +} + diff --git a/src/eom-pixbuf-util.h b/src/eom-pixbuf-util.h new file mode 100644 index 0000000..e3d6268 --- /dev/null +++ b/src/eom-pixbuf-util.h @@ -0,0 +1,20 @@ +#ifndef _EOM_PIXBUF_UTIL_H_ +#define _EOM_PIXBUF_UTIL_H_ + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gio/gio.h> + +G_GNUC_INTERNAL +GSList*          eom_pixbuf_get_savable_formats (void); + +G_GNUC_INTERNAL +GdkPixbufFormat* eom_pixbuf_get_format_by_suffix (const char *suffix); + +G_GNUC_INTERNAL +GdkPixbufFormat* eom_pixbuf_get_format (GFile *file); + +G_GNUC_INTERNAL +char*            eom_pixbuf_get_common_suffix (GdkPixbufFormat *format); + +#endif /* _EOM_PIXBUF_UTIL_H_ */ + diff --git a/src/eom-plugin-engine.c b/src/eom-plugin-engine.c new file mode 100644 index 0000000..09662b5 --- /dev/null +++ b/src/eom-plugin-engine.c @@ -0,0 +1,943 @@ +/* Eye Of Mate - EOM Plugin Manager + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * 	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "eom-plugin-engine.h" +#include "eom-plugin.h" +#include "eom-module.h" +#include "eom-debug.h" +#include "eom-application.h" +#include "eom-config-keys.h" +#include "eom-util.h" + +#include <glib/gi18n.h> +#include <glib.h> +#include <mateconf/mateconf-client.h> + +#ifdef ENABLE_PYTHON +#include "eom-python-module.h" +#endif + +#define USER_EOM_PLUGINS_LOCATION "plugins/" + +#define EOM_PLUGINS_ENGINE_BASE_KEY "/apps/eom/plugins" + +#define PLUGIN_EXT	".eom-plugin" + +typedef enum { +	EOM_PLUGIN_LOADER_C, +	EOM_PLUGIN_LOADER_PY, +} EomPluginLoader; + +struct _EomPluginInfo +{ +	gchar             *file; + +	gchar             *location; +	EomPluginLoader    loader; +	GTypeModule       *module; + +	gchar             *name; +	gchar             *desc; +	gchar             *icon_name; +	gchar            **authors; +	gchar             *copyright; +	gchar             *website; + +	EomPlugin         *plugin; + +	gint               active : 1; + +	/* A plugin is unavailable if it is not possible to activate it +	   due to an error loading the plugin module (e.g. for Python plugins +	   when the interpreter has not been correctly initializated) */ +	gint               available : 1; +}; + +static void	eom_plugin_engine_active_plugins_changed (MateConfClient *client, +							  guint cnxn_id, +							  MateConfEntry *entry, +							  gpointer user_data); + +static GList *eom_plugins_list = NULL; + +static MateConfClient *eom_plugin_engine_mateconf_client = NULL; + +static GSList *active_plugins = NULL; + +static void +eom_plugin_info_free (EomPluginInfo *info) +{ +	if (info->plugin != NULL) { +	       	eom_debug_message (DEBUG_PLUGINS, "Unref plugin %s", info->name); + +		g_object_unref (info->plugin); +	} + +	g_free (info->file); +	g_free (info->location); +	g_free (info->name); +	g_free (info->desc); +	g_free (info->icon_name); +	g_free (info->website); +	g_free (info->copyright); +	g_strfreev (info->authors); + +	g_free (info); +} + +static EomPluginInfo * +eom_plugin_engine_load (const gchar *file) +{ +	EomPluginInfo *info; +	GKeyFile *plugin_file = NULL; +	gchar *str; + +	g_return_val_if_fail (file != NULL, NULL); + +	eom_debug_message (DEBUG_PLUGINS, "Loading plugin: %s", file); + +	info = g_new0 (EomPluginInfo, 1); +	info->file = g_strdup (file); + +	plugin_file = g_key_file_new (); + +	if (!g_key_file_load_from_file (plugin_file, file, G_KEY_FILE_NONE, NULL)) { +		g_warning ("Bad plugin file: %s", file); + +		goto error; +	} + +	if (!g_key_file_has_key (plugin_file, +			   	 "Eom Plugin", +				 "IAge", +				 NULL))	{ +		eom_debug_message (DEBUG_PLUGINS, +				   "IAge key does not exist in file: %s", file); + +		goto error; +	} + +	/* Check IAge=2 */ +	if (g_key_file_get_integer (plugin_file, +				    "Eom Plugin", +				    "IAge", +				    NULL) != 2) { +		eom_debug_message (DEBUG_PLUGINS, +				   "Wrong IAge in file: %s", file); + +		goto error; +	} + +	/* Get Location */ +	str = g_key_file_get_string (plugin_file, +				     "Eom Plugin", +				     "Module", +				     NULL); + +	if ((str != NULL) && (*str != '\0')) { +		info->location = str; +	} else { +		g_warning ("Could not find 'Module' in %s", file); + +		goto error; +	} + +	/* Get the loader for this plugin */ +	str = g_key_file_get_string (plugin_file, +				     "Eom Plugin", +				     "Loader", +				     NULL); + +	if (str && strcmp(str, "python") == 0) { +		info->loader = EOM_PLUGIN_LOADER_PY; + +#ifndef ENABLE_PYTHON +		g_warning ("Cannot load Python plugin '%s' since eom was not " +			   "compiled with Python support.", file); + +		goto error; +#endif + +	} else { +		info->loader = EOM_PLUGIN_LOADER_C; +	} + +	g_free (str); + +	/* Get Name */ +	str = g_key_file_get_locale_string (plugin_file, +					    "Eom Plugin", +					    "Name", +					    NULL, NULL); +	if (str) { +		info->name = str; +	} else { +		g_warning ("Could not find 'Name' in %s", file); + +		goto error; +	} + +	/* Get Description */ +	str = g_key_file_get_locale_string (plugin_file, +					    "Eom Plugin", +					    "Description", +					    NULL, NULL); +	if (str) { +		info->desc = str; +	} else { +		eom_debug_message (DEBUG_PLUGINS, "Could not find 'Description' in %s", file); + 	} + +	/* Get Icon */ +	str = g_key_file_get_locale_string (plugin_file, +					    "Eom Plugin", +					    "Icon", +					    NULL, NULL); +	if (str) { +		info->icon_name = str; +	} else { +		eom_debug_message (DEBUG_PLUGINS, "Could not find 'Icon' in %s, " +						  "using 'eom-plugin'", file); +	} + +	/* Get Authors */ +	info->authors = g_key_file_get_string_list (plugin_file, +						    "Eom Plugin", +						    "Authors", +						    NULL, +						    NULL); + +	if (info->authors == NULL) +		eom_debug_message (DEBUG_PLUGINS, "Could not find 'Authors' in %s", file); + + +	/* Get Copyright */ +	str = g_key_file_get_string (plugin_file, +				     "Eom Plugin", +				     "Copyright", +				     NULL); +	if (str) { +		info->copyright = str; +	} else { +		eom_debug_message (DEBUG_PLUGINS, "Could not find 'Copyright' in %s", file); +	} + +	/* Get Website */ +	str = g_key_file_get_string (plugin_file, +				     "Eom Plugin", +				     "Website", +				     NULL); +	if (str) { +		info->website = str; +	} else { +		eom_debug_message (DEBUG_PLUGINS, "Could not find 'Website' in %s", file); +	} + +	g_key_file_free (plugin_file); + +	/* If we know nothing about the availability of the plugin, +	   set it as available */ +	info->available = TRUE; + +	return info; + +error: +	g_free (info->file); +	g_free (info->location); +	g_free (info->name); +	g_free (info); +	g_key_file_free (plugin_file); + +	return NULL; +} + +static gint +compare_plugin_info (EomPluginInfo *info1, +		     EomPluginInfo *info2) +{ +	return strcmp (info1->location, info2->location); +} + +static void +eom_plugin_engine_load_dir (const gchar *dir) +{ +	GError *error = NULL; +	GDir *d; +	const gchar *dirent; + +	if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) { +		return; +	} + +	g_return_if_fail (eom_plugin_engine_mateconf_client != NULL); + +	eom_debug_message (DEBUG_PLUGINS, "DIR: %s", dir); + +	d = g_dir_open (dir, 0, &error); + +	if (!d)	{ +		g_warning ("%s", error->message); +		g_error_free (error); + +		return; +	} + +	while ((dirent = g_dir_read_name (d))) { +		if (g_str_has_suffix (dirent, PLUGIN_EXT)) { +			gchar *plugin_file; +			EomPluginInfo *info; + +			plugin_file = g_build_filename (dir, dirent, NULL); +			info = eom_plugin_engine_load (plugin_file); +			g_free (plugin_file); + +			if (info == NULL) +				continue; + +			/* If a plugin with this name has already been loaded +			 * drop this one (user plugins override system plugins) */ +			if (g_list_find_custom (eom_plugins_list, +						info, +						(GCompareFunc)compare_plugin_info) != NULL) { +				g_warning ("Two or more plugins named '%s'. " +					   "Only the first will be considered.\n", +					   info->location); + +				eom_plugin_info_free (info); + +				continue; +			} + +			/* Actually, the plugin will be activated when reactivate_all +			 * will be called for the first time. */ +			info->active = (g_slist_find_custom (active_plugins, +							     info->location, +							     (GCompareFunc)strcmp) != NULL); + +			eom_plugins_list = g_list_prepend (eom_plugins_list, info); + +			eom_debug_message (DEBUG_PLUGINS, "Plugin %s loaded", info->name); +		} +	} + +	eom_plugins_list = g_list_reverse (eom_plugins_list); + +	g_dir_close (d); +} + +static void +eom_plugin_engine_load_all (void) +{ +	gchar *pdir; + +	pdir = g_build_filename (eom_util_dot_dir (), +                                 USER_EOM_PLUGINS_LOCATION, NULL); + +	/* Load user's plugins */ +	eom_plugin_engine_load_dir (pdir); + +	g_free (pdir); + +	/* Load system plugins */ +	eom_plugin_engine_load_dir (EOM_PLUGIN_DIR "/"); +} + +gboolean +eom_plugin_engine_init (void) +{ +	eom_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (eom_plugins_list == NULL, FALSE); + +	if (!g_module_supported ()) { +		g_warning ("eom is not able to initialize the plugins engine."); + +		return FALSE; +	} + +	eom_plugin_engine_mateconf_client = mateconf_client_get_default (); + +	g_return_val_if_fail (eom_plugin_engine_mateconf_client != NULL, FALSE); + +	mateconf_client_add_dir (eom_plugin_engine_mateconf_client, +			      EOM_PLUGINS_ENGINE_BASE_KEY, +			      MATECONF_CLIENT_PRELOAD_ONELEVEL, +			      NULL); + +	mateconf_client_notify_add (eom_plugin_engine_mateconf_client, +				 EOM_CONF_PLUGINS_ACTIVE_PLUGINS, +				 eom_plugin_engine_active_plugins_changed, +				 NULL, NULL, NULL); + +	active_plugins = mateconf_client_get_list (eom_plugin_engine_mateconf_client, +						EOM_CONF_PLUGINS_ACTIVE_PLUGINS, +						MATECONF_VALUE_STRING, +						NULL); + +	eom_plugin_engine_load_all (); + +	return TRUE; +} + +void +eom_plugin_engine_garbage_collect (void) +{ +#ifdef ENABLE_PYTHON +	eom_python_garbage_collect (); +#endif +} + +void +eom_plugin_engine_shutdown (void) +{ +	GList *pl; + +	eom_debug (DEBUG_PLUGINS); + +#ifdef ENABLE_PYTHON +	/* Note: that this may cause finalization of objects (typically +	 * the EomWindow) by running the garbage collector. Since some +	 * of the plugin may have installed callbacks upon object +	 * finalization (typically they need to free the WindowData) +	 * it must run before we get rid of the plugins. +	 */ +	eom_python_shutdown (); +#endif + +	g_return_if_fail (eom_plugin_engine_mateconf_client != NULL); + +	for (pl = eom_plugins_list; pl; pl = pl->next) { +		EomPluginInfo *info = (EomPluginInfo*) pl->data; + +		eom_plugin_info_free (info); +	} + +	g_slist_foreach (active_plugins, (GFunc)g_free, NULL); +	g_slist_free (active_plugins); + +	active_plugins = NULL; + +	g_list_free (eom_plugins_list); +	eom_plugins_list = NULL; + +	g_object_unref (eom_plugin_engine_mateconf_client); +	eom_plugin_engine_mateconf_client = NULL; +} + +const GList * +eom_plugin_engine_get_plugins_list (void) +{ +	eom_debug (DEBUG_PLUGINS); + +	return eom_plugins_list; +} + +static gboolean +load_plugin_module (EomPluginInfo *info) +{ +	gchar *path; +	gchar *dirname; + +	eom_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (info != NULL, FALSE); +	g_return_val_if_fail (info->file != NULL, FALSE); +	g_return_val_if_fail (info->location != NULL, FALSE); +	g_return_val_if_fail (info->plugin == NULL, FALSE); +	g_return_val_if_fail (info->available, FALSE); + +	switch (info->loader) { +	case EOM_PLUGIN_LOADER_C: +		dirname = g_path_get_dirname (info->file); +		g_return_val_if_fail (dirname != NULL, FALSE); + +		path = g_module_build_path (dirname, info->location); + +		g_free (dirname); + +		g_return_val_if_fail (path != NULL, FALSE); + +		info->module = G_TYPE_MODULE (eom_module_new (path)); + +		g_free (path); + +		break; + +#ifdef ENABLE_PYTHON +	case EOM_PLUGIN_LOADER_PY: +	{ +		gchar *dir; + +		if (!eom_python_init ()) { +			/* Mark plugin as unavailable and fails */ +			info->available = FALSE; + +			g_warning ("Cannot load Python plugin '%s' since eom " +			           "was not able to initialize the Python interpreter.", +			           info->name); + +			return FALSE; +		} + +		dir = g_path_get_dirname (info->file); + +		g_return_val_if_fail ((info->location != NULL) && +		                      (info->location[0] != '\0'), +		                      FALSE); + +		info->module = G_TYPE_MODULE ( +				eom_python_module_new (dir, info->location)); + +		g_free (dir); + +		break; +	} +#endif +	default: +		g_return_val_if_reached (FALSE); +	} + +	if (!g_type_module_use (info->module)) { +		switch (info->loader) { +		case EOM_PLUGIN_LOADER_C: +			g_warning ("Cannot load plugin '%s' since file '%s' cannot be read.", +				   info->name, +				   eom_module_get_path (EOM_MODULE (info->module))); +			break; + +		case EOM_PLUGIN_LOADER_PY: +			g_warning ("Cannot load Python plugin '%s' since file '%s' cannot be read.", +				   info->name, +				   info->location); +			break; + +		default: +			g_return_val_if_reached (FALSE); +		} + +		g_object_unref (G_OBJECT (info->module)); + +		info->module = NULL; + +		/* Mark plugin as unavailable and fails */ +		info->available = FALSE; + +		return FALSE; +	} + +	switch (info->loader) { +	case EOM_PLUGIN_LOADER_C: +		info->plugin = +			EOM_PLUGIN (eom_module_new_object (EOM_MODULE (info->module))); +		break; + +#ifdef ENABLE_PYTHON +	case EOM_PLUGIN_LOADER_PY: +		info->plugin = +			EOM_PLUGIN (eom_python_module_new_object (EOM_PYTHON_MODULE (info->module))); +		break; +#endif + +	default: +		g_return_val_if_reached (FALSE); +	} + +	g_type_module_unuse (info->module); + +	eom_debug_message (DEBUG_PLUGINS, "End"); + +	return TRUE; +} + +static gboolean +eom_plugin_engine_activate_plugin_real (EomPluginInfo *info) +{ +	gboolean res = TRUE; + +	/* Plugin is not available, don't try to activate/load it */ +	if (!info->available) { +		return FALSE; +	} + +	if (info->plugin == NULL) +		res = load_plugin_module (info); + +	if (res) { +		const GList *wins = eom_application_get_windows (EOM_APP); + +		while (wins != NULL) { +			eom_plugin_activate (info->plugin, +					     EOM_WINDOW (wins->data)); + +			wins = g_list_next (wins); +		} +	} else { +		g_warning ("Error activating plugin '%s'", info->name); +	} + +	return res; +} + +gboolean +eom_plugin_engine_activate_plugin (EomPluginInfo *info) +{ +	eom_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (!info->available) +		return FALSE; + +	if (info->active) +		return TRUE; + +	if (eom_plugin_engine_activate_plugin_real (info)) { +		gboolean res; +		GSList *list; + +		/* Update plugin state */ +		info->active = TRUE; + +		list = active_plugins; + +		while (list != NULL) { +			if (strcmp (info->location, (gchar *)list->data) == 0) { +				g_warning ("Plugin '%s' is already active.", info->name); + +				return TRUE; +			} + +			list = g_slist_next (list); +		} + +		active_plugins = g_slist_insert_sorted (active_plugins, +						        g_strdup (info->location), +						        (GCompareFunc)strcmp); + +		res = mateconf_client_set_list (eom_plugin_engine_mateconf_client, +		    			     EOM_CONF_PLUGINS_ACTIVE_PLUGINS, +					     MATECONF_VALUE_STRING, +					     active_plugins, +					     NULL); + +		if (!res) +			g_warning ("Error saving the list of active plugins."); + +		return TRUE; +	} + +	return FALSE; +} + +static void +eom_plugin_engine_deactivate_plugin_real (EomPluginInfo *info) +{ +	const GList *wins = eom_application_get_windows (EOM_APP); + +	while (wins != NULL) { +		eom_plugin_deactivate (info->plugin, +				       EOM_WINDOW (wins->data)); + +		wins = g_list_next (wins); +	} +} + +gboolean +eom_plugin_engine_deactivate_plugin (EomPluginInfo *info) +{ +	gboolean res; +	GSList *list; + +	eom_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (!info->active || !info->available) +		return TRUE; + +	eom_plugin_engine_deactivate_plugin_real (info); + +	/* Update plugin state */ +	info->active = FALSE; + +	list = active_plugins; +	res = (list == NULL); + +	while (list != NULL) { +		if (strcmp (info->location, (gchar *)list->data) == 0) { +			g_free (list->data); +			active_plugins = g_slist_delete_link (active_plugins, list); +			list = NULL; +			res = TRUE; +		} else { +			list = g_slist_next (list); +		} +	} + +	if (!res) { +		g_warning ("Plugin '%s' is already deactivated.", info->name); + +		return TRUE; +	} + +	res = mateconf_client_set_list (eom_plugin_engine_mateconf_client, +	    			     EOM_CONF_PLUGINS_ACTIVE_PLUGINS, +				     MATECONF_VALUE_STRING, +				     active_plugins, +				     NULL); + +	if (!res) +		g_warning ("Error saving the list of active plugins."); + +	return TRUE; +} + +gboolean +eom_plugin_engine_plugin_is_active (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, FALSE); + +	return (info->available && info->active); +} + +gboolean +eom_plugin_engine_plugin_is_available (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, FALSE); + +	return (info->available != FALSE); +} + +static void +reactivate_all (EomWindow *window) +{ +	GList *pl; + +	eom_debug (DEBUG_PLUGINS); + +	for (pl = eom_plugins_list; pl; pl = pl->next) { +		gboolean res = TRUE; + +		EomPluginInfo *info = (EomPluginInfo*)pl->data; + +		/* If plugin is not available, don't try to activate/load it */ +		if (info->available && info->active) { +			if (info->plugin == NULL) +				res = load_plugin_module (info); + +			if (res) +				eom_plugin_activate (info->plugin, +						     window); +		} +	} + +	eom_debug_message (DEBUG_PLUGINS, "End"); +} + +void +eom_plugin_engine_update_plugins_ui (EomWindow *window, +				     gboolean   new_window) +{ +	GList *pl; + +	eom_debug (DEBUG_PLUGINS); + +	g_return_if_fail (EOM_IS_WINDOW (window)); + +	if (new_window) +		reactivate_all (window); + +	/* Updated ui of all the plugins that implement update_ui */ +	for (pl = eom_plugins_list; pl; pl = pl->next) { +		EomPluginInfo *info = (EomPluginInfo*)pl->data; + +		if (!info->available || !info->active) +			continue; + +	       	eom_debug_message (DEBUG_PLUGINS, "Updating UI of %s", info->name); + +		eom_plugin_update_ui (info->plugin, window); +	} +} + +gboolean +eom_plugin_engine_plugin_is_configurable (EomPluginInfo *info) { +	eom_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if ((info->plugin == NULL) || !info->active || !info->available) +		return FALSE; + +	return eom_plugin_is_configurable (info->plugin); +} + +void +eom_plugin_engine_configure_plugin (EomPluginInfo *info, +				    GtkWindow     *parent) +{ +	GtkWidget *conf_dlg; + +	GtkWindowGroup *wg; + +	eom_debug (DEBUG_PLUGINS); + +	g_return_if_fail (info != NULL); + +	conf_dlg = eom_plugin_create_configure_dialog (info->plugin); + +	g_return_if_fail (conf_dlg != NULL); + +	gtk_window_set_transient_for (GTK_WINDOW (conf_dlg), +				      parent); + +	// Will return a default group if no group is set +	wg = gtk_window_get_group (parent); + +	// For now assign a dedicated window group if it is  +	// the default one until we know if this is really needed +	if (wg == gtk_window_get_group (NULL)) { +		wg = gtk_window_group_new (); +		gtk_window_group_add_window (wg, parent); +	} + +	gtk_window_group_add_window (wg, +				     GTK_WINDOW (conf_dlg)); + +	gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE); + +	gtk_widget_show (conf_dlg); +} + +static void +eom_plugin_engine_active_plugins_changed (MateConfClient *client, +					  guint cnxn_id, +					  MateConfEntry *entry, +					  gpointer user_data) +{ +	GList *pl; +	gboolean to_activate; + +	eom_debug (DEBUG_PLUGINS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (!((entry->value->type == MATECONF_VALUE_LIST) && +	      (mateconf_value_get_list_type (entry->value) == MATECONF_VALUE_STRING))) { +		g_warning ("The mateconf key '%s' may be corrupted.", EOM_CONF_PLUGINS_ACTIVE_PLUGINS); +		return; +	} + +	active_plugins = mateconf_client_get_list (eom_plugin_engine_mateconf_client, +						EOM_CONF_PLUGINS_ACTIVE_PLUGINS, +						MATECONF_VALUE_STRING, +						NULL); + +	for (pl = eom_plugins_list; pl; pl = pl->next) { +		EomPluginInfo *info = (EomPluginInfo*)pl->data; + +		if (!info->available) +			continue; + +		to_activate = (g_slist_find_custom (active_plugins, +						    info->location, +						    (GCompareFunc)strcmp) != NULL); + +		if (!info->active && to_activate) { +			/* Activate plugin */ +			if (eom_plugin_engine_activate_plugin_real (info)) +				/* Update plugin state */ +				info->active = TRUE; +		} else { +			if (info->active && !to_activate) { +				eom_plugin_engine_deactivate_plugin_real (info); + +				/* Update plugin state */ +				info->active = FALSE; +			} +		} +	} +} + +const gchar * +eom_plugin_engine_get_plugin_name (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->name; +} + +const gchar * +eom_plugin_engine_get_plugin_description (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->desc; +} + +const gchar * +eom_plugin_engine_get_plugin_icon_name (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	/* Use the eom-plugin icon as a default if the plugin does not +	   have its own */ +	if (info->icon_name != NULL && +	    gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), +	    			     info->icon_name)) +		return info->icon_name; +	else +		return "eom-plugin"; +} + +const gchar ** +eom_plugin_engine_get_plugin_authors (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, (const gchar **)NULL); + +	return (const gchar **) info->authors; +} + +const gchar * +eom_plugin_engine_get_plugin_website (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->website; +} + +const gchar * +eom_plugin_engine_get_plugin_copyright (EomPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->copyright; +} diff --git a/src/eom-plugin-engine.h b/src/eom-plugin-engine.h new file mode 100644 index 0000000..bd2ce7f --- /dev/null +++ b/src/eom-plugin-engine.h @@ -0,0 +1,89 @@ +/* Eye Of Mate - EOM Plugin Engine + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-plugins-engine.h) by: + * 	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_PLUGIN_ENGINE_H__ +#define __EOM_PLUGIN_ENGINE_H__ + +#include "eom-window.h" + +#include <glib.h> + +typedef struct _EomPluginInfo EomPluginInfo; + +G_GNUC_INTERNAL +gboolean	 eom_plugin_engine_init 		(void); + +G_GNUC_INTERNAL +void		 eom_plugin_engine_shutdown 		(void); + +G_GNUC_INTERNAL +void		 eom_plugin_engine_garbage_collect	(void); + +G_GNUC_INTERNAL +const GList	*eom_plugin_engine_get_plugins_list 	(void); + +G_GNUC_INTERNAL +gboolean 	 eom_plugin_engine_activate_plugin 	(EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean 	 eom_plugin_engine_deactivate_plugin	(EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean 	 eom_plugin_engine_plugin_is_active 	(EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean 	 eom_plugin_engine_plugin_is_available	(EomPluginInfo *info); + +G_GNUC_INTERNAL +gboolean	 eom_plugin_engine_plugin_is_configurable +			       				(EomPluginInfo *info); + +G_GNUC_INTERNAL +void	 	 eom_plugin_engine_configure_plugin	(EomPluginInfo *info, +			       			 	 GtkWindow     *parent); + +G_GNUC_INTERNAL +void		 eom_plugin_engine_update_plugins_ui	(EomWindow     *window, +			       			 	 gboolean       new_window); + +G_GNUC_INTERNAL +const gchar	*eom_plugin_engine_get_plugin_name	(EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar	*eom_plugin_engine_get_plugin_description +			       				(EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar	*eom_plugin_engine_get_plugin_icon_name (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar    **eom_plugin_engine_get_plugin_authors   (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar	*eom_plugin_engine_get_plugin_website   (EomPluginInfo *info); + +G_GNUC_INTERNAL +const gchar	*eom_plugin_engine_get_plugin_copyright (EomPluginInfo *info); + +#endif  /* __EOM_PLUGIN_ENGINE_H__ */ diff --git a/src/eom-plugin-manager.c b/src/eom-plugin-manager.c new file mode 100644 index 0000000..623827f --- /dev/null +++ b/src/eom-plugin-manager.c @@ -0,0 +1,917 @@ +/* Eye Of Mate - EOM Plugin Manager + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * 	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "eom-plugin-manager.h" +#include "eom-plugin-engine.h" +#include "eom-util.h" +#include "eom-plugin.h" +#include "eom-debug.h" + +#include <glib/gi18n.h> + +enum { +	ACTIVE_COLUMN, +	AVAILABLE_COLUMN, +	INFO_COLUMN, +	N_COLUMNS +}; + +#define EOM_PLUGIN_MANAGER_GET_PRIVATE(object) \ +	(G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_PLUGIN_MANAGER, EomPluginManagerPrivate)) + +G_DEFINE_TYPE (EomPluginManager, eom_plugin_manager, GTK_TYPE_VBOX) + +#define PLUGIN_MANAGER_NAME_TITLE   _("Plugin") +#define PLUGIN_MANAGER_ACTIVE_TITLE _("Enabled") + +struct _EomPluginManagerPrivate { +	GtkWidget	*tree; + +	GtkWidget	*about_button; +	GtkWidget	*configure_button; + +	const GList	*plugins; + +	GtkWidget 	*about; + +	GtkWidget	*popup_menu; +}; + +static EomPluginInfo *plugin_manager_get_selected_plugin (EomPluginManager *pm); +static void plugin_manager_toggle_active (GtkTreeIter *iter, GtkTreeModel *model); +static void eom_plugin_manager_finalize (GObject *object); + +static void +eom_plugin_manager_class_init (EomPluginManagerClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = eom_plugin_manager_finalize; + +	g_type_class_add_private (object_class, sizeof (EomPluginManagerPrivate)); +} + +static void +about_button_cb (GtkWidget        *button, +		 EomPluginManager *pm) +{ +	EomPluginInfo *info; + +	eom_debug (DEBUG_PLUGINS); + +	info = plugin_manager_get_selected_plugin (pm); + +	g_return_if_fail (info != NULL); + +	/* If there is another about dialog already open destroy it */ +	if (pm->priv->about) +		gtk_widget_destroy (pm->priv->about); + +	pm->priv->about = g_object_new (GTK_TYPE_ABOUT_DIALOG, +		"program-name" , eom_plugin_engine_get_plugin_name (info), +		"copyright", eom_plugin_engine_get_plugin_copyright (info), +		"authors", eom_plugin_engine_get_plugin_authors (info), +		"comments", eom_plugin_engine_get_plugin_description (info), +		"website", eom_plugin_engine_get_plugin_website (info), +		"logo-icon-name", eom_plugin_engine_get_plugin_icon_name (info), +		NULL); + +	gtk_window_set_destroy_with_parent (GTK_WINDOW (pm->priv->about), +					    TRUE); + +	g_signal_connect (pm->priv->about, +			  "response", +			  G_CALLBACK (gtk_widget_destroy), +			  NULL); + +	g_signal_connect (pm->priv->about, +			  "destroy", +			  G_CALLBACK (gtk_widget_destroyed), +			  &pm->priv->about); + +	gtk_window_set_transient_for (GTK_WINDOW (pm->priv->about), +				      GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm)))); + +	gtk_widget_show (pm->priv->about); +} + +static void +configure_button_cb (GtkWidget        *button, +		     EomPluginManager *pm) +{ +	EomPluginInfo *info; +	GtkWindow *toplevel; + +	eom_debug (DEBUG_PLUGINS); + +	info = plugin_manager_get_selected_plugin (pm); + +	g_return_if_fail (info != NULL); + +	eom_debug_message (DEBUG_PLUGINS, "Configuring: %s\n", +			   eom_plugin_engine_get_plugin_name (info)); + +	toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm))); + +	eom_plugin_engine_configure_plugin (info, toplevel); + +	eom_debug_message (DEBUG_PLUGINS, "Done"); +} + +static void +plugin_manager_view_info_cell_cb (GtkTreeViewColumn *tree_column, +				  GtkCellRenderer   *cell, +				  GtkTreeModel      *tree_model, +				  GtkTreeIter       *iter, +				  gpointer           data) +{ +	EomPluginInfo *info; +	gchar *text; + +	g_return_if_fail (tree_model != NULL); +	g_return_if_fail (tree_column != NULL); + +	gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1); + +	if (info == NULL) +		return; + +	text = g_markup_printf_escaped ("<b>%s</b>\n%s", +					eom_plugin_engine_get_plugin_name (info), +					eom_plugin_engine_get_plugin_description (info)); + +	g_object_set (G_OBJECT (cell), +		      "markup", text, +		      "sensitive", eom_plugin_engine_plugin_is_available (info), +		      NULL); + +	g_free (text); +} + +static void +plugin_manager_view_icon_cell_cb (GtkTreeViewColumn *tree_column, +				  GtkCellRenderer   *cell, +				  GtkTreeModel      *tree_model, +				  GtkTreeIter       *iter, +				  gpointer           data) +{ +	EomPluginInfo *info; + +	g_return_if_fail (tree_model != NULL); +	g_return_if_fail (tree_column != NULL); + +	gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1); + +	if (info == NULL) +		return; + +	g_object_set (G_OBJECT (cell), +		      "icon-name", eom_plugin_engine_get_plugin_icon_name (info), +		      "sensitive", eom_plugin_engine_plugin_is_available (info), +		      NULL); +} + + +static void +active_toggled_cb (GtkCellRendererToggle *cell, +		   gchar                 *path_str, +		   EomPluginManager    *pm) +{ +	GtkTreeIter iter; +	GtkTreePath *path; +	GtkTreeModel *model; + +	eom_debug (DEBUG_PLUGINS); + +	path = gtk_tree_path_new_from_string (path_str); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (model != NULL); + +	gtk_tree_model_get_iter (model, &iter, path); + +	if (&iter != NULL) +		plugin_manager_toggle_active (&iter, model); + +	gtk_tree_path_free (path); +} + +static void +cursor_changed_cb (GtkTreeView *view, +		   gpointer     data) +{ +	EomPluginManager *pm = data; +	EomPluginInfo *info; + +	eom_debug (DEBUG_PLUGINS); + +	info = plugin_manager_get_selected_plugin (pm); + +	gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->about_button), +				  info != NULL); + +	gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button), +				  (info != NULL) && +				   eom_plugin_engine_plugin_is_configurable (info)); +} + +static void +row_activated_cb (GtkTreeView       *tree_view, +		  GtkTreePath       *path, +		  GtkTreeViewColumn *column, +		  gpointer           data) +{ +	EomPluginManager *pm = data; +	GtkTreeIter iter; +	GtkTreeModel *model; + +	eom_debug (DEBUG_PLUGINS); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (model != NULL); + +	gtk_tree_model_get_iter (model, &iter, path); + +	g_return_if_fail (&iter != NULL); + +	plugin_manager_toggle_active (&iter, model); +} + +static void +plugin_manager_populate_lists (EomPluginManager *pm) +{ +	const GList *plugins; +	GtkListStore *model; +	GtkTreeIter iter; + +	eom_debug (DEBUG_PLUGINS); + +	plugins = pm->priv->plugins; + +	model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree))); + +	while (plugins) { +		EomPluginInfo *info; +		info = (EomPluginInfo *)plugins->data; + +		gtk_list_store_append (model, &iter); +		gtk_list_store_set (model, &iter, +				    ACTIVE_COLUMN, eom_plugin_engine_plugin_is_active (info), +				    AVAILABLE_COLUMN, eom_plugin_engine_plugin_is_available (info), +				    INFO_COLUMN, info, +				    -1); + +		plugins = plugins->next; +	} + +	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) { +		GtkTreeSelection *selection; +		EomPluginInfo* info; + +		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + +		g_return_if_fail (selection != NULL); + +		gtk_tree_selection_select_iter (selection, &iter); + +		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, +				    INFO_COLUMN, &info, -1); + +		gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button), +					  eom_plugin_engine_plugin_is_configurable (info)); +	} +} + +static gboolean +plugin_manager_set_active (GtkTreeIter  *iter, +			   GtkTreeModel *model, +			   gboolean      active) +{ +	EomPluginInfo *info; +	gboolean res = TRUE; + +	eom_debug (DEBUG_PLUGINS); + +	gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (active) { +		/* Activate the plugin */ +		if (!eom_plugin_engine_activate_plugin (info)) { +			eom_debug_message (DEBUG_PLUGINS, "Could not activate %s.\n", +					   eom_plugin_engine_get_plugin_name (info)); + +			res = FALSE; +		} +	} else { +		/* Deactivate the plugin */ +		if (!eom_plugin_engine_deactivate_plugin (info)) { +			eom_debug_message (DEBUG_PLUGINS, "Could not deactivate %s.\n", +					     eom_plugin_engine_get_plugin_name (info)); + +			res = FALSE; +		} +	} + +	/* Set new value */ +	gtk_list_store_set (GTK_LIST_STORE (model), +			    iter, +			    ACTIVE_COLUMN, eom_plugin_engine_plugin_is_active (info), +			    AVAILABLE_COLUMN, eom_plugin_engine_plugin_is_available (info), +			    -1); + +	return res; +} + +static void +plugin_manager_toggle_active (GtkTreeIter  *iter, +			      GtkTreeModel *model) +{ +	gboolean active; + +	eom_debug (DEBUG_PLUGINS); + +	gtk_tree_model_get (model, iter, ACTIVE_COLUMN, &active, -1); + +	active ^= 1; + +	plugin_manager_set_active (iter, model, active); +} + +static EomPluginInfo * +plugin_manager_get_selected_plugin (EomPluginManager *pm) +{ +	EomPluginInfo *info = NULL; +	GtkTreeModel *model; +	GtkTreeIter iter; +	GtkTreeSelection *selection; + +	eom_debug (DEBUG_PLUGINS); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_val_if_fail (model != NULL, NULL); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_val_if_fail (selection != NULL, NULL); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { +		gtk_tree_model_get (model, &iter, INFO_COLUMN, &info, -1); +	} + +	return info; +} + +static void +plugin_manager_set_active_all (EomPluginManager *pm, +			       gboolean            active) +{ +	GtkTreeModel *model; +	GtkTreeIter iter; + +	eom_debug (DEBUG_PLUGINS); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (model != NULL); + +	gtk_tree_model_get_iter_first (model, &iter); + +	do { +		plugin_manager_set_active (&iter, model, active); +	} while (gtk_tree_model_iter_next (model, &iter)); +} + +/* Callback used as the interactive search comparison function */ +static gboolean +name_search_cb (GtkTreeModel *model, +		gint          column, +		const gchar  *key, +		GtkTreeIter  *iter, +		gpointer      data) +{ +	EomPluginInfo *info; +	gchar *normalized_string; +	gchar *normalized_key; +	gchar *case_normalized_string; +	gchar *case_normalized_key; +	gint key_len; +	gboolean retval; + +	gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1); + +	if (!info) +		return FALSE; + +	normalized_string = g_utf8_normalize (eom_plugin_engine_get_plugin_name (info), -1, G_NORMALIZE_ALL); +	normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); +	case_normalized_string = g_utf8_casefold (normalized_string, -1); +	case_normalized_key = g_utf8_casefold (normalized_key, -1); + +	key_len = strlen (case_normalized_key); + +	/* Oddly enough, this callback must return whether to stop the search +	 * because we found a match, not whether we actually matched. */ +	retval = (strncmp (case_normalized_key, case_normalized_string, key_len) != 0); + +	g_free (normalized_key); +	g_free (normalized_string); +	g_free (case_normalized_key); +	g_free (case_normalized_string); + +	return retval; +} + +static void +enable_plugin_menu_cb (GtkMenu            *menu, +		       EomPluginManager *pm) +{ +	GtkTreeModel *model; +	GtkTreeIter iter; +	GtkTreeSelection *selection; + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (model != NULL); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (selection != NULL); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) +		plugin_manager_toggle_active (&iter, model); +} + +static void +enable_all_menu_cb (GtkMenu          *menu, +		    EomPluginManager *pm) +{ +	plugin_manager_set_active_all (pm, TRUE); +} + +static void +disable_all_menu_cb (GtkMenu          *menu, +		     EomPluginManager *pm) +{ +	plugin_manager_set_active_all (pm, FALSE); +} + +static GtkWidget * +create_tree_popup_menu (EomPluginManager *pm) +{ +	GtkWidget *menu; +	GtkWidget *item; +	GtkWidget *image; +	EomPluginInfo *info; + +	info = plugin_manager_get_selected_plugin (pm); + +	if (info == NULL) +		return NULL; + +	menu = gtk_menu_new (); + +	item = gtk_image_menu_item_new_with_mnemonic (_("_About")); +	image = gtk_image_new_from_stock (GTK_STOCK_ABOUT, +					  GTK_ICON_SIZE_MENU); +	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); +	g_signal_connect (item, "activate", +			  G_CALLBACK (about_button_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_image_menu_item_new_with_mnemonic (_("C_onfigure")); +	image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, +					  GTK_ICON_SIZE_MENU); +	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); +	g_signal_connect (item, "activate", +			  G_CALLBACK (configure_button_cb), pm); +	gtk_widget_set_sensitive (item, +				  eom_plugin_engine_plugin_is_configurable (info)); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_check_menu_item_new_with_mnemonic (_("A_ctivate")); +	gtk_widget_set_sensitive (item, +				  eom_plugin_engine_plugin_is_available (info)); +	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), +					eom_plugin_engine_plugin_is_active (info)); +	g_signal_connect (item, "toggled", +			  G_CALLBACK (enable_plugin_menu_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_separator_menu_item_new (); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_menu_item_new_with_mnemonic (_("Ac_tivate All")); +	g_signal_connect (item, "activate", +			  G_CALLBACK (enable_all_menu_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_menu_item_new_with_mnemonic (_("_Deactivate All")); +	g_signal_connect (item, "activate", +			  G_CALLBACK (disable_all_menu_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	gtk_widget_show_all (menu); + +	return menu; +} + +static void +tree_popup_menu_detach (EomPluginManager *pm, +			GtkMenu            *menu) +{ +	pm->priv->popup_menu = NULL; +} + +static void +menu_position_under_widget (GtkMenu  *menu, +			    gint     *x, +			    gint     *y, +			    gboolean *push_in, +			    gpointer  user_data) +{ +	GtkWidget *w = GTK_WIDGET (user_data); +	GtkRequisition requisition; +	GtkAllocation allocation; + +	gdk_window_get_origin (gtk_widget_get_window (w), x, y); +	gtk_widget_size_request (GTK_WIDGET (menu), &requisition); +	gtk_widget_get_allocation (w, &allocation); + +	if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL) { +		*x += allocation.x + allocation.width - requisition.width; +	} else { +		*x += allocation.x; +	} + +	*y += allocation.y + allocation.height; + +	*push_in = TRUE; +} + +static void +menu_position_under_tree_view (GtkMenu  *menu, +			       gint     *x, +			       gint     *y, +			       gboolean *push_in, +			       gpointer  user_data) +{ +	GtkTreeView *tree = GTK_TREE_VIEW (user_data); +	GtkTreeModel *model; +	GtkTreeSelection *selection; +	GtkTreeIter iter; + +	model = gtk_tree_view_get_model (tree); + +	g_return_if_fail (model != NULL); + +	selection = gtk_tree_view_get_selection (tree); + +	g_return_if_fail (selection != NULL); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { +		GtkTreePath *path; +		GdkRectangle rect; + +		gdk_window_get_origin (gtk_widget_get_window(GTK_WIDGET (tree)), +				       x, y); + +		path = gtk_tree_model_get_path (model, &iter); + +		gtk_tree_view_get_cell_area (tree, +					     path, +					     gtk_tree_view_get_column (tree, 0), /* FIXME 0 for RTL ? */ +					     &rect); +		gtk_tree_path_free (path); + +		*x += rect.x; +		*y += rect.y + rect.height; + +		if (gtk_widget_get_direction (GTK_WIDGET (tree)) == GTK_TEXT_DIR_RTL) { +			GtkRequisition requisition; + +			gtk_widget_size_request (GTK_WIDGET (menu), &requisition); + +			*x += rect.width - requisition.width; +		} +	} else { +		/* No selection -> regular "under widget" positioning */ +		menu_position_under_widget (menu, +					    x, y, push_in, +					    tree); +	} +} +static void +show_tree_popup_menu (GtkTreeView        *tree, +		      EomPluginManager *pm, +		      GdkEventButton     *event) +{ +	if (pm->priv->popup_menu) +		gtk_widget_destroy (pm->priv->popup_menu); + +	pm->priv->popup_menu = create_tree_popup_menu (pm); + +	if (pm->priv->popup_menu == NULL) +		return; + +	gtk_menu_attach_to_widget (GTK_MENU (pm->priv->popup_menu), +				   GTK_WIDGET (pm), +				   (GtkMenuDetachFunc) tree_popup_menu_detach); + +	if (event != NULL) { +		gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL, +				NULL, NULL, +				event->button, event->time); +	} else { +		gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL, +				menu_position_under_tree_view, tree, +				0, gtk_get_current_event_time ()); + +		gtk_menu_shell_select_first (GTK_MENU_SHELL (pm->priv->popup_menu), +					     FALSE); +	} +} + +static gboolean +button_press_event_cb (GtkWidget          *tree, +		       GdkEventButton     *event, +		       EomPluginManager *pm) +{ +	/* We want the treeview selection to be updated before showing the menu. +	 * This code is evil, thanks to Federico Mena Quintero's black magic. +	 * See: http://mail.gnome.org/archives/gtk-devel-list/2006-February/msg00168.html +	 * FIXME: Let's remove it asap. +	 */ +	static gboolean in_press = FALSE; +	gboolean handled; + +	if (in_press) +		return FALSE; /* we re-entered */ + +	if (GDK_BUTTON_PRESS != event->type || 3 != event->button) +		return FALSE; /* let the normal handler run */ + +	in_press = TRUE; +	handled = gtk_widget_event (tree, (GdkEvent *) event); +	in_press = FALSE; + +	if (!handled) +		return FALSE; + +	/* The selection is fully updated by now */ +	show_tree_popup_menu (GTK_TREE_VIEW (tree), pm, event); + +	return TRUE; +} + +static gboolean +popup_menu_cb (GtkTreeView      *tree, +	       EomPluginManager *pm) +{ +	show_tree_popup_menu (tree, pm, NULL); + +	return TRUE; +} + +static gint +model_name_sort_func (GtkTreeModel *model, +		      GtkTreeIter  *iter1, +		      GtkTreeIter  *iter2, +		      gpointer      user_data) +{ +	EomPluginInfo *info1, *info2; + +	gtk_tree_model_get (model, iter1, INFO_COLUMN, &info1, -1); +	gtk_tree_model_get (model, iter2, INFO_COLUMN, &info2, -1); + +	return g_utf8_collate (eom_plugin_engine_get_plugin_name (info1), +			       eom_plugin_engine_get_plugin_name (info2)); +} + +static void +plugin_manager_construct_tree (EomPluginManager *pm) +{ +	GtkTreeViewColumn *column; +	GtkCellRenderer *cell; +	GtkListStore *model; + +	eom_debug (DEBUG_PLUGINS); + +	model = gtk_list_store_new (N_COLUMNS, +				    G_TYPE_BOOLEAN, +				    G_TYPE_BOOLEAN, +				    G_TYPE_POINTER); + +	gtk_tree_view_set_model (GTK_TREE_VIEW (pm->priv->tree), +				 GTK_TREE_MODEL (model)); + +	g_object_unref (model); + +	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (pm->priv->tree), TRUE); +	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (pm->priv->tree), FALSE); + +	/* First column */ +	cell = gtk_cell_renderer_toggle_new (); +	g_object_set (cell, "xpad", 6, NULL); +	g_signal_connect (cell, +			  "toggled", +			  G_CALLBACK (active_toggled_cb), +			  pm); +	column = gtk_tree_view_column_new_with_attributes (PLUGIN_MANAGER_ACTIVE_TITLE, +							   cell, +							   "active", ACTIVE_COLUMN, +							   "activatable", AVAILABLE_COLUMN, +							   "sensitive", AVAILABLE_COLUMN, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column); + +	/* Second column */ +	column = gtk_tree_view_column_new (); +	gtk_tree_view_column_set_title (column, PLUGIN_MANAGER_NAME_TITLE); +	gtk_tree_view_column_set_resizable (column, TRUE); + +	cell = gtk_cell_renderer_pixbuf_new (); +	gtk_tree_view_column_pack_start (column, cell, FALSE); +	g_object_set (cell, "stock-size", GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); +	gtk_tree_view_column_set_cell_data_func (column, cell, +						 plugin_manager_view_icon_cell_cb, +						 pm, NULL); + +	cell = gtk_cell_renderer_text_new (); +	gtk_tree_view_column_pack_start (column, cell, TRUE); +	g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +	gtk_tree_view_column_set_cell_data_func (column, cell, +						 plugin_manager_view_info_cell_cb, +						 pm, NULL); + +	gtk_tree_view_column_set_spacing (column, 6); +	gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column); + +	/* Sort on the plugin names */ +	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model), +	                                         model_name_sort_func, +        	                                 NULL, +                	                         NULL); +	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), +					      GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, +					      GTK_SORT_ASCENDING); + +	/* Enable search for our non-string column */ +	gtk_tree_view_set_search_column (GTK_TREE_VIEW (pm->priv->tree), +					 INFO_COLUMN); +	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (pm->priv->tree), +					     name_search_cb, +					     NULL, +					     NULL); + +	g_signal_connect (pm->priv->tree, +			  "cursor_changed", +			  G_CALLBACK (cursor_changed_cb), +			  pm); + +	g_signal_connect (pm->priv->tree, +			  "row_activated", +			  G_CALLBACK (row_activated_cb), +			  pm); + +	g_signal_connect (pm->priv->tree, +			  "button-press-event", +			  G_CALLBACK (button_press_event_cb), +			  pm); + +	g_signal_connect (pm->priv->tree, +			  "popup-menu", +			  G_CALLBACK (popup_menu_cb), +			  pm); + +	gtk_widget_show (pm->priv->tree); +} + +static void +eom_plugin_manager_init (EomPluginManager *pm) +{ +	GtkWidget *label; +	GtkWidget *viewport; +	GtkWidget *hbuttonbox; + +	eom_debug (DEBUG_PLUGINS); + +	pm->priv = EOM_PLUGIN_MANAGER_GET_PRIVATE (pm); + +	gtk_box_set_spacing (GTK_BOX (pm), 6); + +	label = gtk_label_new_with_mnemonic (_("Active _Plugins:")); + +	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + +	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + +	gtk_box_pack_start (GTK_BOX (pm), label, FALSE, TRUE, 0); + +	viewport = gtk_scrolled_window_new (NULL, NULL); +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (viewport), +					GTK_POLICY_AUTOMATIC, +					GTK_POLICY_AUTOMATIC); +	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (viewport), +					     GTK_SHADOW_IN); + +	gtk_box_pack_start (GTK_BOX (pm), viewport, TRUE, TRUE, 0); + +	pm->priv->tree = gtk_tree_view_new (); +	gtk_container_add (GTK_CONTAINER (viewport), pm->priv->tree); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (label), pm->priv->tree); + +	hbuttonbox = gtk_hbutton_box_new (); + +	gtk_box_pack_start (GTK_BOX (pm), hbuttonbox, FALSE, FALSE, 0); + +	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END); +	gtk_box_set_spacing (GTK_BOX (hbuttonbox), 8); + +	pm->priv->about_button = gtk_button_new_with_mnemonic (_("_About Plugin")); +	gtk_button_set_image (GTK_BUTTON (pm->priv->about_button), +			      gtk_image_new_from_stock (GTK_STOCK_ABOUT, +							GTK_ICON_SIZE_BUTTON)); + +	gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->about_button); + +	pm->priv->configure_button = gtk_button_new_with_mnemonic (_("C_onfigure Plugin")); +	gtk_button_set_image (GTK_BUTTON (pm->priv->configure_button), +			      gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, +							GTK_ICON_SIZE_BUTTON)); + +	gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->configure_button); + +	gtk_widget_set_size_request (GTK_WIDGET (viewport), 270, 100); + +	g_signal_connect (pm->priv->about_button, +			  "clicked", +			  G_CALLBACK (about_button_cb), +			  pm); + +	g_signal_connect (pm->priv->configure_button, +			  "clicked", +			  G_CALLBACK (configure_button_cb), +			  pm); + +	plugin_manager_construct_tree (pm); + +	/* Get the list of available plugins (or installed) */ +	pm->priv->plugins = eom_plugin_engine_get_plugins_list (); + +	if (pm->priv->plugins != NULL) { +		plugin_manager_populate_lists (pm); +	} else { +		gtk_widget_set_sensitive (pm->priv->about_button, FALSE); +		gtk_widget_set_sensitive (pm->priv->configure_button, FALSE); +	} +} + +static void +eom_plugin_manager_finalize (GObject *object) +{ +	EomPluginManager *pm = EOM_PLUGIN_MANAGER (object); + +	if (pm->priv->popup_menu) +		gtk_widget_destroy (pm->priv->popup_menu); + +	G_OBJECT_CLASS (eom_plugin_manager_parent_class)->finalize (object); +} + +GtkWidget * +eom_plugin_manager_new (void) +{ +	return g_object_new (EOM_TYPE_PLUGIN_MANAGER, NULL); +} diff --git a/src/eom-plugin-manager.h b/src/eom-plugin-manager.h new file mode 100644 index 0000000..e35c817 --- /dev/null +++ b/src/eom-plugin-manager.h @@ -0,0 +1,61 @@ +/* Eye Of Mate - EOM Plugin Manager + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * 	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_PLUGIN_MANAGER_H__ +#define __EOM_PLUGIN_MANAGER_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _EomPluginManager EomPluginManager; +typedef struct _EomPluginManagerClass EomPluginManagerClass; +typedef struct _EomPluginManagerPrivate EomPluginManagerPrivate; + +#define EOM_TYPE_PLUGIN_MANAGER              (eom_plugin_manager_get_type()) +#define EOM_PLUGIN_MANAGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PLUGIN_MANAGER, EomPluginManager)) +#define EOM_PLUGIN_MANAGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass),  EOM_TYPE_PLUGIN_MANAGER, EomPluginManagerClass)) +#define EOM_IS_PLUGIN_MANAGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_PLUGIN_MANAGER)) +#define EOM_IS_PLUGIN_MANAGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_PLUGIN_MANAGER)) +#define EOM_PLUGIN_MANAGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj),  EOM_TYPE_PLUGIN_MANAGER, EomPluginManagerClass)) + +struct _EomPluginManager { +	GtkVBox vbox; + +	EomPluginManagerPrivate *priv; +}; + +struct _EomPluginManagerClass { +	GtkVBoxClass parent_class; +}; + +G_GNUC_INTERNAL +GType		 eom_plugin_manager_get_type	(void) G_GNUC_CONST; + +G_GNUC_INTERNAL +GtkWidget	*eom_plugin_manager_new		(void); + +G_END_DECLS + +#endif  /* __EOM_PLUGIN_MANAGER_H__  */ diff --git a/src/eom-plugin.c b/src/eom-plugin.c new file mode 100644 index 0000000..7210128 --- /dev/null +++ b/src/eom-plugin.c @@ -0,0 +1,108 @@ +/* Eye Of Mate - EOM Plugin + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * 	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eom-plugin.h" + +G_DEFINE_TYPE (EomPlugin, eom_plugin, G_TYPE_OBJECT) + +static void +dummy (EomPlugin *plugin, EomWindow *window) +{ +} + +static GtkWidget * +create_configure_dialog	(EomPlugin *plugin) +{ +	return NULL; +} + +static gboolean +is_configurable (EomPlugin *plugin) +{ +	return (EOM_PLUGIN_GET_CLASS (plugin)->create_configure_dialog != +		create_configure_dialog); +} + +static void +eom_plugin_class_init (EomPluginClass *klass) +{ +	klass->activate = dummy; +	klass->deactivate = dummy; +	klass->update_ui = dummy; + +	klass->create_configure_dialog = create_configure_dialog; +	klass->is_configurable = is_configurable; +} + +static void +eom_plugin_init (EomPlugin *plugin) +{ +} + +void +eom_plugin_activate (EomPlugin *plugin, EomWindow *window) +{ +	g_return_if_fail (EOM_IS_PLUGIN (plugin)); +	g_return_if_fail (EOM_IS_WINDOW (window)); + +	EOM_PLUGIN_GET_CLASS (plugin)->activate (plugin, window); +} + +void +eom_plugin_deactivate (EomPlugin *plugin, EomWindow *window) +{ +	g_return_if_fail (EOM_IS_PLUGIN (plugin)); +	g_return_if_fail (EOM_IS_WINDOW (window)); + +	EOM_PLUGIN_GET_CLASS (plugin)->deactivate (plugin, window); +} + +void +eom_plugin_update_ui (EomPlugin *plugin, EomWindow *window) +{ +	g_return_if_fail (EOM_IS_PLUGIN (plugin)); +	g_return_if_fail (EOM_IS_WINDOW (window)); + +	EOM_PLUGIN_GET_CLASS (plugin)->update_ui (plugin, window); +} + +gboolean +eom_plugin_is_configurable (EomPlugin *plugin) +{ +	g_return_val_if_fail (EOM_IS_PLUGIN (plugin), FALSE); + +	return EOM_PLUGIN_GET_CLASS (plugin)->is_configurable (plugin); +} + +GtkWidget * +eom_plugin_create_configure_dialog (EomPlugin *plugin) +{ +	g_return_val_if_fail (EOM_IS_PLUGIN (plugin), NULL); + +	return EOM_PLUGIN_GET_CLASS (plugin)->create_configure_dialog (plugin); +} diff --git a/src/eom-plugin.h b/src/eom-plugin.h new file mode 100644 index 0000000..83151e9 --- /dev/null +++ b/src/eom-plugin.h @@ -0,0 +1,222 @@ +/* Eye Of Mate - EOM Plugin + * + * Copyright (C) 2007 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on gedit code (gedit/gedit-module.c) by: + * 	- Paolo Maggi <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EOM_PLUGIN_H__ +#define __EOM_PLUGIN_H__ + +#include "eom-window.h" +#include "eom-debug.h" + +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _EomPlugin EomPlugin; +typedef struct _EomPluginClass EomPluginClass; +typedef struct _EomPluginPrivate EomPluginPrivate; + +#define EOM_TYPE_PLUGIN            (eom_plugin_get_type()) +#define EOM_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PLUGIN, EomPlugin)) +#define EOM_PLUGIN_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST((obj), EOM_TYPE_PLUGIN, EomPlugin const)) +#define EOM_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  EOM_TYPE_PLUGIN, EomPluginClass)) +#define EOM_IS_PLUGIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOM_TYPE_PLUGIN)) +#define EOM_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EOM_TYPE_PLUGIN)) +#define EOM_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  EOM_TYPE_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  | 
