summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJannis Pohlmann <jannis@xfce.org>2011-01-10 00:18:39 +0100
committerJannis Pohlmann <jannis@xfce.org>2011-01-10 00:18:39 +0100
commit4c5c0972e639b78ef5976feb64a3e9f8aa6fbe1f (patch)
treecc7e6cd60844d86ee0772150b0adeb1e6583051e
parent68640975a8b03fbda3e56cea7daa976bdd1f2d54 (diff)
downloadtumbler-jannis/folder-thumbnails.tar.gz
Add simple folder thumbnailer based on the JPEG thumbnailer.jannis/folder-thumbnails
This thumbnailer searches all directories for {F,f}older.{jpg,jpeg,JPG,JPEG} files and tries to use the first one found as the thumbnail image for the folder. It is just an example implementation that shows that this CAN be done. It shouldn't necessarily be done this way.
-rw-r--r--acinclude.m444
-rw-r--r--configure.ac11
-rw-r--r--plugins/Makefile.am3
-rw-r--r--plugins/folder-thumbnailer/Makefile.am62
-rw-r--r--plugins/folder-thumbnailer/folder-thumbnailer-plugin.c91
-rw-r--r--plugins/folder-thumbnailer/folder-thumbnailer-provider.c126
-rw-r--r--plugins/folder-thumbnailer/folder-thumbnailer-provider.h43
-rw-r--r--plugins/folder-thumbnailer/folder-thumbnailer.c853
-rw-r--r--plugins/folder-thumbnailer/folder-thumbnailer.h43
9 files changed, 1272 insertions, 4 deletions
diff --git a/acinclude.m4 b/acinclude.m4
index e2d374b..cefece0 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -1,6 +1,6 @@
dnl vi:set et ai sw=2 sts=2 ts=2: */
dnl -
-dnl Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
+dnl Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
dnl
dnl This program is free software; you can redistribute it and/or
dnl modify it under the terms of the GNU General Public License as
@@ -39,6 +39,48 @@ AC_MSG_RESULT([$ac_tumbler_pixbuf_thumbnailer])
+dnl TUMBLER_FOLDER_THUMBNAILER()
+dnl
+dnl Check whether to build and install the folder thumbnailer plugin.
+dnl
+AC_DEFUN([TUMBLER_FOLDER_THUMBNAILER],
+[
+AC_ARG_ENABLE([folder-thumbnailer], [AC_HELP_STRING([--disable-folder-thumbnailer], [Don't build the folder thumbnailer plugin])],
+ [ac_tumbler_folder_thumbnailer=$enableval], [ac_tumbler_folder_thumbnailer=yes])
+if test x"$ac_tumbler_folder_thumbnailer" = x"yes"; then
+ dnl Check for gdk-pixbuf
+ PKG_CHECK_MODULES([GDK_PIXBUF], [gdk-pixbuf-2.0 >= 2.14],
+ [
+ dnl Check for libjpeg
+ LIBJPEG_LIBS=""
+ LIBJPEG_CFLAGS=""
+ AC_CHECK_LIB([jpeg], [jpeg_start_decompress],
+ [
+ AC_CHECK_HEADER([jpeglib.h],
+ [
+ LIBJPEG_LIBS="-ljpeg -lm"
+ ],
+ [
+ dnl We can only build the JPEG thumbnailer if the JPEG headers are available
+ ac_tumbler_jpeg_thumbnailer=no
+ ])
+ ],
+ [
+ dnl We can only build the JPEG thumbnailer if libjpeg is available
+ ac_tumbler_jpeg_thumbnailer=no
+ ])
+ AC_SUBST([LIBJPEG_CFLAGS])
+ AC_SUBST([LIBJPEG_LIBS])
+ ], [ac_tumbler_jpeg_thumbnailer=no])
+fi
+
+AC_MSG_CHECKING([whether to build the folder thumbnailer plugin])
+AM_CONDITIONAL([TUMBLER_FOLDER_THUMBNAILER], [test x"$ac_tumbler_folder_thumbnailer" = x"yes"])
+AC_MSG_RESULT([$ac_tumbler_folder_thumbnailer])
+])
+
+
+
dnl TUMBLER_FONT_THUMBNAILER()
dnl
dnl Check whether to build and install the FreeType2 font thumbnailer plugin.
diff --git a/configure.ac b/configure.ac
index 45ec523..fd91b02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# vi:set et ai sw=2 sts=2 ts=2: */
# -
-# Copyright (c) 2009-2010 Jannis Pohlmann <jannis@xfce.org>
+# Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -35,7 +35,7 @@ m4_define([tumbler_debug_default], [minimum])
dnl ***************************
dnl *** Initialize autoconf ***
dnl ***************************
-AC_COPYRIGHT([Copyright (c) 2009-2010 Jannis Pohlmann <jannis@xfce.org>])
+AC_COPYRIGHT([Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>])
AC_INIT([tumbler], [tumbler_version], [http://bugzilla.xfce.org/])
AC_PREREQ([2.50])
AC_CANONICAL_TARGET()
@@ -150,6 +150,7 @@ PKG_CHECK_MODULES([DBUS_GLIB], [dbus-glib-1 >= 0.72])
dnl *************************
dnl *** Check for plugins ***
dnl *************************
+TUMBLER_FOLDER_THUMBNAILER()
TUMBLER_FONT_THUMBNAILER()
TUMBLER_JPEG_THUMBNAILER()
TUMBLER_PIXBUF_THUMBNAILER()
@@ -180,6 +181,7 @@ docs/reference/Makefile
docs/reference/tumbler/Makefile
docs/reference/tumbler/version.xml
plugins/Makefile
+plugins/folder-thumbnailer/Makefile
plugins/font-thumbnailer/Makefile
plugins/jpeg-thumbnailer/Makefile
plugins/pixbuf-thumbnailer/Makefile
@@ -203,6 +205,11 @@ echo " * Debug: $enable_debug"
echo
echo "Plugins:"
echo
+if test x"$ac_tumbler_folder_thumbnailer" = x"yes"; then
+echo " * Folder thumbnailer plugin: yes"
+else
+echo " * Folder thumbnailer plugin: no"
+fi
if test x"$ac_tumbler_pixbuf_thumbnailer" = x"yes"; then
echo " * GdkPixbuf thumbnailer plugin: yes"
else
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 7894a05..49984a3 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,6 +1,6 @@
# vi:set ts=8 sw=8 noet ai nocindent:
# -
-# Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
+# Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -18,6 +18,7 @@
# Boston, MA 02110-1301, USA.
SUBDIRS = \
+ folder-thumbnailer \
font-thumbnailer \
jpeg-thumbnailer \
pixbuf-thumbnailer \
diff --git a/plugins/folder-thumbnailer/Makefile.am b/plugins/folder-thumbnailer/Makefile.am
new file mode 100644
index 0000000..9c0fd65
--- /dev/null
+++ b/plugins/folder-thumbnailer/Makefile.am
@@ -0,0 +1,62 @@
+# vi:set ts=8 sw=8 noet ai nocindent:
+# -
+# Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
+#
+# 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.
+
+if TUMBLER_FOLDER_THUMBNAILER
+
+tumbler_plugindir = $(libdir)/tumbler-$(TUMBLER_VERSION_API)/plugins
+tumbler_plugin_LTLIBRARIES = \
+ tumbler-folder-thumbnailer.la
+
+tumbler_folder_thumbnailer_la_SOURCES = \
+ folder-thumbnailer-plugin.c \
+ folder-thumbnailer-provider.c \
+ folder-thumbnailer-provider.h \
+ folder-thumbnailer.c \
+ folder-thumbnailer.h
+
+tumbler_folder_thumbnailer_la_CFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_builddir)/plugins \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/plugins \
+ -DG_LOG_DOMAIN=\"tumbler-folder-thumbnailer\" \
+ -DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(LIBJPEG_CFLAGS) \
+ $(PLATFORM_CPPFLAGS)
+
+tumbler_folder_thumbnailer_la_LDFLAGS = \
+ -avoid-version \
+ -export-dynamic \
+ -module \
+ $(PLATFORM_LDFLAGS)
+
+tumbler_folder_thumbnailer_la_LIBADD = \
+ $(top_builddir)/tumbler/libtumbler-$(TUMBLER_VERSION_API).la \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS) \
+ $(GLIB_LIBS) \
+ $(LIBJPEG_LIBS)
+
+tumbler_folder_thumbnailer_la_DEPENDENCIES = \
+ $(top_builddir)/tumbler/libtumbler-$(TUMBLER_VERSION_API).la
+
+endif
diff --git a/plugins/folder-thumbnailer/folder-thumbnailer-plugin.c b/plugins/folder-thumbnailer/folder-thumbnailer-plugin.c
new file mode 100644
index 0000000..02730a9
--- /dev/null
+++ b/plugins/folder-thumbnailer/folder-thumbnailer-plugin.c
@@ -0,0 +1,91 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public License along with this library; 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 <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include <tumbler/tumbler.h>
+
+#include <folder-thumbnailer/folder-thumbnailer-provider.h>
+#include <folder-thumbnailer/folder-thumbnailer.h>
+
+
+
+G_MODULE_EXPORT void tumbler_plugin_initialize (TumblerProviderPlugin *plugin);
+G_MODULE_EXPORT void tumbler_plugin_shutdown (void);
+G_MODULE_EXPORT void tumbler_plugin_get_types (const GType **types,
+ gint *n_types);
+
+
+
+static GType type_list[1];
+
+
+
+void
+tumbler_plugin_initialize (TumblerProviderPlugin *plugin)
+{
+ const gchar *mismatch;
+
+ /* verify that the tumbler versions are compatible */
+ mismatch = tumbler_check_version (TUMBLER_MAJOR_VERSION, TUMBLER_MINOR_VERSION,
+ TUMBLER_MICRO_VERSION);
+ if (G_UNLIKELY (mismatch != NULL))
+ {
+ g_warning (_("Version mismatch: %s"), mismatch);
+ return;
+ }
+
+#ifdef DEBUG
+ g_message (_("Initializing the Tumbler Folder Thumbnailer plugin"));
+#endif
+
+ /* register the types provided by this plugin */
+ folder_thumbnailer_register (plugin);
+ folder_thumbnailer_provider_register (plugin);
+
+ /* set up the plugin provider type list */
+ type_list[0] = TYPE_FOLDER_THUMBNAILER_PROVIDER;
+}
+
+
+
+void
+tumbler_plugin_shutdown (void)
+{
+#ifdef DEBUG
+ g_message (_("Shutting down the Tumbler Folder Thumbnailer plugin"));
+#endif
+}
+
+
+
+void
+tumbler_plugin_get_types (const GType **types,
+ gint *n_types)
+{
+ *types = type_list;
+ *n_types = G_N_ELEMENTS (type_list);
+}
diff --git a/plugins/folder-thumbnailer/folder-thumbnailer-provider.c b/plugins/folder-thumbnailer/folder-thumbnailer-provider.c
new file mode 100644
index 0000000..11eff6e
--- /dev/null
+++ b/plugins/folder-thumbnailer/folder-thumbnailer-provider.c
@@ -0,0 +1,126 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public License along with this library; 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 <glib.h>
+#include <glib-object.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <tumbler/tumbler.h>
+
+#include <folder-thumbnailer/folder-thumbnailer-provider.h>
+#include <folder-thumbnailer/folder-thumbnailer.h>
+
+
+
+static void folder_thumbnailer_provider_thumbnailer_provider_init (TumblerThumbnailerProviderIface *iface);
+static GList *folder_thumbnailer_provider_get_thumbnailers (TumblerThumbnailerProvider *provider);
+
+
+
+struct _FolderThumbnailerProviderClass
+{
+ GObjectClass __parent__;
+};
+
+struct _FolderThumbnailerProvider
+{
+ GObject __parent__;
+};
+
+
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (FolderThumbnailerProvider,
+ folder_thumbnailer_provider,
+ G_TYPE_OBJECT,
+ 0,
+ TUMBLER_ADD_INTERFACE (TUMBLER_TYPE_THUMBNAILER_PROVIDER,
+ folder_thumbnailer_provider_thumbnailer_provider_init));
+
+
+
+void
+folder_thumbnailer_provider_register (TumblerProviderPlugin *plugin)
+{
+ folder_thumbnailer_provider_register_type (G_TYPE_MODULE (plugin));
+}
+
+
+
+static void
+folder_thumbnailer_provider_class_init (FolderThumbnailerProviderClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+}
+
+
+
+static void
+folder_thumbnailer_provider_class_finalize (FolderThumbnailerProviderClass *klass)
+{
+}
+
+
+
+static void
+folder_thumbnailer_provider_thumbnailer_provider_init (TumblerThumbnailerProviderIface *iface)
+{
+ iface->get_thumbnailers = folder_thumbnailer_provider_get_thumbnailers;
+}
+
+
+
+static void
+folder_thumbnailer_provider_init (FolderThumbnailerProvider *provider)
+{
+}
+
+
+
+static GList *
+folder_thumbnailer_provider_get_thumbnailers (TumblerThumbnailerProvider *provider)
+{
+ FolderThumbnailer *thumbnailer;
+ GList *thumbnailers = NULL;
+ GStrv uri_schemes;
+ const gchar *const mime_types[2] = { "inode/directory", NULL };
+
+ /* determine which URI schemes are supported by GIO */
+ uri_schemes = tumbler_util_get_supported_uri_schemes ();
+
+ /* create the pixbuf thumbnailer */
+ thumbnailer = g_object_new (TYPE_FOLDER_THUMBNAILER,
+ "uri-schemes", uri_schemes, "mime-types", mime_types,
+ NULL);
+
+ /* free URI schemes and MIME types */
+ g_strfreev (uri_schemes);
+
+ /* add the thumbnailer to the list */
+ thumbnailers = g_list_append (thumbnailers, thumbnailer);
+
+ return thumbnailers;
+}
diff --git a/plugins/folder-thumbnailer/folder-thumbnailer-provider.h b/plugins/folder-thumbnailer/folder-thumbnailer-provider.h
new file mode 100644
index 0000000..7cd016b
--- /dev/null
+++ b/plugins/folder-thumbnailer/folder-thumbnailer-provider.h
@@ -0,0 +1,43 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __FOLDER_THUMBNAILER_PROVIDER_H__
+#define __FOLDER_THUMBNAILER_PROVIDER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS;
+
+#define TYPE_FOLDER_THUMBNAILER_PROVIDER (folder_thumbnailer_provider_get_type ())
+#define FOLDER_THUMBNAILER_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOLDER_THUMBNAILER_PROVIDER, FolderThumbnailerProvider))
+#define FOLDER_THUMBNAILER_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOLDER_THUMBNAILER_PROVIDER, FolderThumbnailerProviderClass))
+#define IS_FOLDER_THUMBNAILER_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOLDER_THUMBNAILER_PROVIDER))
+#define IS_FOLDER_THUMBNAILER_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOLDER_THUMBNAILER_PROVIDER)
+#define FOLDER_THUMBNAILER_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOLDER_THUMBNAILER_PROVIDER, FolderThumbnailerProviderClass))
+
+typedef struct _FolderThumbnailerProviderClass FolderThumbnailerProviderClass;
+typedef struct _FolderThumbnailerProvider FolderThumbnailerProvider;
+
+GType folder_thumbnailer_provider_get_type (void) G_GNUC_CONST;
+void folder_thumbnailer_provider_register (TumblerProviderPlugin *plugin);
+
+G_END_DECLS;
+
+#endif /* !__FOLDER_THUMBNAILER_PROVIDER_H__ */
diff --git a/plugins/folder-thumbnailer/folder-thumbnailer.c b/plugins/folder-thumbnailer/folder-thumbnailer.c
new file mode 100644
index 0000000..ada44c5
--- /dev/null
+++ b/plugins/folder-thumbnailer/folder-thumbnailer.c
@@ -0,0 +1,853 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public License along with this library; 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
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <math.h>
+
+#include <jpeglib.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <tumbler/tumbler.h>
+
+#include <folder-thumbnailer/folder-thumbnailer.h>
+
+
+
+static const gchar * const cover_filenames[] = {
+ "folder.jpg",
+ "folder.jpeg",
+ "folder.JPG",
+ "folder.JPEG",
+ "Folder.jpg",
+ "Folder.jpeg",
+ "Folder.JPG",
+ "Folder.JPEG",
+};
+
+
+
+static void folder_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
+ GCancellable *cancellable,
+ TumblerFileInfo *info);
+
+
+
+struct _FolderThumbnailerClass
+{
+ TumblerAbstractThumbnailerClass __parent__;
+};
+
+struct _FolderThumbnailer
+{
+ TumblerAbstractThumbnailer __parent__;
+};
+
+
+
+G_DEFINE_DYNAMIC_TYPE (FolderThumbnailer,
+ folder_thumbnailer,
+ TUMBLER_TYPE_ABSTRACT_THUMBNAILER);
+
+
+
+void
+folder_thumbnailer_register (TumblerProviderPlugin *plugin)
+{
+ folder_thumbnailer_register_type (G_TYPE_MODULE (plugin));
+}
+
+
+
+static void
+folder_thumbnailer_class_init (FolderThumbnailerClass *klass)
+{
+ TumblerAbstractThumbnailerClass *abstractthumbnailer_class;
+
+ abstractthumbnailer_class = TUMBLER_ABSTRACT_THUMBNAILER_CLASS (klass);
+ abstractthumbnailer_class->create = folder_thumbnailer_create;
+}
+
+
+
+static void
+folder_thumbnailer_class_finalize (FolderThumbnailerClass *klass)
+{
+}
+
+
+
+static void
+folder_thumbnailer_init (FolderThumbnailer *thumbnailer)
+{
+}
+
+
+
+static void
+tvtj_noop (void)
+{
+}
+
+
+
+typedef struct
+{
+ struct jpeg_error_mgr mgr;
+ jmp_buf setjmp_buffer;
+} TvtjErrorHandler;
+
+
+
+static G_GNUC_NORETURN void
+fatal_error_handler (j_common_ptr cinfo)
+{
+ TvtjErrorHandler *handler = (TvtjErrorHandler *) cinfo->err;
+ longjmp (handler->setjmp_buffer, 1);
+}
+
+
+
+static gboolean
+tvtj_fill_input_buffer (j_decompress_ptr cinfo)
+{
+ struct jpeg_source_mgr *source = cinfo->src;
+
+ /* return a fake EOI marker so we will eventually terminate */
+ if (G_LIKELY (source->bytes_in_buffer == 0))
+ {
+ static const JOCTET FAKE_EOI[2] =
+ {
+ (JOCTET) 0xff,
+ (JOCTET) JPEG_EOI,
+ };
+
+ source->next_input_byte = FAKE_EOI;
+ source->bytes_in_buffer = G_N_ELEMENTS (FAKE_EOI);
+ }
+
+ return TRUE;
+}
+
+
+
+static void
+tvtj_skip_input_data (j_decompress_ptr cinfo,
+ glong num_bytes)
+{
+ struct jpeg_source_mgr *source = cinfo->src;
+
+ if (G_LIKELY (num_bytes > 0))
+ {
+ num_bytes = MIN (num_bytes, (glong) source->bytes_in_buffer);
+
+ source->next_input_byte += num_bytes;
+ source->bytes_in_buffer -= num_bytes;
+ }
+}
+
+
+
+static inline gint
+tvtj_denom (gint width,
+ gint height,
+ gint size)
+{
+ if (width > size * 8 && height > size * 8)
+ return 8;
+ else if (width > size * 4 && height > size * 4)
+ return 4;
+ else if (width > size * 2 && height > size * 2)
+ return 2;
+ else
+ return 1;
+}
+
+
+
+static inline void
+tvtj_convert_cmyk_to_rgb (j_decompress_ptr cinfo,
+ guchar *line)
+{
+ guchar *p;
+ gint c, k, m, n, y;
+
+ g_return_if_fail (cinfo != NULL);
+ g_return_if_fail (cinfo->output_components == 4);
+ g_return_if_fail (cinfo->out_color_space == JCS_CMYK);
+
+ for (n = cinfo->output_width, p = line; n > 0; --n, p += 4)
+ {
+ c = p[0];
+ m = p[1];
+ y = p[2];
+ k = p[3];
+
+ if (cinfo->saw_Adobe_marker)
+ {
+ p[0] = k * c / 255;
+ p[1] = k * m / 255;
+ p[2] = k * y / 255;
+ }
+ else
+ {
+ p[0] = (255 - k) * (255 - c) / 255;
+ p[1] = (255 - k) * (255 - m) / 255;
+ p[2] = (255 - k) * (255 - y) / 255;
+ }
+
+ p[3] = 255;
+ }
+}
+
+
+
+static GdkPixbuf*
+tvtj_jpeg_load (const JOCTET *content,
+ gsize length,
+ gint size)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_source_mgr source;
+ TvtjErrorHandler handler;
+ guchar *lines[1];
+ guchar *buffer = NULL;
+ guchar *pixels = NULL;
+ guchar *p;
+ gint out_num_components;
+ guint n;
+
+ /* setup JPEG error handling */
+ cinfo.err = jpeg_std_error (&handler.mgr);
+ handler.mgr.error_exit = fatal_error_handler;
+ handler.mgr.output_message = (gpointer) tvtj_noop;
+ if (setjmp (handler.setjmp_buffer))
+ goto error;
+
+ /* setup the source */
+ source.bytes_in_buffer = length;
+ source.next_input_byte = content;
+ source.init_source = (gpointer) tvtj_noop;
+ source.fill_input_buffer = tvtj_fill_input_buffer;
+ source.skip_input_data = tvtj_skip_input_data;
+ source.resync_to_restart = jpeg_resync_to_restart;
+ source.term_source = (gpointer) tvtj_noop;
+
+ /* setup the JPEG decompress struct */
+ jpeg_create_decompress (&cinfo);
+ cinfo.src = &source;
+
+ /* read the JPEG header from the file */
+ jpeg_read_header (&cinfo, TRUE);
+
+ /* configure the JPEG decompress struct */
+ cinfo.scale_num = 1;
+ cinfo.scale_denom = tvtj_denom (cinfo.image_width, cinfo.image_height, size);
+ cinfo.dct_method = JDCT_FASTEST;
+ cinfo.do_fancy_upsampling = FALSE;
+
+ /* calculate the output dimensions */
+ jpeg_calc_output_dimensions (&cinfo);
+
+ /* verify the JPEG color space */
+ if (cinfo.out_color_space != JCS_GRAYSCALE
+ && cinfo.out_color_space != JCS_CMYK
+ && cinfo.out_color_space != JCS_RGB)
+ {
+ /* we don't support this color space */
+ goto error;
+ }
+
+ /* start the decompression */
+ jpeg_start_decompress (&cinfo);
+
+ /* allocate the pixel buffer and extra space for grayscale data */
+ if (G_LIKELY (cinfo.num_components != 1))
+ {
+ pixels = g_malloc (cinfo.output_width * cinfo.output_height * cinfo.num_components);
+ out_num_components = cinfo.num_components;
+ lines[0] = pixels;
+ }
+ else
+ {
+ pixels = g_malloc (cinfo.output_width * cinfo.output_height * 3);
+ buffer = g_malloc (cinfo.output_width);
+ out_num_components = 3;
+ lines[0] = buffer;
+ }
+
+ /* process the JPEG data */
+ for (p = pixels; cinfo.output_scanline < cinfo.output_height; )
+ {
+ jpeg_read_scanlines (&cinfo, lines, 1);
+
+ /* convert the data to RGB */
+ if (cinfo.num_components == 1)
+ {
+ for (n = 0; n < cinfo.output_width; ++n)
+ {
+ p[n * 3 + 0] = buffer[n];
+ p[n * 3 + 1] = buffer[n];
+ p[n * 3 + 2] = buffer[n];
+ }
+ p += cinfo.output_width * 3;
+ }
+ else
+ {
+ if (cinfo.out_color_space == JCS_CMYK)
+ tvtj_convert_cmyk_to_rgb (&cinfo, lines[0]);
+ lines[0] += cinfo.output_width * cinfo.num_components;
+ }
+ }
+
+ /* release the grayscale buffer */
+ g_free (buffer);
+ buffer = NULL;
+
+ /* finish the JPEG decompression */
+ jpeg_finish_decompress (&cinfo);
+ jpeg_destroy_decompress (&cinfo);
+
+ /* generate a pixbuf for the pixel data */
+ return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB,
+ (cinfo.out_color_components == 4), 8,
+ cinfo.output_width, cinfo.output_height,
+ cinfo.output_width * out_num_components,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+
+error:
+ jpeg_destroy_decompress (&cinfo);
+ g_free (buffer);
+ g_free (pixels);
+ return NULL;
+}
+
+
+
+typedef struct
+{
+ const guchar *data_ptr;
+ guint data_len;
+
+ guint thumb_compression;
+ union
+ {
+ struct /* thumbnail JPEG */
+ {
+ guint length;
+ guint offset;
+ } thumb_jpeg;
+ struct /* thumbnail TIFF */
+ {
+ guint length;
+ guint offset;
+ guint interp;
+ guint height;
+ guint width;
+ } thumb_tiff;
+ } thumb;
+
+ gboolean big_endian;
+} TvtjExif;
+
+
+
+static guint
+tvtj_exif_get_ushort (const TvtjExif *exif,
+ const guchar *data)
+{
+ if (G_UNLIKELY (exif->big_endian))
+ return ((data[0] << 8) | data[1]);
+ else
+ return ((data[1] << 8) | data[0]);
+}
+
+
+
+static guint
+tvtj_exif_get_ulong (const TvtjExif *exif,
+ const guchar *data)
+{
+ if (G_UNLIKELY (exif->big_endian))
+ return ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
+ else
+ return ((data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0]);
+}
+
+
+
+static void
+tvtj_exif_parse_ifd (TvtjExif *exif,
+ const guchar *ifd_ptr,
+ guint ifd_len,
+ GSList *ifd_previous_list)
+{
+ const guchar *subifd_ptr;
+ GSList ifd_list;
+ guint subifd_off;
+ guint value;
+ guint tag;
+ guint n;
+
+ /* make sure we have a valid IFD here */
+ if (G_UNLIKELY (ifd_len < 2))
+ return;
+
+ /* make sure we don't recurse into IFDs that are already being processed */
+ if (g_slist_find (ifd_previous_list, ifd_ptr) != NULL)
+ return;
+ ifd_list.next = ifd_previous_list;
+ ifd_list.data = (gpointer) ifd_ptr;
+
+ /* determine the number of entries */
+ n = tvtj_exif_get_ushort (exif, ifd_ptr);
+
+ /* advance to the IFD content */
+ ifd_ptr += 2;
+ ifd_len -= 2;
+
+ /* validate the number of entries */
+ if (G_UNLIKELY (n * 12 > ifd_len))
+ n = ifd_len / 12;
+
+ /* process all IFD entries */
+ for (; n > 0; ifd_ptr += 12, --n)
+ {
+ /* determine the tag of this entry */
+ tag = tvtj_exif_get_ushort (exif, ifd_ptr);
+ if (tag == 0x8769 || tag == 0xa005)
+ {
+ /* check if we have a valid sub IFD offset here */
+ subifd_off = tvtj_exif_get_ulong (exif, ifd_ptr + 8);
+ subifd_ptr = exif->data_ptr + subifd_off;
+ if (G_LIKELY (subifd_off < exif->data_len))
+ {
+ /* process the sub IFD recursively */
+ tvtj_exif_parse_ifd (exif, subifd_ptr, exif->data_len - subifd_off, &ifd_list);
+ }
+ }
+ else if (tag == 0x0103)
+ {
+ /* verify that we have an ushort here (format 3) */
+ if (tvtj_exif_get_ushort (exif, ifd_ptr + 2) == 3)
+ {
+ /* determine the thumbnail compression */
+ exif->thumb_compression = tvtj_exif_get_ushort (exif, ifd_ptr + 8);
+ }
+ }
+ else if (tag == 0x0100 || tag == 0x0101 || tag == 0x0106 || tag == 0x0111 || tag == 0x0117)
+ {
+ /* this can be either ushort or ulong */
+ if (tvtj_exif_get_ushort (exif, ifd_ptr + 2) == 3)
+ value = tvtj_exif_get_ushort (exif, ifd_ptr + 8);
+ else if (tvtj_exif_get_ushort (exif, ifd_ptr + 2) == 4)
+ value = tvtj_exif_get_ulong (exif, ifd_ptr + 8);
+ else
+ value = 0;
+
+ /* and remember it appropriately */
+ if (tag == 0x0100)
+ exif->thumb.thumb_tiff.width = value;
+ else if (tag == 0x0100)
+ exif->thumb.thumb_tiff.height = value;
+ else if (tag == 0x0106)
+ exif->thumb.thumb_tiff.interp = value;
+ else if (tag == 0x0111)
+ exif->thumb.thumb_tiff.offset = value;
+ else
+ exif->thumb.thumb_tiff.length = value;
+ }
+ else if (tag == 0x0201 || tag == 0x0202)
+ {
+ /* verify that we have an ulong here (format 4) */
+ if (tvtj_exif_get_ushort (exif, ifd_ptr + 2) == 4)
+ {
+ /* determine the value (thumbnail JPEG offset or length) */
+ value = tvtj_exif_get_ulong (exif, ifd_ptr + 8);
+
+ /* and remember it appropriately */
+ if (G_LIKELY (tag == 0x201))
+ exif->thumb.thumb_jpeg.offset = value;
+ else
+ exif->thumb.thumb_jpeg.length = value;
+ }
+ }
+ }
+
+ /* check for link to next IFD */
+ subifd_off = tvtj_exif_get_ulong (exif, ifd_ptr);
+ if (subifd_off != 0 && subifd_off < exif->data_len)
+ {
+ /* parse next IFD recursively as well */
+ tvtj_exif_parse_ifd (exif, exif->data_ptr + subifd_off, exif->data_len - subifd_off, &ifd_list);
+ }
+}
+
+
+
+static GdkPixbuf*
+tvtj_exif_extract_thumbnail (const guchar *data,
+ guint length,
+ gint size)
+{
+ TvtjExif exif;
+ guint offset;
+
+ /* make sure we have enough data */
+ if (G_UNLIKELY (length < 6 + 8))
+ return NULL;
+
+ /* validate Exif header */
+ if (memcmp (data, "Exif\0\0", 6) != 0)
+ return NULL;
+
+ /* advance to TIFF header */
+ data += 6;
+ length -= 6;
+
+ /* setup Exif data struct */
+ memset (&exif, 0, sizeof (exif));
+ exif.data_ptr = data;
+ exif.data_len = length;
+
+ /* determine byte order */
+ if (memcmp (data, "II", 2) == 0)
+ exif.big_endian = FALSE;
+ else if (memcmp (data, "MM", 2) == 0)
+ exif.big_endian = TRUE;
+ else
+ return NULL;
+
+ /* validate the TIFF header */
+ if (tvtj_exif_get_ushort (&exif, data + 2) != 0x2a)
+ return NULL;
+
+ /* determine the first IFD offset */
+ offset = tvtj_exif_get_ulong (&exif, data + 4);
+
+ /* validate the offset */
+ if (G_LIKELY (offset < length))
+ {
+ /* parse the first IFD (recursively parses the remaining...) */
+ tvtj_exif_parse_ifd (&exif, data + offset, length - offset, NULL);
+
+ /* check thumbnail compression type */
+ if (G_LIKELY (exif.thumb_compression == 6)) /* JPEG */
+ {
+ /* check if we have a valid thumbnail JPEG */
+ if (exif.thumb.thumb_jpeg.offset > 0 && exif.thumb.thumb_jpeg.length > 0
+ && exif.thumb.thumb_jpeg.offset + exif.thumb.thumb_jpeg.length <= length)
+ {
+ /* try to load the embedded thumbnail JPEG */
+ return tvtj_jpeg_load (data + exif.thumb.thumb_jpeg.offset, exif.thumb.thumb_jpeg.length, size);
+ }
+ }
+ else if (exif.thumb_compression == 1) /* Uncompressed */
+ {
+ /* check if we have a valid thumbnail (current only RGB interpretations) */
+ if (G_LIKELY (exif.thumb.thumb_tiff.interp == 2)
+ && exif.thumb.thumb_tiff.offset > 0 && exif.thumb.thumb_tiff.length > 0
+ && exif.thumb.thumb_tiff.offset + exif.thumb.thumb_tiff.length <= length
+ && exif.thumb.thumb_tiff.height * exif.thumb.thumb_tiff.width == exif.thumb.thumb_tiff.length)
+ {
+ /* plain RGB data, just what we need for a GdkPixbuf */
+ return gdk_pixbuf_new_from_data (g_memdup (data + exif.thumb.thumb_tiff.offset, exif.thumb.thumb_tiff.length),
+ GDK_COLORSPACE_RGB, FALSE, 8, exif.thumb.thumb_tiff.width,
+ exif.thumb.thumb_tiff.height, exif.thumb.thumb_tiff.width,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+
+static GdkPixbuf*
+tvtj_jpeg_load_thumbnail (const JOCTET *content,
+ gsize length,
+ gint size)
+{
+ guint marker_len;
+ guint marker;
+ gsize n;
+
+ /* valid JPEG headers begin with SOI (Start Of Image) */
+ if (G_LIKELY (length >= 2 && content[0] == 0xff && content[1] == 0xd8))
+ {
+ /* search for an EXIF marker */
+ for (length -= 2, n = 2; n < length; )
+ {
+ /* check for valid marker start */
+ if (G_UNLIKELY (content[n++] != 0xff))
+ break;
+
+ /* determine the next marker */
+ marker = content[n];
+
+ /* skip additional padding */
+ if (G_UNLIKELY (marker == 0xff))
+ continue;
+
+ /* stop at SOS marker */
+ if (marker == 0xda)
+ break;
+
+ /* advance */
+ ++n;
+
+ /* check if valid */
+ if (G_UNLIKELY (n + 2 >= length))
+ break;
+
+ /* determine the marker length */
+ marker_len = (content[n] << 8) | content[n + 1];
+
+ /* check if we have an exif marker here */
+ if (marker == 0xe1 && n + marker_len <= length)
+ {
+ /* try to extract the Exif thumbnail */
+ return tvtj_exif_extract_thumbnail (content + n + 2, marker_len - 2, size);
+ }
+
+ /* try next one then */
+ n += marker_len;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+static void
+folder_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
+ GCancellable *cancellable,
+ TumblerFileInfo *info)
+{
+ TumblerThumbnailFlavor *flavor;
+ TumblerImageData data;
+ TumblerThumbnail *thumbnail;
+ struct stat statb;
+ const gchar *uri;
+ GdkPixbuf *pixbuf = NULL;
+ gboolean streaming_needed = TRUE;
+ JOCTET *content;
+ GError *error = NULL;
+ GFile *file = NULL;
+ GFile *folder;
+ gchar *path;
+ gsize length;
+ guint n;
+ gint fd;
+ gint height;
+ gint width;
+ gint size;
+
+ g_return_if_fail (IS_FOLDER_THUMBNAILER (thumbnailer));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (TUMBLER_IS_FILE_INFO (info));
+
+ /* do nothing if cancelled */
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ thumbnail = tumbler_file_info_get_thumbnail (info);
+ g_assert (thumbnail != NULL);
+
+ flavor = tumbler_thumbnail_get_flavor (thumbnail);
+ g_assert (flavor != NULL);
+
+ uri = tumbler_file_info_get_uri (info);
+
+ /* try to open the source file for reading */
+ folder = g_file_new_for_uri (uri);
+ for (n = 0; file == NULL && n < G_N_ELEMENTS (cover_filenames); ++n)
+ {
+ file = g_file_get_child (folder, cover_filenames[n]);
+ if (!g_file_query_exists (file, cancellable))
+ {
+ g_object_unref (file);
+ file = NULL;
+ }
+ }
+ g_object_unref (folder);
+
+ if (file == NULL)
+ {
+ g_set_error (&error, TUMBLER_ERROR,
+ TUMBLER_ERROR_INVALID_FORMAT,
+ _("No album cover file found in this folder"));
+ }
+
+ tumbler_thumbnail_flavor_get_size (flavor, &width, &height);
+ size = MIN (width, height);
+
+#ifdef HAVE_MMAP
+ if (error == NULL && g_file_is_native (file))
+ {
+ path = g_file_get_path (file);
+
+ /* try to open the file at the given path */
+ fd = open (path, O_RDONLY);
+ if (G_LIKELY (fd >= 0))
+ {
+ /* determine the status of the file */
+ if (G_LIKELY (fstat (fd, &statb) == 0 && statb.st_size > 0))
+ {
+ /* try to mmap the file */
+ content = (JOCTET *) mmap (NULL, statb.st_size, PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ /* verify whether the mmap was successful */
+ if (G_LIKELY (content != (JOCTET *) MAP_FAILED))
+ {
+ /* try to load the embedded thumbnail first */
+ pixbuf = tvtj_jpeg_load_thumbnail (content, statb.st_size, size);
+ if (pixbuf == NULL)
+ {
+ /* fall back to loading and scaling the image itself */
+ pixbuf = tvtj_jpeg_load (content, statb.st_size, size);
+
+ if (pixbuf == NULL)
+ {
+ g_set_error (&error, TUMBLER_ERROR,
+ TUMBLER_ERROR_INVALID_FORMAT,
+ _("Thumbnail could not be inferred from file contents"));
+ }
+ }
+
+ /* we have successfully mmapped the file. we may not have
+ * a thumbnail but trying to read the image from a stream
+ * won't help us here, so we don't need to attempt streaming
+ * as a fallback */
+ streaming_needed = FALSE;
+ }
+
+ /* unmap the file content */
+ munmap ((void *) content, statb.st_size);
+ }
+
+ /* close the file */
+ close (fd);
+ }
+
+ g_free (path);
+ }
+#endif
+
+ if (error == NULL && streaming_needed)
+ {
+ g_file_load_contents (file, cancellable, (gchar **)&content, &length,
+ NULL, &error);
+
+ if (error == NULL)
+ {
+ pixbuf = tvtj_jpeg_load_thumbnail (content, length, size);
+
+ if (pixbuf == NULL)
+ {
+ pixbuf = tvtj_jpeg_load (content, length, size);
+ if (pixbuf == NULL)
+ {
+ g_set_error (&error, TUMBLER_ERROR, TUMBLER_ERROR_INVALID_FORMAT,
+ _("Thumbnail could not be inferred from file contents"));
+ }
+ }
+ }
+ }
+
+ /* either we have an error now or we have a valid thumbnail pixbuf */
+ g_assert (error != NULL || pixbuf != NULL);
+
+ if (pixbuf != NULL)
+ {
+ data.data = gdk_pixbuf_get_pixels (pixbuf);
+ data.has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+ data.bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf);
+ data.width = gdk_pixbuf_get_width (pixbuf);
+ data.height = gdk_pixbuf_get_height (pixbuf);
+ data.rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ data.colorspace = (TumblerColorspace) gdk_pixbuf_get_colorspace (pixbuf);
+
+ tumbler_thumbnail_save_image_data (thumbnail, &data,
+ tumbler_file_info_get_mtime (info),
+ NULL, &error);
+
+ g_object_unref (pixbuf);
+ }
+
+ if (error != NULL)
+ {
+ g_signal_emit_by_name (thumbnailer, "error", uri, error->code, error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_signal_emit_by_name (thumbnailer, "ready", uri);
+ }
+
+ g_object_unref (flavor);
+ g_object_unref (thumbnail);
+
+ if (file != NULL)
+ g_object_unref (file);
+}
+
diff --git a/plugins/folder-thumbnailer/folder-thumbnailer.h b/plugins/folder-thumbnailer/folder-thumbnailer.h
new file mode 100644
index 0000000..055f584
--- /dev/null
+++ b/plugins/folder-thumbnailer/folder-thumbnailer.h
@@ -0,0 +1,43 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __FOLDER_THUMBNAILER_H__
+#define __FOLDER_THUMBNAILER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS;
+
+#define TYPE_FOLDER_THUMBNAILER (folder_thumbnailer_get_type ())
+#define FOLDER_THUMBNAILER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOLDER_THUMBNAILER, FolderThumbnailer))
+#define FOLDER_THUMBNAILER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOLDER_THUMBNAILER, FolderThumbnailerClass))
+#define IS_FOLDER_THUMBNAILER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOLDER_THUMBNAILER))
+#define IS_FOLDER_THUMBNAILER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOLDER_THUMBNAILER)
+#define FOLDER_THUMBNAILER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOLDER_THUMBNAILER, FolderThumbnailerClass))
+
+typedef struct _FolderThumbnailerClass FolderThumbnailerClass;
+typedef struct _FolderThumbnailer FolderThumbnailer;
+
+GType folder_thumbnailer_get_type (void) G_GNUC_CONST;
+void folder_thumbnailer_register (TumblerProviderPlugin *plugin);
+
+G_END_DECLS;
+
+#endif /* !__FOLDER_THUMBNAILER_H__ */