[Xfce4-commits] <tumbler:jannis/specialized> Massive refactoring to support flavors properly.

Jannis Pohlmann noreply at xfce.org
Tue Oct 27 18:48:03 CET 2009


Updating branch refs/heads/jannis/specialized
         to 43ae2cf88831f0d4a9c4c28784ef103f0c18b567 (commit)
       from 725d6a7cbf803786e46bada5da0e72d50b505376 (commit)

commit 43ae2cf88831f0d4a9c4c28784ef103f0c18b567
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Tue Oct 27 03:13:42 2009 +0100

    Massive refactoring to support flavors properly.
    
    Refactor the entire code to use TumblerFileInfo instead of separate
    URI/MIME/flavor arrays. We now support flavors which means that
    applications can request "normal" and "large" thumbnails and thus have
    more control over what is created. This simplifies the code in a lot of
    places. We now use a real GObject class called TumblerThumbnailFlavor
    instead of a fixed-size enum. Cache backends can return as many flavors
    as they want, each consisting of a name, a width and a height. As a
    consequence, we can also get rid of the configure flags to
    enable/disable normal, large and cropped thumbnails. The default cache
    plugin shipped with Tumbler only supports "normal" (128x128px) and
    "large" (256x256px).
    
    Optimize the code a little bit. We now load the TunarFileInfo and
    ThunarThumbnail only *once* for each URI instead of loading them once in
    the scheduler and again in thumbnailer plugins.
    
    This needs careful testing. I might have introduced a few memory leaks
    here and there, plus new bugs... you never know.

 configure.ac                                    |   45 ----
 plugins/font-thumbnailer/font-thumbnailer.c     |  181 ++++++---------
 plugins/pixbuf-thumbnailer/pixbuf-thumbnailer.c |  102 +++-----
 plugins/xdg-cache/xdg-cache-cache.c             |  282 +++++++++++------------
 plugins/xdg-cache/xdg-cache-cache.h             |   32 ++--
 plugins/xdg-cache/xdg-cache-thumbnail.c         |   14 +-
 tumbler/Makefile.am                             |    2 +
 tumbler/tumbler-abstract-thumbnailer.c          |   16 +-
 tumbler/tumbler-abstract-thumbnailer.h          |    6 +-
 tumbler/tumbler-cache-plugin.c                  |  219 ++++++++++++++++++
 tumbler/tumbler-cache-plugin.h                  |   46 ++++
 tumbler/tumbler-cache-provider.c                |   64 -----
 tumbler/tumbler-cache-provider.h                |   56 -----
 tumbler/tumbler-cache.c                         |   50 ++++-
 tumbler/tumbler-cache.h                         |   73 ++++---
 tumbler/tumbler-enum-types.c                    |   24 --
 tumbler/tumbler-enum-types.h                    |   12 -
 tumbler/tumbler-file-info.c                     |  184 ++++++++++++---
 tumbler/tumbler-file-info.h                     |   31 ++-
 tumbler/tumbler-thumbnail-flavor.c              |  240 +++++++++++++++++++
 tumbler/tumbler-thumbnail-flavor.h              |   57 +++++
 tumbler/tumbler-thumbnail.c                     |   68 +-----
 tumbler/tumbler-thumbnail.h                     |   10 +-
 tumbler/tumbler-thumbnailer.c                   |   26 +--
 tumbler/tumbler-thumbnailer.h                   |   22 +-
 tumbler/tumbler.h                               |    3 +-
 tumblerd/tumbler-group-scheduler.c              |   49 +---
 tumblerd/tumbler-lifo-scheduler.c               |   56 ++----
 tumblerd/tumbler-registry.c                     |   36 ++--
 tumblerd/tumbler-registry.h                     |    5 +-
 tumblerd/tumbler-scheduler.c                    |   28 +--
 tumblerd/tumbler-scheduler.h                    |   15 +-
 tumblerd/tumbler-service.c                      |  100 +++++----
 tumblerd/tumbler-service.h                      |    8 +-
 tumblerd/tumbler-specialized-thumbnailer.c      |    8 +-
 35 files changed, 1248 insertions(+), 922 deletions(-)

diff --git a/configure.ac b/configure.ac
index a036824..f8988d7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -152,33 +152,6 @@ TUMBLER_PIXBUF_THUMBNAILER()
 TUMBLER_FONT_THUMBNAILER()
 TUMBLER_XDG_CACHE()
 
-dnl ************************************************************
-dnl *** Check for thumbnail flavors (normal, large, cropped) ***
-dnl ************************************************************
-AC_ARG_ENABLE([normal-thumbnails], 
-              [AC_HELP_STRING([--enable-normal-thumbnails], 
-                              [Enable normal thumbnails (128 pixels) @<:@default=yes@:>@])],, 
-              [enable_normal_thumbnails=yes])
-if test x"$enable_normal_thumbnails" = x"yes"; then
-  AC_DEFINE([ENABLE_NORMAL_THUMBNAILS], [1], [Define if built with support for normal thumbnails])
-fi
-
-AC_ARG_ENABLE([large-thumbnails], 
-              [AC_HELP_STRING([--enable-large-thumbnails], 
-                              [Enable large thumbnails (256 pixels) @<:@default=no@:>@])],, 
-              [enable_large_thumbnails=no])
-if test x"$enable_large_thumbnails" = x"yes"; then
-  AC_DEFINE([ENABLE_LARGE_THUMBNAILS], [1], [Define if built with support for large thumbnails])
-fi
-
-AC_ARG_ENABLE([cropped-thumbnails], 
-              [AC_HELP_STRING([--enable-cropped-thumbnails], 
-                              [Enable cropped thumbnails (124 pixels) @<:@default=no@:>@])],, 
-              [enable_cropped_thumbnails=no])
-if test x"$enable_cropped_thumbnails" = x"yes"; then
-  AC_DEFINE([ENABLE_CROPPED_THUMBNAILS], [1], [Define if built with support for cropped thumbnails])
-fi
-
 dnl ***********************************
 dnl *** Check for debugging support ***
 dnl ***********************************
@@ -238,21 +211,3 @@ else
 echo "  * Freedesktop.org cache plugin:     no"
 fi
 echo
-echo "Supported Thumbnail Flavors:"
-echo
-if test x"$enable_normal_thumbnails" = x"yes"; then
-echo "  * Normal (128 pixels):              yes"
-else
-echo "  * Normal (128 pixels):              no"
-fi
-if test x"$enable_large_thumbnails" = x"yes"; then
-echo "  * Large (256 pixels):               yes"
-else
-echo "  * Large (256 pixels):               no"
-fi
-if test x"$enable_cropped_thumbnails" = x"yes"; then
-echo "  * Cropped (124 pixels):             yes"
-else
-echo "  * Cropped (124 pixels):             no"
-fi
-echo
diff --git a/plugins/font-thumbnailer/font-thumbnailer.c b/plugins/font-thumbnailer/font-thumbnailer.c
index d8be2a1..76ae117 100644
--- a/plugins/font-thumbnailer/font-thumbnailer.c
+++ b/plugins/font-thumbnailer/font-thumbnailer.c
@@ -43,9 +43,7 @@
 static void font_thumbnailer_finalize (GObject                    *object);
 static void font_thumbnailer_create   (TumblerAbstractThumbnailer *thumbnailer,
                                        GCancellable               *cancellable,
-                                       const gchar                *uri,
-                                       const gchar                *mime_hint,
-                                       const gchar                *flavor);
+                                       TumblerFileInfo            *info);
 
 
 
@@ -371,9 +369,9 @@ trim_and_scale_pixbuf (GdkPixbuf *pixbuf,
 
 
 static GdkPixbuf *
-generate_pixbuf (FT_Face                face,
-                 TumblerThumbnailFlavor flavor,
-                 FT_Error              *error)
+generate_pixbuf (FT_Face                 face,
+                 TumblerThumbnailFlavor *flavor,
+                 FT_Error               *error)
 {
   GdkPixbuf *pixbuf = NULL;
   GdkPixbuf *result = NULL;
@@ -400,34 +398,27 @@ generate_pixbuf (FT_Face                face,
   if (G_UNLIKELY (glyph2 == 0))
     glyph2 = MIN (97, face->num_glyphs - 1);
   
-  if (flavor == TUMBLER_THUMBNAIL_FLAVOR_CROPPED)
-    {
-      /* TODO unsupported */
-    }
-  else
-    {
-      /* allocate the pixbuf to render the glyphs to */
-      pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width * 3, (height * 3) / 2);
-      gdk_pixbuf_fill (pixbuf, 0xffffffff);
-
-      /* initial pen position */
-      pen_x = width / 2;
-      pen_y = height;
-
-      /* render the first letter to the pixbuf */
-      *error = render_glyph (pixbuf, face, glyph1, &pen_x, &pen_y);
-      if (G_UNLIKELY (*error != 0))
-        return NULL;
-
-      /* render the second letter to the pixbuf */
-      *error = render_glyph (pixbuf, face, glyph2, &pen_x, &pen_y);
-      if (G_UNLIKELY (*error != 0))
-        return NULL;
-
-      /* trim the pixbuf and rescale if necessary */
-      result = trim_and_scale_pixbuf (pixbuf, width, height);
-      g_object_unref (pixbuf);
-    }
+  /* allocate the pixbuf to render the glyphs to */
+  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width * 3, (height * 3) / 2);
+  gdk_pixbuf_fill (pixbuf, 0xffffffff);
+
+  /* initial pen position */
+  pen_x = width / 2;
+  pen_y = height;
+
+  /* render the first letter to the pixbuf */
+  *error = render_glyph (pixbuf, face, glyph1, &pen_x, &pen_y);
+  if (G_UNLIKELY (*error != 0))
+    return NULL;
+
+  /* render the second letter to the pixbuf */
+  *error = render_glyph (pixbuf, face, glyph2, &pen_x, &pen_y);
+  if (G_UNLIKELY (*error != 0))
+    return NULL;
+
+  /* trim the pixbuf and rescale if necessary */
+  result = trim_and_scale_pixbuf (pixbuf, width, height);
+  g_object_unref (pixbuf);
 
   return result;
 }
@@ -437,15 +428,13 @@ generate_pixbuf (FT_Face                face,
 static void
 font_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
                          GCancellable               *cancellable,
-                         const gchar                *uri,
-                         const gchar                *mime_hint,
-                         const gchar                *flavor_)
+                         TumblerFileInfo            *info)
 {
-  TumblerThumbnailFlavor *flavors;
-  TumblerThumbnailFlavor  flavor;
+  TumblerThumbnailFlavor *flavor;
   TumblerImageData        data;
-  TumblerFileInfo        *info;
+  TumblerThumbnail       *thumbnail;
   FontThumbnailer        *font_thumbnailer = FONT_THUMBNAILER (thumbnailer);
+  const gchar            *uri;
   GHashTable             *pixbufs;
   GdkPixbuf              *pixbuf;
   FT_Error                ft_error;
@@ -461,12 +450,15 @@ font_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
   gint                    n;
 
   g_return_if_fail (IS_FONT_THUMBNAILER (thumbnailer));
-  g_return_if_fail (uri != NULL && *uri != '\0');
+  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;
 
+  uri = tumbler_file_info_get_uri (info);
+
   /* check if we have a valid freetype library object */
   if (font_thumbnailer->library_error != 0)
     {
@@ -478,25 +470,13 @@ font_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
       return;
     }
 
-  /* create the file info for this URI */
-  info = tumbler_file_info_new (uri);
-
-  /* try to load the file information */
-  if (!tumbler_file_info_load (info, NULL, &error))
-    {
-      g_signal_emit_by_name (thumbnailer, "error", uri, error->code, error->message);
-      g_error_free (error);
-      g_object_unref (info);
-      return;
-    }
-
   /* try to read the file into memory */
-  file = g_file_new_for_uri (uri);
+  file = g_file_new_for_uri (tumbler_file_info_get_uri (info));
   if (!g_file_load_contents (file, cancellable, &font_data, &length, NULL, &error))
     {
       /* there was an error, emit error signal */
       error_msg = g_strdup_printf (_("Could not load file contents: %s"), 
-                                    error->message);
+                                   error->message);
       g_signal_emit_by_name (thumbnailer, "error", uri, 0, error_msg);
       g_free (error_msg);
 
@@ -555,40 +535,30 @@ font_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
         }
     }
 
-  /* determine which flavors we need to generate */
-  flavors = tumbler_thumbnail_get_flavors ();
+  thumbnail = tumbler_file_info_get_thumbnail (info);
+
+  g_assert (thumbnail != NULL);
 
-  /* allocate a hash table in which to store the flavor pixbufs */
-  pixbufs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+  /* generate a thumbnail for the flavor */
+  flavor = tumbler_thumbnail_get_flavor (thumbnail);
+  pixbuf = generate_pixbuf (face, flavor, &ft_error);
+  g_object_unref (flavor);
 
-  /* iterate over all flavors */
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
+  /* abort if there was an error */
+  if (G_UNLIKELY (ft_error != 0))
     {
-      /* generate a thumbnail for the current flavor */
-      pixbuf = generate_pixbuf (face, flavors[n], &ft_error);
+      /* emit an error signal */
+      error_msg = g_strdup_printf (_("Could not render glyphs: %s"),
+                                   ft_strerror (ft_error));
+      g_signal_emit_by_name (thumbnailer, "error", uri, 0, error_msg);
+      g_free (error_msg);
 
-      /* abort if there was an error */
-      if (G_UNLIKELY (ft_error != 0))
-        {
-          /* emit an error signal */
-          error_msg = g_strdup_printf (_("Could not render glyphs: %s"),
-                                       ft_strerror (ft_error));
-          g_signal_emit_by_name (thumbnailer, "error", uri, 0, error_msg);
-          g_free (error_msg);
-
-          /* clean up */
-          g_hash_table_unref (pixbufs);
-          g_free (font_data);
-          FT_Done_Face (face);
-          g_object_unref (info);
-
-          return;
-        }
-      else
-        {
-          /* add the pixbuf to the hash table if it could be generated */
-          g_hash_table_insert (pixbufs, GINT_TO_POINTER (flavors[n]), pixbuf);
-        }
+      /* clean up */
+      g_free (font_data);
+      FT_Done_Face (face);
+      g_object_unref (info);
+
+      return;
     }
 
   /* release the font face */
@@ -597,35 +567,17 @@ font_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
   /* determine when the URI was last modified */
   mtime = tumbler_file_info_get_mtime (info);
 
-  /* get all thumbnail infos for this URI */
-  thumbnails = tumbler_file_info_get_thumbnails (info);
+  /* compose the image data */
+  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);
 
-  /* iterate over all thumbnail infos */
-  for (lp = thumbnails; error == NULL && lp != NULL; lp = lp->next)
-    {
-      /* load the thumbnail information and check if it is possibly outdated */
-      if (tumbler_thumbnail_load (lp->data, NULL, &error))
-        if (tumbler_thumbnail_needs_update (lp->data, uri, mtime))
-          {
-            /* get the pixbuf for the flavor of the thumbnail info */
-            flavor = tumbler_thumbnail_get_flavor (lp->data);
-            pixbuf = g_hash_table_lookup (pixbufs, GINT_TO_POINTER (flavor));
-
-            /* if an image for this flavor was generated, save it now */
-            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 (lp->data, &data, mtime, NULL, &error);
-              }
-          }
-    }
+  /* save the thumbnail */
+  tumbler_thumbnail_save_image_data (thumbnail, &data, mtime, NULL, &error);
 
   /* check if there was an error */
   if (error != NULL)
@@ -641,7 +593,8 @@ font_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
     }
 
   /* clean up */
+  g_object_unref (pixbuf);
+  g_object_unref (thumbnail);
   g_free (font_data);
-  g_hash_table_unref (pixbufs);
   g_object_unref (info);
 }
diff --git a/plugins/pixbuf-thumbnailer/pixbuf-thumbnailer.c b/plugins/pixbuf-thumbnailer/pixbuf-thumbnailer.c
index 5ffabf6..83e272f 100644
--- a/plugins/pixbuf-thumbnailer/pixbuf-thumbnailer.c
+++ b/plugins/pixbuf-thumbnailer/pixbuf-thumbnailer.c
@@ -37,9 +37,7 @@
 
 static void pixbuf_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
                                        GCancellable               *cancellable,
-                                       const gchar                *uri,
-                                       const gchar                *mime_hint,
-                                       const gchar                *flavor);
+                                       TumblerFileInfo            *info);
 
 
 
@@ -96,7 +94,7 @@ pixbuf_thumbnailer_init (PixbufThumbnailer *thumbnailer)
 
 static GdkPixbuf *
 generate_pixbuf (GdkPixbuf              *source,
-                 TumblerThumbnailFlavor  flavor)
+                 TumblerThumbnailFlavor *flavor)
 {
   gdouble    hratio;
   gdouble    wratio;
@@ -116,25 +114,19 @@ generate_pixbuf (GdkPixbuf              *source,
   if (source_width <= dest_width && source_height <= dest_height)
     return g_object_ref (source);
 
-  if (flavor == TUMBLER_THUMBNAIL_FLAVOR_CROPPED)
-    {
-      /* TODO unsupported */
-    }
-  else
-    {
-      /* determine which axis needs to be scaled down more */
-      wratio = (gdouble) source_width / (gdouble) dest_width;
-      hratio = (gdouble) source_height / (gdouble) dest_height;
-
-      /* adjust the other axis */
-      if (hratio > wratio)
-        dest_width = rint (source_width / hratio);
-      else
-        dest_height = rint (source_height / wratio);
-    }
+  /* determine which axis needs to be scaled down more */
+  wratio = (gdouble) source_width / (gdouble) dest_width;
+  hratio = (gdouble) source_height / (gdouble) dest_height;
 
+  /* adjust the other axis */
+  if (hratio > wratio)
+    dest_width = rint (source_width / hratio);
+  else
+    dest_height = rint (source_height / wratio);
+  
   /* scale the pixbuf down to the desired size */
-  return gdk_pixbuf_scale_simple (source, MAX (dest_width, 1), MAX (dest_height, 1), 
+  return gdk_pixbuf_scale_simple (source, 
+                                  MAX (dest_width, 1), MAX (dest_height, 1), 
                                   GDK_INTERP_BILINEAR);
 }
 
@@ -143,15 +135,13 @@ generate_pixbuf (GdkPixbuf              *source,
 static void
 pixbuf_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
                            GCancellable               *cancellable,
-                           const gchar                *uri,
-                           const gchar                *mime_hint,
-                           const gchar                *flavor_)
+                           TumblerFileInfo            *info)
 {
-  TumblerThumbnailFlavor *flavors;
-  TumblerThumbnailFlavor  flavor;
+  TumblerThumbnailFlavor *flavor;
   GFileInputStream       *stream;
   TumblerImageData        data;
-  TumblerFileInfo        *info;
+  TumblerThumbnail       *thumbnail;
+  const gchar            *uri;
   GHashTable             *pixbufs;
   GdkPixbuf              *source_pixbuf;
   GdkPixbuf              *pixbuf;
@@ -163,14 +153,14 @@ pixbuf_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
   guint                   n;
 
   g_return_if_fail (IS_PIXBUF_THUMBNAILER (thumbnailer));
-  g_return_if_fail (uri != NULL && *uri != '\0');
+  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;
- 
-  /* create the file info for this URI */
-  info = tumbler_file_info_new (uri);
+
+  uri = tumbler_file_info_get_uri (info);
 
   /* try to load the file information */
   if (!tumbler_file_info_load (info, NULL, &error))
@@ -206,43 +196,26 @@ pixbuf_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
       return;
     }
 
-  flavors = tumbler_thumbnail_get_flavors ();
-  pixbufs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+  thumbnail = tumbler_file_info_get_thumbnail (info);
 
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
-    {
-      pixbuf = generate_pixbuf (source_pixbuf, flavors[n]);
+  g_assert (thumbnail != NULL);
 
-      if (pixbuf != NULL)
-        g_hash_table_insert (pixbufs, GINT_TO_POINTER (flavors[n]), pixbuf);
-    }
+  /* generate a pixbuf for the thumbnail */
+  flavor = tumbler_thumbnail_get_flavor (thumbnail);
+  pixbuf = generate_pixbuf (source_pixbuf, flavor);
+  g_object_unref (flavor);
 
-  mtime = tumbler_file_info_get_mtime (info);
+  g_assert (pixbuf != NULL);
 
-  thumbnails = tumbler_file_info_get_thumbnails (info);
+  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);
 
-  for (lp = thumbnails; error == NULL && lp != NULL; lp = lp->next)
-    {
-      if (tumbler_thumbnail_load (lp->data, NULL, &error))
-        if (tumbler_thumbnail_needs_update (lp->data, uri, mtime))
-          {
-            flavor = tumbler_thumbnail_get_flavor (lp->data);
-            pixbuf = g_hash_table_lookup (pixbufs, GINT_TO_POINTER (flavor));
-
-            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 (lp->data, &data, mtime, NULL, &error);
-              }
-          }
-    }
+  tumbler_thumbnail_save_image_data (thumbnail, &data, mtime, NULL, &error);
 
   if (error != NULL)
     {
@@ -254,7 +227,8 @@ pixbuf_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
       g_signal_emit_by_name (thumbnailer, "ready", uri);
     }
 
-  g_hash_table_unref (pixbufs);
+  g_object_unref (thumbnail);
+  g_object_unref (pixbuf);
   g_object_unref (source_pixbuf);
   g_object_unref (info);
 }
diff --git a/plugins/xdg-cache/xdg-cache-cache.c b/plugins/xdg-cache/xdg-cache-cache.c
index add9a16..de3d73b 100644
--- a/plugins/xdg-cache/xdg-cache-cache.c
+++ b/plugins/xdg-cache/xdg-cache-cache.c
@@ -40,28 +40,26 @@
 
 
 
-typedef struct _FlavorInfo FlavorInfo;
-
-
-
-static void         xdg_cache_cache_iface_init         (TumblerCacheIface     *iface);
-static GList       *xdg_cache_cache_get_thumbnails     (TumblerCache          *cache,
-                                                        const gchar           *uri);
-static void         xdg_cache_cache_cleanup            (TumblerCache          *cache,
-                                                        const gchar           *uri_prefix,
-                                                        guint64                since);
-static void         xdg_cache_cache_delete             (TumblerCache          *cache,
-                                                        const GStrv            uris);
-static void         xdg_cache_cache_copy               (TumblerCache          *cache,
-                                                        const GStrv            from_uris,
-                                                        const GStrv            to_uris);
-static void         xdg_cache_cache_move               (TumblerCache          *cache,
-                                                        const GStrv            from_uris,
-                                                        const GStrv            to_uris);
-static gboolean     xdg_cache_cache_is_thumbnail       (TumblerCache          *cache,
-                                                        const gchar           *uri);
-static const gchar *xdg_cache_cache_get_flavor_dirname (TumblerThumbnailFlavor flavor);
-static const gchar *xdg_cache_cache_get_home           (void);
+static void              xdg_cache_cache_iface_init         (TumblerCacheIface      *iface);
+static void              xdg_cache_cache_finalize           (GObject                *object);
+static TumblerThumbnail *xdg_cache_cache_get_thumbnail      (TumblerCache           *cache,
+                                                             const gchar            *uri,
+                                                             TumblerThumbnailFlavor *flavor);
+static void              xdg_cache_cache_cleanup            (TumblerCache           *cache,
+                                                             const gchar            *uri_prefix,
+                                                             guint64                 since);
+static void              xdg_cache_cache_delete             (TumblerCache           *cache,
+                                                             const GStrv             uris);
+static void              xdg_cache_cache_copy               (TumblerCache           *cache,
+                                                             const GStrv             from_uris,
+                                                             const GStrv             to_uris);
+static void              xdg_cache_cache_move               (TumblerCache           *cache,
+                                                             const GStrv             from_uris,
+                                                             const GStrv             to_uris);
+static gboolean          xdg_cache_cache_is_thumbnail       (TumblerCache           *cache,
+                                                             const gchar            *uri);
+static GList            *xdg_cache_cache_get_flavors        (TumblerCache           *cache);
+static const gchar      *xdg_cache_cache_get_home           (void);
 
 
 
@@ -73,12 +71,8 @@ struct _XDGCacheCacheClass
 struct _XDGCacheCache
 {
   GObject __parent__;
-};
 
-struct _FlavorInfo
-{
-  TumblerThumbnailFlavor flavor;
-  const gchar           *dirname;
+  GList  *flavors;
 };
 
 
@@ -92,15 +86,6 @@ G_DEFINE_DYNAMIC_TYPE_EXTENDED (XDGCacheCache,
 
 
 
-static const FlavorInfo flavor_infos[] = 
-{
-  { TUMBLER_THUMBNAIL_FLAVOR_NORMAL,  "normal"  },
-  { TUMBLER_THUMBNAIL_FLAVOR_LARGE,   "large"   },
-  { TUMBLER_THUMBNAIL_FLAVOR_CROPPED, "cropped" },
-};
-
-
-
 void
 xdg_cache_cache_register (TumblerCachePlugin *plugin)
 {
@@ -112,6 +97,10 @@ xdg_cache_cache_register (TumblerCachePlugin *plugin)
 static void
 xdg_cache_cache_class_init (XDGCacheCacheClass *klass)
 {
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = xdg_cache_cache_finalize;
 }
 
 
@@ -126,12 +115,13 @@ xdg_cache_cache_class_finalize (XDGCacheCacheClass *klass)
 static void
 xdg_cache_cache_iface_init (TumblerCacheIface *iface)
 {
-  iface->get_thumbnails = xdg_cache_cache_get_thumbnails;
+  iface->get_thumbnail = xdg_cache_cache_get_thumbnail;
   iface->cleanup = xdg_cache_cache_cleanup;
   iface->delete = xdg_cache_cache_delete;
   iface->copy = xdg_cache_cache_copy;
   iface->move = xdg_cache_cache_move;
   iface->is_thumbnail = xdg_cache_cache_is_thumbnail;
+  iface->get_flavors = xdg_cache_cache_get_flavors;
 }
 
 
@@ -139,33 +129,41 @@ xdg_cache_cache_iface_init (TumblerCacheIface *iface)
 static void
 xdg_cache_cache_init (XDGCacheCache *cache)
 {
+  TumblerThumbnailFlavor *flavor;
+
+  flavor = tumbler_thumbnail_flavor_new_normal ();
+  cache->flavors = g_list_prepend (cache->flavors, flavor);
+
+  flavor = tumbler_thumbnail_flavor_new_large ();
+  cache->flavors = g_list_prepend (cache->flavors, flavor);
 }
 
 
 
-static GList *
-xdg_cache_cache_get_thumbnails (TumblerCache *cache,
-                                const gchar  *uri)
+static void
+xdg_cache_cache_finalize (GObject *object)
 {
-  TumblerThumbnailFlavor *flavors;
-  TumblerThumbnail       *thumbnail;
-  GList                  *thumbnails = NULL;
-  gint                    n;
+  XDGCacheCache *cache = XDG_CACHE_CACHE (object);
 
-  g_return_val_if_fail (XDG_CACHE_IS_CACHE (cache), NULL);
-  g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+  g_list_foreach (cache->flavors, (GFunc) g_object_unref, NULL);
+  g_list_free (cache->flavors);
+}
 
-  flavors = tumbler_thumbnail_get_flavors ();
 
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
-    {
-      thumbnail = g_object_new (XDG_CACHE_TYPE_THUMBNAIL, "cache", cache,
-                                "uri", uri, "flavor", flavors[n], NULL);
 
-      thumbnails = g_list_append (thumbnails, thumbnail);
-    }
+static TumblerThumbnail *
+xdg_cache_cache_get_thumbnail (TumblerCache           *cache,
+                               const gchar            *uri,
+                               TumblerThumbnailFlavor *flavor)
+{
+  g_return_val_if_fail (XDG_CACHE_IS_CACHE (cache), NULL);
+  g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL);
+
+  /* TODO check if the flavor is supported */
 
-  return thumbnails;
+  return g_object_new (XDG_CACHE_TYPE_THUMBNAIL, "cache", cache,
+                       "uri", uri, "flavor", flavor, NULL);
 }
 
 
@@ -175,24 +173,23 @@ xdg_cache_cache_cleanup (TumblerCache *cache,
                          const gchar  *uri_prefix,
                          guint64       since)
 {
-  TumblerThumbnailFlavor *flavors;
-  const gchar            *file_basename;
-  guint64                 mtime;
-  GFile                  *dummy_file;
-  GFile                  *parent;
-  gchar                  *dirname;
-  gchar                  *filename;
-  gchar                  *uri;
-  GDir                   *dir;
-  gint                    n;
+  XDGCacheCache *xdg_cache = XDG_CACHE_CACHE (cache);
+  const gchar   *file_basename;
+  guint64        mtime;
+  GFile         *dummy_file;
+  GFile         *parent;
+  GList         *iter;
+  gchar         *dirname;
+  gchar         *filename;
+  gchar         *uri;
+  GDir          *dir;
+  gint           n;
 
   g_return_if_fail (XDG_CACHE_IS_CACHE (cache));
   
-  flavors = tumbler_thumbnail_get_flavors ();
-
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
+  for (iter = xdg_cache->flavors; iter != NULL; iter = iter->next)
     {
-      dummy_file = xdg_cache_cache_get_file ("foo", flavors[n]);
+      dummy_file = xdg_cache_cache_get_file ("foo", iter->data);
       parent = g_file_get_parent (dummy_file);
       dirname = g_file_get_path (parent);
       g_object_unref (parent);
@@ -232,21 +229,19 @@ static void
 xdg_cache_cache_delete (TumblerCache *cache,
                         const GStrv   uris)
 {
-  TumblerThumbnailFlavor *flavors;
-  GFile                  *file;
-  gint                    n;
-  gint                    i;
+  XDGCacheCache *xdg_cache = XDG_CACHE_CACHE (cache);
+  GList         *iter;
+  GFile         *file;
+  gint           n;
 
   g_return_if_fail (XDG_CACHE_IS_CACHE (cache));
   g_return_if_fail (uris != NULL);
 
-  flavors = tumbler_thumbnail_get_flavors ();
-
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
+  for (iter = xdg_cache->flavors; iter != NULL; iter = iter->next)
     {
-      for (i = 0; uris[i] != NULL; ++i)
+      for (n = 0; uris[n] != NULL; ++n)
         {
-          file = xdg_cache_cache_get_file (uris[i], flavors[n]);
+          file = xdg_cache_cache_get_file (uris[n], iter->data);
           g_file_delete (file, NULL, NULL);
           g_object_unref (file);
         }
@@ -260,30 +255,28 @@ xdg_cache_cache_copy (TumblerCache *cache,
                       const GStrv   from_uris,
                       const GStrv   to_uris)
 {
-  TumblerThumbnailFlavor *flavors;
-  GFileInfo              *info;
-  guint64                 mtime;
-  GFile                  *dest_file;
-  GFile                  *dest_source_file;
-  GFile                  *from_file;
-  GFile                  *temp_file;
-  gchar                  *temp_path;
-  gchar                  *dest_path;
-  guint                   i;
-  guint                   n;
+  XDGCacheCache *xdg_cache = XDG_CACHE_CACHE (cache);
+  GFileInfo     *info;
+  guint64        mtime;
+  GFile         *dest_file;
+  GFile         *dest_source_file;
+  GFile         *from_file;
+  GFile         *temp_file;
+  GList         *iter;
+  gchar         *temp_path;
+  gchar         *dest_path;
+  guint          n;
 
   g_return_if_fail (XDG_CACHE_IS_CACHE (cache));
   g_return_if_fail (from_uris != NULL);
   g_return_if_fail (to_uris != NULL);
   g_return_if_fail (g_strv_length (from_uris) == g_strv_length (to_uris));
 
-  flavors = tumbler_thumbnail_get_flavors ();
-
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
+  for (iter = xdg_cache->flavors; iter != NULL; iter = iter->next)
     {
-      for (i = 0; i < g_strv_length (from_uris); ++i)
+      for (n = 0; n < g_strv_length (from_uris); ++n)
         {
-          dest_source_file = g_file_new_for_uri (to_uris[i]);
+          dest_source_file = g_file_new_for_uri (to_uris[n]);
           info = g_file_query_info (dest_source_file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
                                     G_FILE_QUERY_INFO_NONE, NULL, NULL);
           g_object_unref (dest_source_file);
@@ -295,18 +288,18 @@ xdg_cache_cache_copy (TumblerCache *cache,
                                                     G_FILE_ATTRIBUTE_TIME_MODIFIED);
           g_object_unref (info);
 
-          from_file = xdg_cache_cache_get_file (from_uris[i], flavors[n]);
-          temp_file = xdg_cache_cache_get_temp_file (to_uris[i], flavors[n]);
+          from_file = xdg_cache_cache_get_file (from_uris[n], iter->data);
+          temp_file = xdg_cache_cache_get_temp_file (to_uris[n], iter->data);
 
           if (g_file_copy (from_file, temp_file, G_FILE_COPY_OVERWRITE, 
                            NULL, NULL, NULL, NULL))
             {
               temp_path = g_file_get_path (temp_file);
 
-              if (xdg_cache_cache_write_thumbnail_info (temp_path, to_uris[i], mtime,
+              if (xdg_cache_cache_write_thumbnail_info (temp_path, to_uris[n], mtime,
                                                         NULL, NULL))
                 {
-                  dest_file = xdg_cache_cache_get_file (to_uris[i], flavors[n]);
+                  dest_file = xdg_cache_cache_get_file (to_uris[n], iter->data);
                   dest_path = g_file_get_path (dest_file);
 
                   if (g_rename (temp_path, dest_path) != 0)
@@ -337,31 +330,29 @@ xdg_cache_cache_move (TumblerCache *cache,
                       const GStrv   from_uris,
                       const GStrv   to_uris)
 {
-  TumblerThumbnailFlavor *flavors;
-  GFileInfo              *info;
-  guint64                 mtime;
-  GFile                  *dest_file;
-  GFile                  *dest_source_file;
-  GFile                  *from_file;
-  GFile                  *temp_file;
-  gchar                  *from_path;
-  gchar                  *temp_path;
-  gchar                  *dest_path;
-  guint                   i;
-  guint                   n;
+  XDGCacheCache *xdg_cache = XDG_CACHE_CACHE (cache);
+  GFileInfo     *info;
+  guint64        mtime;
+  GFile         *dest_file;
+  GFile         *dest_source_file;
+  GFile         *from_file;
+  GFile         *temp_file;
+  GList         *iter;
+  gchar         *from_path;
+  gchar         *temp_path;
+  gchar         *dest_path;
+  guint          n;
 
   g_return_if_fail (XDG_CACHE_IS_CACHE (cache));
   g_return_if_fail (from_uris != NULL);
   g_return_if_fail (to_uris != NULL);
   g_return_if_fail (g_strv_length (from_uris) == g_strv_length (to_uris));
 
-  flavors = tumbler_thumbnail_get_flavors ();
-
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
+  for (iter = xdg_cache->flavors; iter != NULL; iter = iter->next)
     {
-      for (i = 0; i < g_strv_length (from_uris); ++i)
+      for (n = 0; n < g_strv_length (from_uris); ++n)
         {
-          dest_source_file = g_file_new_for_uri (to_uris[i]);
+          dest_source_file = g_file_new_for_uri (to_uris[n]);
           info = g_file_query_info (dest_source_file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
                                     G_FILE_QUERY_INFO_NONE, NULL, NULL);
           g_object_unref (dest_source_file);
@@ -373,18 +364,18 @@ xdg_cache_cache_move (TumblerCache *cache,
                                                     G_FILE_ATTRIBUTE_TIME_MODIFIED);
           g_object_unref (info);
 
-          from_file = xdg_cache_cache_get_file (from_uris[i], flavors[n]);
-          temp_file = xdg_cache_cache_get_temp_file (to_uris[i], flavors[n]);
+          from_file = xdg_cache_cache_get_file (from_uris[n], iter->data);
+          temp_file = xdg_cache_cache_get_temp_file (to_uris[n], iter->data);
 
           if (g_file_move (from_file, temp_file, G_FILE_COPY_OVERWRITE, 
                            NULL, NULL, NULL, NULL))
             {
               temp_path = g_file_get_path (temp_file);
 
-              if (xdg_cache_cache_write_thumbnail_info (temp_path, to_uris[i], mtime,
+              if (xdg_cache_cache_write_thumbnail_info (temp_path, to_uris[n], mtime,
                                                         NULL, NULL))
                 {
-                  dest_file = xdg_cache_cache_get_file (to_uris[i], flavors[n]);
+                  dest_file = xdg_cache_cache_get_file (to_uris[n], iter->data);
                   dest_path = g_file_get_path (dest_file);
 
                   if (g_rename (temp_path, dest_path) != 0)
@@ -417,24 +408,22 @@ static gboolean
 xdg_cache_cache_is_thumbnail (TumblerCache *cache,
                               const gchar  *uri)
 {
-  TumblerThumbnailFlavor *flavors;
-  const gchar            *home;
-  const gchar            *dirname;
-  gboolean                is_thumbnail = FALSE;
-  GFile                  *flavor_dir;
-  GFile                  *file;
-  gchar                  *path;
-  guint                   n;
+  XDGCacheCache *xdg_cache = XDG_CACHE_CACHE (cache);
+  const gchar   *home;
+  const gchar   *dirname;
+  gboolean       is_thumbnail = FALSE;
+  GList         *iter;
+  GFile         *flavor_dir;
+  GFile         *file;
+  gchar         *path;
 
   g_return_val_if_fail (XDG_CACHE_IS_CACHE (cache), FALSE);
   g_return_val_if_fail (uri != NULL, FALSE);
 
-  flavors = tumbler_thumbnail_get_flavors ();
-
-  for (n = 0; !is_thumbnail && flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
+  for (iter = xdg_cache->flavors; !is_thumbnail && iter != NULL; iter = iter->next)
     {
       home = xdg_cache_cache_get_home ();
-      dirname = xdg_cache_cache_get_flavor_dirname (flavors[n]);
+      dirname = tumbler_thumbnail_flavor_get_name (iter->data);
       path = g_build_filename (home, ".thumbnails", dirname, NULL);
 
       flavor_dir = g_file_new_for_path (path);
@@ -454,18 +443,19 @@ xdg_cache_cache_is_thumbnail (TumblerCache *cache,
 
 
 
-static const gchar *
-xdg_cache_cache_get_flavor_dirname (TumblerThumbnailFlavor flavor)
+static GList *
+xdg_cache_cache_get_flavors (TumblerCache *cache)
 {
-  guint n;
+  XDGCacheCache *xdg_cache = XDG_CACHE_CACHE (cache);
+  GList         *flavors = NULL;
+  GList         *iter;
 
-  for (n = 0; n < G_N_ELEMENTS (flavor_infos); ++n)
-    if (flavor_infos[n].flavor == flavor)
-      return flavor_infos[n].dirname;
+  g_return_val_if_fail (XDG_CACHE_IS_CACHE (cache), NULL);
 
-  g_assert_not_reached ();
+  for (iter = g_list_last (xdg_cache->flavors); iter != NULL; iter = iter->prev)
+    flavors = g_list_prepend (flavors, g_object_ref (iter->data));
 
-  return NULL;
+  return flavors;
 }
 
 
@@ -479,8 +469,8 @@ xdg_cache_cache_get_home (void)
 
 
 GFile *
-xdg_cache_cache_get_file (const gchar           *uri,
-                          TumblerThumbnailFlavor flavor)
+xdg_cache_cache_get_file (const gchar            *uri,
+                          TumblerThumbnailFlavor *flavor)
 {
   const gchar *home;
   const gchar *dirname;
@@ -490,10 +480,10 @@ xdg_cache_cache_get_file (const gchar           *uri,
   gchar       *path;
 
   g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
-  g_return_val_if_fail (flavor != TUMBLER_THUMBNAIL_FLAVOR_INVALID, NULL);
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL);
 
   home = xdg_cache_cache_get_home ();
-  dirname = xdg_cache_cache_get_flavor_dirname (flavor);
+  dirname = tumbler_thumbnail_flavor_get_name (flavor);
 
   md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
   filename = g_strdup_printf ("%s.png", md5_hash);
@@ -511,8 +501,8 @@ xdg_cache_cache_get_file (const gchar           *uri,
 
 
 GFile *
-xdg_cache_cache_get_temp_file (const gchar           *uri,
-                               TumblerThumbnailFlavor flavor)
+xdg_cache_cache_get_temp_file (const gchar            *uri,
+                               TumblerThumbnailFlavor *flavor)
 {
   const gchar *home;
   const gchar *dirname;
@@ -523,10 +513,10 @@ xdg_cache_cache_get_temp_file (const gchar           *uri,
   gchar       *path;
 
   g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
-  g_return_val_if_fail (flavor != TUMBLER_THUMBNAIL_FLAVOR_INVALID, NULL);
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL);
 
   home = xdg_cache_cache_get_home ();
-  dirname = xdg_cache_cache_get_flavor_dirname (flavor);
+  dirname = tumbler_thumbnail_flavor_get_name (flavor);
 
   g_get_current_time (&current_time);
 
diff --git a/plugins/xdg-cache/xdg-cache-cache.h b/plugins/xdg-cache/xdg-cache-cache.h
index 2e32641..f1c114c 100644
--- a/plugins/xdg-cache/xdg-cache-cache.h
+++ b/plugins/xdg-cache/xdg-cache-cache.h
@@ -38,22 +38,22 @@ typedef struct _XDGCacheCacheClass XDGCacheCacheClass;
 typedef struct _XDGCacheCache      XDGCacheCache;
 
 GType    xdg_cache_cache_get_type             (void) G_GNUC_CONST;
-void     xdg_cache_cache_register             (TumblerCachePlugin    *plugin);
-
-GFile   *xdg_cache_cache_get_file             (const gchar           *uri,
-                                               TumblerThumbnailFlavor flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-GFile   *xdg_cache_cache_get_temp_file        (const gchar           *uri,
-                                               TumblerThumbnailFlavor flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-gboolean xdg_cache_cache_read_thumbnail_info  (const gchar           *filename,
-                                               gchar                **uri,
-                                               guint64               *mtime,
-                                               GCancellable          *cancellable,
-                                               GError               **error);
-gboolean xdg_cache_cache_write_thumbnail_info (const gchar           *filename,
-                                               gchar                 *uri,
-                                               guint64                mtime,
-                                               GCancellable          *cancellable,
-                                               GError               **error);
+void     xdg_cache_cache_register             (TumblerCachePlugin     *plugin);
+
+GFile   *xdg_cache_cache_get_file             (const gchar            *uri,
+                                               TumblerThumbnailFlavor *flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+GFile   *xdg_cache_cache_get_temp_file        (const gchar            *uri,
+                                               TumblerThumbnailFlavor  *flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+gboolean xdg_cache_cache_read_thumbnail_info  (const gchar            *filename,
+                                               gchar                 **uri,
+                                               guint64                *mtime,
+                                               GCancellable           *cancellable,
+                                               GError                **error);
+gboolean xdg_cache_cache_write_thumbnail_info (const gchar            *filename,
+                                               gchar                  *uri,
+                                               guint64                 mtime,
+                                               GCancellable           *cancellable,
+                                               GError                **error);
 
 G_END_DECLS;
 
diff --git a/plugins/xdg-cache/xdg-cache-thumbnail.c b/plugins/xdg-cache/xdg-cache-thumbnail.c
index d07a8ce..7da3e89 100644
--- a/plugins/xdg-cache/xdg-cache-thumbnail.c
+++ b/plugins/xdg-cache/xdg-cache-thumbnail.c
@@ -83,11 +83,11 @@ struct _XDGCacheThumbnail
 {
   GObject __parent__;
 
-  TumblerThumbnailFlavor flavor;
-  XDGCacheCache         *cache;
-  gchar                 *uri;
-  gchar                 *cached_uri;
-  guint64                cached_mtime;
+  TumblerThumbnailFlavor *flavor;
+  XDGCacheCache          *cache;
+  gchar                  *uri;
+  gchar                  *cached_uri;
+  guint64                 cached_mtime;
 };
 
 
@@ -182,7 +182,7 @@ xdg_cache_thumbnail_get_property (GObject    *object,
       g_value_set_string (value, thumbnail->uri);
       break;
     case PROP_FLAVOR:
-      g_value_set_enum (value, thumbnail->flavor);
+      g_value_set_object (value, thumbnail->flavor);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -209,7 +209,7 @@ xdg_cache_thumbnail_set_property (GObject      *object,
       thumbnail->uri = g_value_dup_string (value);
       break;
     case PROP_FLAVOR:
-      thumbnail->flavor = g_value_get_enum (value);
+      thumbnail->flavor = g_value_dup_object (value);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
diff --git a/tumbler/Makefile.am b/tumbler/Makefile.am
index 12f8d0b..b8ccf7f 100644
--- a/tumbler/Makefile.am
+++ b/tumbler/Makefile.am
@@ -42,6 +42,7 @@ libtumbler_headers =							\
 	tumbler-thumbnailer-provider.h					\
 	tumbler-thumbnailer.h						\
 	tumbler-thumbnail.h						\
+	tumbler-thumbnail-flavor.h					\
 	tumbler-util.h							\
 	tumbler.h
 
@@ -58,6 +59,7 @@ libtumbler_sources =							\
 	tumbler-thumbnailer-provider.c					\
 	tumbler-thumbnailer.c						\
 	tumbler-thumbnail.c						\
+	tumbler-thumbnail-flavor.c					\
 	tumbler-util.c
 
 libtumblerincludedir = $(includedir)/tumbler-$(TUMBLER_VERSION_API)/tumbler
diff --git a/tumbler/tumbler-abstract-thumbnailer.c b/tumbler/tumbler-abstract-thumbnailer.c
index 3497393..31a8f79 100644
--- a/tumbler/tumbler-abstract-thumbnailer.c
+++ b/tumbler/tumbler-abstract-thumbnailer.c
@@ -26,6 +26,7 @@
 #include <glib-object.h>
 
 #include <tumbler/tumbler-abstract-thumbnailer.h>
+#include <tumbler/tumbler-file-info.h>
 #include <tumbler/tumbler-thumbnailer.h>
 
 
@@ -58,9 +59,7 @@ static void tumbler_abstract_thumbnailer_set_property     (GObject
                                                            GParamSpec              *pspec);
 static void tumbler_abstract_thumbnailer_create           (TumblerThumbnailer      *thumbnailer,
                                                            GCancellable            *cancellable,
-                                                           const gchar             *uri,
-                                                           const gchar             *mime_hint,
-                                                           const gchar             *flavor);
+                                                           TumblerFileInfo         *info);
 
 
 
@@ -236,17 +235,12 @@ tumbler_abstract_thumbnailer_set_property (GObject      *object,
 static void
 tumbler_abstract_thumbnailer_create (TumblerThumbnailer *thumbnailer,
                                      GCancellable       *cancellable,
-                                     const gchar        *uri,
-                                     const gchar        *mime_hint,
-                                     const gchar        *flavor)
+                                     TumblerFileInfo    *info)
 {
   g_return_if_fail (TUMBLER_IS_ABSTRACT_THUMBNAILER (thumbnailer));
-  g_return_if_fail (uri != NULL && *uri != '\0');
-  g_return_if_fail (flavor != NULL && *flavor != '\0');
+  g_return_if_fail (TUMBLER_IS_FILE_INFO (info));
   g_return_if_fail (TUMBLER_ABSTRACT_THUMBNAILER_GET_CLASS (thumbnailer)->create != NULL);
 
   TUMBLER_ABSTRACT_THUMBNAILER_GET_CLASS (thumbnailer)->create (TUMBLER_ABSTRACT_THUMBNAILER (thumbnailer),
-                                                                cancellable, uri, 
-                                                                mime_hint,
-                                                                flavor);
+                                                                cancellable, info);
 }
diff --git a/tumbler/tumbler-abstract-thumbnailer.h b/tumbler/tumbler-abstract-thumbnailer.h
index d82a154..f4435c4 100644
--- a/tumbler/tumbler-abstract-thumbnailer.h
+++ b/tumbler/tumbler-abstract-thumbnailer.h
@@ -28,6 +28,8 @@
 #include <glib-object.h>
 #include <gio/gio.h>
 
+#include <tumbler/tumbler-file-info.h>
+
 G_BEGIN_DECLS;
 
 #define TUMBLER_TYPE_ABSTRACT_THUMBNAILER            (tumbler_abstract_thumbnailer_get_type ())
@@ -48,9 +50,7 @@ struct _TumblerAbstractThumbnailerClass
   /* virtual methods */
   void (*create) (TumblerAbstractThumbnailer *thumbnailer,
                   GCancellable               *cancellable,
-                  const gchar                *uri,
-                  const gchar                *mime_hint,
-                  const gchar                *flavor);
+                  TumblerFileInfo            *info);
 };
 
 struct _TumblerAbstractThumbnailer
diff --git a/tumbler/tumbler-cache-plugin.c b/tumbler/tumbler-cache-plugin.c
new file mode 100644
index 0000000..4a92b1c
--- /dev/null
+++ b/tumbler/tumbler-cache-plugin.c
@@ -0,0 +1,219 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009 Jannis Pohlmann <jannis at 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gmodule.h>
+
+#include <tumbler/tumbler-cache.h>
+#include <tumbler/tumbler-cache-plugin.h>
+
+
+
+static void     tumbler_cache_plugin_constructed (GObject           *object);
+static void     tumbler_cache_plugin_dispose     (GObject           *object);
+static void     tumbler_cache_plugin_finalize    (GObject           *object);
+static gboolean tumbler_cache_plugin_load        (GTypeModule       *type_module);
+static void     tumbler_cache_plugin_unload      (GTypeModule       *type_module);
+
+
+
+struct _TumblerCachePluginClass
+{
+  GTypeModuleClass __parent__;
+};
+
+struct _TumblerCachePlugin
+{
+  GTypeModule __parent__;
+
+  GModule      *library;
+
+  void          (*initialize) (TumblerCachePlugin *plugin);
+  void          (*shutdown)   (void);
+  TumblerCache *(*get_cache)  (void);
+};
+
+
+
+G_DEFINE_TYPE (TumblerCachePlugin, tumbler_cache_plugin, G_TYPE_TYPE_MODULE);
+
+
+
+static void
+tumbler_cache_plugin_class_init (TumblerCachePluginClass *klass)
+{
+  GTypeModuleClass *gtype_module_class;
+  GObjectClass     *gobject_class;
+
+  /* Determine the parent type class */
+  tumbler_cache_plugin_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->constructed = tumbler_cache_plugin_constructed; 
+  gobject_class->dispose = tumbler_cache_plugin_dispose; 
+  gobject_class->finalize = tumbler_cache_plugin_finalize; 
+
+  gtype_module_class = G_TYPE_MODULE_CLASS (klass);
+  gtype_module_class->load = tumbler_cache_plugin_load;
+  gtype_module_class->unload = tumbler_cache_plugin_unload;
+}
+
+
+
+static void
+tumbler_cache_plugin_cache_init (TumblerCacheIface *iface)
+{
+}
+
+
+
+static void
+tumbler_cache_plugin_init (TumblerCachePlugin *plugin)
+{
+}
+
+
+
+static void
+tumbler_cache_plugin_constructed (GObject *object)
+{
+  TumblerCachePlugin *plugin = TUMBLER_CACHE_PLUGIN (object);
+}
+
+
+
+static void
+tumbler_cache_plugin_dispose (GObject *object)
+{
+  TumblerCachePlugin *plugin = TUMBLER_CACHE_PLUGIN (object);
+
+  (*G_OBJECT_CLASS (tumbler_cache_plugin_parent_class)->dispose) (object);
+}
+
+
+
+static void
+tumbler_cache_plugin_finalize (GObject *object)
+{
+  TumblerCachePlugin *plugin = TUMBLER_CACHE_PLUGIN (object);
+
+  (*G_OBJECT_CLASS (tumbler_cache_plugin_parent_class)->finalize) (object);
+}
+
+
+
+static gboolean
+tumbler_cache_plugin_load (GTypeModule *type_module)
+{
+  TumblerCachePlugin *plugin = TUMBLER_CACHE_PLUGIN (type_module);
+  gchar              *path;
+
+  /* load the module using the runtime link eeditor */
+  path = g_build_filename (TUMBLER_PLUGIN_DIRECTORY, G_DIR_SEPARATOR_S, 
+                           "cache", G_DIR_SEPARATOR_S, type_module->name, NULL);
+  plugin->library = g_module_open (path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+  g_free (path);
+
+  /* check if the load operation was successful */
+  if (G_LIKELY (plugin->library != NULL))
+    {
+      /* verify that all required public symbols are present in the plugin */
+      if (g_module_symbol (plugin->library, "tumbler_plugin_initialize", 
+                            (gpointer) &plugin->initialize)
+          && g_module_symbol (plugin->library, "tumbler_plugin_shutdown",
+                              (gpointer) &plugin->shutdown)
+          && g_module_symbol (plugin->library, "tumbler_plugin_get_cache",
+                              (gpointer) &plugin->get_cache))
+        {
+          /* initialize the plugin */
+          (*plugin->initialize) (plugin);
+          return TRUE;
+        }
+      else
+        {
+          g_warning (_("Cache plugin \"%s\" lacks required symbols"), type_module->name);
+          g_module_close (plugin->library);
+          plugin->library = NULL;
+          return FALSE;
+        }
+    }
+  else
+    {
+      g_warning (_("Failed to load the cache plugin \"%s\": %s"), type_module->name,
+                 g_module_error ());
+      return FALSE;
+    }
+}
+
+
+
+static void
+tumbler_cache_plugin_unload (GTypeModule *type_module)
+{
+  TumblerCachePlugin *plugin = TUMBLER_CACHE_PLUGIN (type_module);
+
+  /* shutdown the plugin */
+  (*plugin->shutdown) ();
+
+  /* unload the plugin from memory */
+  g_module_close (plugin->library);
+  plugin->library = NULL;
+
+  /* reset plugin state */
+  plugin->initialize = NULL;
+  plugin->shutdown = NULL;
+  plugin->get_cache = NULL;
+}
+
+
+
+GTypeModule *
+tumbler_cache_plugin_get_default (void)
+{
+  static TumblerCachePlugin *plugin = NULL;
+
+  if (plugin == NULL)
+    {
+      plugin = g_object_new (TUMBLER_TYPE_CACHE_PLUGIN, NULL);
+      g_type_module_set_name (G_TYPE_MODULE (plugin), 
+                              "tumbler-cache-plugin." G_MODULE_SUFFIX);
+      g_object_add_weak_pointer (G_OBJECT (plugin), (gpointer) &plugin);
+
+      if (!g_type_module_use (G_TYPE_MODULE (plugin)))
+        return NULL;
+    }
+
+  return G_TYPE_MODULE (plugin);
+}
+
+
+
+TumblerCache *
+tumbler_cache_plugin_get_cache (TumblerCachePlugin *plugin)
+{
+  g_return_val_if_fail (TUMBLER_IS_CACHE_PLUGIN (plugin), NULL);
+  return (*plugin->get_cache) ();
+}
diff --git a/tumbler/tumbler-cache-plugin.h b/tumbler/tumbler-cache-plugin.h
new file mode 100644
index 0000000..13a9dc1
--- /dev/null
+++ b/tumbler/tumbler-cache-plugin.h
@@ -0,0 +1,46 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009 Jannis Pohlmann <jannis at 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.
+ */
+
+#ifndef __TUMBLER_CACHE_PLUGIN_H__
+#define __TUMBLER_CACHE_PLUGIN_H__
+
+#include <tumbler/tumbler-cache.h>
+
+G_BEGIN_DECLS
+
+#define TUMBLER_TYPE_CACHE_PLUGIN            (tumbler_cache_plugin_get_type ())
+#define TUMBLER_CACHE_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TUMBLER_TYPE_CACHE_PLUGIN, TumblerCachePlugin))
+#define TUMBLER_CACHE_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TUMBLER_TYPE_CACHE_PLUGIN, TumblerCachePluginClass))
+#define TUMBLER_IS_CACHE_PLUGIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TUMBLER_TYPE_CACHE_PLUGIN))
+#define TUMBLER_IS_CACHE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TUMBLER_TYPE_CACHE_PLUGIN)
+#define TUMBLER_CACHE_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TUMBLER_TYPE_CACHE_PLUGIN, TumblerCachePluginClass))
+
+typedef struct _TumblerCachePluginPrivate TumblerCachePluginPrivate;
+typedef struct _TumblerCachePluginClass   TumblerCachePluginClass;
+typedef struct _TumblerCachePlugin        TumblerCachePlugin;
+
+GType         tumbler_cache_plugin_get_type    (void) G_GNUC_CONST;
+
+GTypeModule  *tumbler_cache_plugin_get_default (void);
+TumblerCache *tumbler_cache_plugin_get_cache   (TumblerCachePlugin *plugin);
+
+G_END_DECLS
+
+#endif /* !__TUMBLER_CACHE_PLUGIN_H__ */
diff --git a/tumbler/tumbler-cache-provider.c b/tumbler/tumbler-cache-provider.c
deleted file mode 100644
index f7949ab..0000000
--- a/tumbler/tumbler-cache-provider.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* vi:set et ai sw=2 sts=2 ts=2: */
-/*-
- * Copyright (c) 2009 Jannis Pohlmann <jannis at 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-object.h>
-
-#include <tumbler/tumbler-cache-provider.h>
-
-
-
-GType
-tumbler_cache_provider_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-  
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      GType g_define_type_id =
-        g_type_register_static_simple (G_TYPE_INTERFACE,
-                                       "TumblerCacheProvider",
-                                       sizeof (TumblerCacheProviderIface),
-                                       NULL,
-                                       0,
-                                       NULL,
-                                       0);
-
-      g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
-
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-
-
-
-GList *
-tumbler_cache_provider_get_caches (TumblerCacheProvider *provider)
-{
-  g_return_val_if_fail (TUMBLER_IS_CACHE_PROVIDER (provider), NULL);
-  g_return_val_if_fail (TUMBLER_CACHE_PROVIDER_GET_IFACE (provider)->get_caches != NULL, NULL);
-
-  return (TUMBLER_CACHE_PROVIDER_GET_IFACE (provider)->get_caches) (provider);
-}
diff --git a/tumbler/tumbler-cache-provider.h b/tumbler/tumbler-cache-provider.h
deleted file mode 100644
index efd65d4..0000000
--- a/tumbler/tumbler-cache-provider.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* vi:set et ai sw=2 sts=2 ts=2: */
-/*-
- * Copyright (c) 2009 Jannis Pohlmann <jannis at 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.
- */
-
-#if !defined (TUMBLER_INSIDE_TUMBLER_H) && !defined (TUMBLER_COMPILATION)
-#error "Only <tumbler/tumbler.h> may be included directly. This file might disappear or change contents."
-#endif
-
-#ifndef __TUMBLER_CACHE_PROVIDER_H__
-#define __TUMBLER_CACHE_PROVIDER_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define TUMBLER_TYPE_CACHE_PROVIDER           (tumbler_cache_provider_get_type ())
-#define TUMBLER_CACHE_PROVIDER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), TUMBLER_TYPE_CACHE_PROVIDER, TumblerCacheProvider))
-#define TUMBLER_IS_CACHE_PROVIDER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TUMBLER_TYPE_CACHE_PROVIDER))
-#define TUMBLER_CACHE_PROVIDER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TUMBLER_TYPE_CACHE_PROVIDER, TumblerCacheProviderIface))
-
-typedef struct _TumblerCacheProvider      TumblerCacheProvider;
-typedef struct _TumblerCacheProviderIface TumblerCacheProviderIface;
-
-struct _TumblerCacheProviderIface
-{
-  GTypeInterface __parent__;
-
-  /* signals */
-
-  /* virtual methods */
-  GList *(*get_caches) (TumblerCacheProvider *provider);
-};
-
-GType  tumbler_cache_provider_get_type   (void) G_GNUC_CONST;
-
-GList *tumbler_cache_provider_get_caches (TumblerCacheProvider *provider) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-
-G_END_DECLS
-
-#endif /* !__TUMBLER_CACHE_PROVIDER_H__ */
diff --git a/tumbler/tumbler-cache.c b/tumbler/tumbler-cache.c
index cdb5156..76dbf43 100644
--- a/tumbler/tumbler-cache.c
+++ b/tumbler/tumbler-cache.c
@@ -26,6 +26,8 @@
 
 #include <tumbler/tumbler-cache.h>
 #include <tumbler/tumbler-cache-plugin.h>
+#include <tumbler/tumbler-thumbnail.h>
+#include <tumbler/tumbler-thumbnail-flavor.h>
 
 
 
@@ -82,15 +84,17 @@ tumbler_cache_get_default (void)
 
 
 
-GList *
-tumbler_cache_get_thumbnails (TumblerCache *cache,
-                              const gchar  *uri)
+TumblerThumbnail *
+tumbler_cache_get_thumbnail (TumblerCache           *cache,
+                             const gchar            *uri,
+                             TumblerThumbnailFlavor *flavor)
 {
   g_return_val_if_fail (TUMBLER_IS_CACHE (cache), NULL);
   g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
-  g_return_val_if_fail (TUMBLER_CACHE_GET_IFACE (cache)->get_thumbnails != NULL, NULL);
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL);
+  g_return_val_if_fail (TUMBLER_CACHE_GET_IFACE (cache)->get_thumbnail != NULL, NULL);
 
-  return (TUMBLER_CACHE_GET_IFACE (cache)->get_thumbnails) (cache, uri);
+  return (TUMBLER_CACHE_GET_IFACE (cache)->get_thumbnail) (cache, uri, flavor);
 }
 
 
@@ -163,3 +167,39 @@ tumbler_cache_is_thumbnail (TumblerCache *cache,
 
   return (TUMBLER_CACHE_GET_IFACE (cache)->is_thumbnail) (cache, uri);
 }
+
+
+
+GList *
+tumbler_cache_get_flavors (TumblerCache *cache)
+{
+  g_return_val_if_fail (TUMBLER_IS_CACHE (cache), NULL);
+  g_return_val_if_fail (TUMBLER_CACHE_GET_IFACE (cache)->get_flavors != NULL, NULL);
+
+  return (TUMBLER_CACHE_GET_IFACE (cache)->get_flavors) (cache);
+}
+
+
+
+TumblerThumbnailFlavor *
+tumbler_cache_get_flavor (TumblerCache *cache,
+                          const gchar  *name)
+{
+  TumblerThumbnailFlavor *flavor = NULL;
+  GList                  *flavors;
+  GList                  *iter;
+
+  g_return_val_if_fail (TUMBLER_IS_CACHE (cache), NULL);
+  g_return_val_if_fail (name != NULL && *name != '\0', NULL);
+
+  flavors = tumbler_cache_get_flavors (cache);
+
+  for (iter = flavors; flavor == NULL && iter != NULL; iter = iter->next)
+    if (g_strcmp0 (tumbler_thumbnail_flavor_get_name (iter->data), name) == 0)
+      flavor = g_object_ref (iter->data);
+
+  g_list_foreach (flavors, (GFunc) g_object_unref, NULL);
+  g_list_free (flavors);
+
+  return flavor;
+}
diff --git a/tumbler/tumbler-cache.h b/tumbler/tumbler-cache.h
index 7e42539..49f18ff 100644
--- a/tumbler/tumbler-cache.h
+++ b/tumbler/tumbler-cache.h
@@ -25,7 +25,8 @@
 #ifndef __TUMBLER_CACHE_H__
 #define __TUMBLER_CACHE_H__
 
-#include <glib-object.h>
+#include <tumbler/tumbler-thumbnail.h>
+#include <tumbler/tumbler-thumbnail-flavor.h>
 
 G_BEGIN_DECLS
 
@@ -44,42 +45,48 @@ struct _TumblerCacheIface
   /* signals */
 
   /* virtual methods */
-  GList   *(*get_thumbnails) (TumblerCache *cache,
-                              const gchar  *uri);
-  void     (*cleanup)        (TumblerCache *cache,
-                              const gchar  *uri,
-                              guint64       since);
-  void     (*delete)         (TumblerCache *cache,
-                              const GStrv   uris);
-  void     (*copy)           (TumblerCache *cache,
-                              const GStrv   from_uris,
-                              const GStrv   to_uris);
-  void     (*move)           (TumblerCache *cache,
-                              const GStrv   from_uris,
-                              const GStrv   to_uris);
-  gboolean (*is_thumbnail)   (TumblerCache *cache,
-                              const gchar  *uri);
+  TumblerThumbnail *(*get_thumbnail) (TumblerCache           *cache,
+                                      const gchar            *uri,
+                                      TumblerThumbnailFlavor *flavor);
+  void              (*cleanup)       (TumblerCache           *cache,
+                                      const gchar            *uri,
+                                      guint64                 since);
+  void              (*delete)        (TumblerCache           *cache,
+                                      const GStrv             uris);
+  void              (*copy)          (TumblerCache           *cache,
+                                      const GStrv             from_uris,
+                                      const GStrv             to_uris);
+  void              (*move)          (TumblerCache           *cache,
+                                      const GStrv             from_uris,
+                                      const GStrv             to_uris);
+  gboolean          (*is_thumbnail)  (TumblerCache           *cache,
+                                      const gchar            *uri);
+  GList            *(*get_flavors)   (TumblerCache           *cache);
 };
 
-GType         tumbler_cache_get_type (void) G_GNUC_CONST;
+GType                   tumbler_cache_get_type (void) G_GNUC_CONST;
 
-TumblerCache *tumbler_cache_get_default    (void) G_GNUC_WARN_UNUSED_RESULT;
+TumblerCache           *tumbler_cache_get_default    (void) G_GNUC_WARN_UNUSED_RESULT;
 
-GList        *tumbler_cache_get_thumbnails (TumblerCache *cache,
-                                            const gchar  *uri) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-void          tumbler_cache_cleanup        (TumblerCache *cache,
-                                            const gchar  *uri_prefix,
-                                            guint64       since);
-void          tumbler_cache_delete         (TumblerCache *cache,
-                                            const GStrv   uris);
-void          tumbler_cache_copy           (TumblerCache *cache,
-                                            const GStrv   from_uris,
-                                            const GStrv   to_uris);
-void          tumbler_cache_move           (TumblerCache *cache,
-                                            const GStrv   from_uris,
-                                            const GStrv   to_uris);
-gboolean      tumbler_cache_is_thumbnail   (TumblerCache *cache,
-                                            const gchar  *uri);
+TumblerThumbnail       *tumbler_cache_get_thumbnail  (TumblerCache           *cache,
+                                                      const gchar            *uri,
+                                                      TumblerThumbnailFlavor *flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+void                    tumbler_cache_cleanup        (TumblerCache           *cache,
+                                                      const gchar            *uri_prefix,
+                                                      guint64                 since);
+void                    tumbler_cache_delete         (TumblerCache           *cache,
+                                                      const GStrv             uris);
+void                    tumbler_cache_copy           (TumblerCache           *cache,
+                                                      const GStrv             from_uris,
+                                                      const GStrv             to_uris);
+void                    tumbler_cache_move           (TumblerCache           *cache,
+                                                      const GStrv             from_uris,
+                                                      const GStrv             to_uris);
+gboolean                tumbler_cache_is_thumbnail   (TumblerCache           *cache,
+                                                      const gchar            *uri);
+GList                  *tumbler_cache_get_flavors    (TumblerCache           *cache) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+TumblerThumbnailFlavor *tumbler_cache_get_flavor     (TumblerCache           *cache,
+                                                      const gchar            *name) G_GNUC_WARN_UNUSED_RESULT;
 
 G_END_DECLS
 
diff --git a/tumbler/tumbler-enum-types.c b/tumbler/tumbler-enum-types.c
index baba55f..1e2cdd3 100644
--- a/tumbler/tumbler-enum-types.c
+++ b/tumbler/tumbler-enum-types.c
@@ -38,30 +38,6 @@
 
 
 GType
-tumbler_thumbnail_flavor_get_type (void)
-{
-  GType type = G_TYPE_INVALID;
-
-  if (G_UNLIKELY (type == G_TYPE_INVALID))
-    {
-      static const GEnumValue values[] = 
-      {
-        { TUMBLER_THUMBNAIL_FLAVOR_INVALID, "TUMBLER_THUMBNAIL_FLAVOR_INVALID",  N_ ("Invalid format"), },
-        { TUMBLER_THUMBNAIL_FLAVOR_NORMAL,  "TUMBLER_THUMBNAIL_FLAVOR_NORMAL",   N_ ("Normal"),  },
-        { TUMBLER_THUMBNAIL_FLAVOR_LARGE,   "TUMBLER_THUMBNAIL_FLAVOR_LARGE",    N_ ("Large"),   },
-        { TUMBLER_THUMBNAIL_FLAVOR_CROPPED, "TUMBLER_THUMBNAIL_FLAVOR_CROPPED",  N_ ("Cropped"), },
-        { 0,                                NULL,                                NULL,           },
-      };
-
-      type = g_enum_register_static ("TumblerThumbnailFlavor", values);
-    }
-
-  return type;
-}
-
-
-
-GType
 tumbler_thumbnail_format_get_type (void)
 {
   GType type = G_TYPE_INVALID;
diff --git a/tumbler/tumbler-enum-types.h b/tumbler/tumbler-enum-types.h
index ea784c3..d40caa4 100644
--- a/tumbler/tumbler-enum-types.h
+++ b/tumbler/tumbler-enum-types.h
@@ -29,18 +29,6 @@
 
 G_BEGIN_DECLS
 
-#define TUMBLER_TYPE_THUMBNAIL_FLAVOR (tumbler_thumbnail_flavor_get_type ())
-
-typedef enum /*< enum >*/
-{
-  TUMBLER_THUMBNAIL_FLAVOR_INVALID,
-  TUMBLER_THUMBNAIL_FLAVOR_NORMAL,
-  TUMBLER_THUMBNAIL_FLAVOR_LARGE,
-  TUMBLER_THUMBNAIL_FLAVOR_CROPPED,
-} TumblerThumbnailFlavor;
-
-GType tumbler_thumbnail_flavor_get_type (void);
-
 #define TUMBLER_TYPE_THUMBNAIL_FORMAT (tumbler_thumbnail_format_get_type ())
 
 typedef enum /*< enum >*/
diff --git a/tumbler/tumbler-file-info.c b/tumbler/tumbler-file-info.c
index c2cbde8..aaac622 100644
--- a/tumbler/tumbler-file-info.c
+++ b/tumbler/tumbler-file-info.c
@@ -26,11 +26,13 @@
 #include <glib/gi18n.h>
 #include <glib-object.h>
 
+#include <tumbler/tumbler-cache.h>
 #include <tumbler/tumbler-error.h>
 #include <tumbler/tumbler-file-info.h>
 #include <tumbler/tumbler-provider-factory.h>
 #include <tumbler/tumbler-cache-provider.h>
 #include <tumbler/tumbler-thumbnail.h>
+#include <tumbler/tumbler-thumbnail-flavor.h>
 
 
 
@@ -40,6 +42,8 @@ enum
   PROP_0,
   PROP_MTIME,
   PROP_URI,
+  PROP_MIME_TYPE,
+  PROP_FLAVOR,
 };
 
 
@@ -65,9 +69,12 @@ struct _TumblerFileInfo
 {
   GObject __parent__;
 
-  guint64 mtime; 
-  GList  *thumbnails;
-  gchar  *uri;
+  TumblerThumbnailFlavor *flavor;
+  TumblerThumbnail       *thumbnail;
+
+  guint64                 mtime; 
+  gchar                  *uri;
+  gchar                  *mime_type;
 };
 
 
@@ -101,6 +108,22 @@ tumbler_file_info_class_init (TumblerFileInfoClass *klass)
                                                         NULL,
                                                         G_PARAM_READWRITE |
                                                         G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (gobject_class, PROP_MIME_TYPE,
+                                   g_param_spec_string ("mime-type",
+                                                        "mime-type",
+                                                        "mime-type",
+                                                        NULL,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (gobject_class, PROP_FLAVOR,
+                                   g_param_spec_object ("flavor",
+                                                        "flavor",
+                                                        "flavor",
+                                                        TUMBLER_TYPE_THUMBNAIL_FLAVOR,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
 }
 
 
@@ -110,7 +133,8 @@ tumbler_file_info_init (TumblerFileInfo *info)
 {
   info->mtime = 0;
   info->uri = NULL;
-  info->thumbnails = NULL;
+  info->mime_type = NULL;
+  info->thumbnail = NULL;
 }
 
 
@@ -120,9 +144,12 @@ tumbler_file_info_finalize (GObject *object)
 {
   TumblerFileInfo *info = TUMBLER_FILE_INFO (object);
 
-  g_list_foreach (info->thumbnails, (GFunc) g_object_unref, NULL);
-  g_list_free (info->thumbnails);
+  if (info->thumbnail != NULL)
+    g_object_unref (info->thumbnail);
+
+  g_object_unref (info->flavor);
 
+  g_free (info->mime_type);
   g_free (info->uri);
 
   (*G_OBJECT_CLASS (tumbler_file_info_parent_class)->finalize) (object);
@@ -146,6 +173,12 @@ tumbler_file_info_get_property (GObject    *object,
     case PROP_URI:
       g_value_set_string (value, info->uri);
       break;
+    case PROP_MIME_TYPE:
+      g_value_set_string (value, info->mime_type);
+      break;
+    case PROP_FLAVOR:
+      g_value_set_object (value, info->flavor);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -170,6 +203,12 @@ tumbler_file_info_set_property (GObject      *object,
     case PROP_URI:
       info->uri = g_value_dup_string (value);
       break;
+    case PROP_MIME_TYPE:
+      info->mime_type = g_value_dup_string (value);
+      break;
+    case PROP_FLAVOR:
+      info->flavor = g_value_dup_object (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -179,10 +218,16 @@ tumbler_file_info_set_property (GObject      *object,
 
 
 TumblerFileInfo *
-tumbler_file_info_new (const gchar *uri)
+tumbler_file_info_new (const gchar            *uri,
+                       const gchar            *mime_type,
+                       TumblerThumbnailFlavor *flavor)
 {
-  g_return_val_if_fail (uri != NULL, NULL);
-  return g_object_new (TUMBLER_TYPE_FILE_INFO, "uri", uri, NULL);
+  g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+  g_return_val_if_fail (mime_type != NULL && *mime_type != '\0', NULL);
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL);
+
+  return g_object_new (TUMBLER_TYPE_FILE_INFO, "uri", uri, "mime-type", mime_type,
+                       "flavor", flavor, NULL);
 }
 
 
@@ -230,11 +275,13 @@ tumbler_file_info_load (TumblerFileInfo *info,
   /* we no longer need the file information */
   g_object_unref (file_info);
 
-  /* make sure to clear the thumbnails list before we load the info, just in
-   * case someone decides to load the info twice */
-  g_list_foreach (info->thumbnails, (GFunc) g_object_unref, NULL);
-  g_list_free (info->thumbnails);
-  info->thumbnails = NULL;
+  /* make sure to clear the thumbnail before we load the info, just in
+   * case someone decides to load it twice */
+  if (info->thumbnail != NULL)
+    {
+      g_object_unref (info->thumbnail);
+      info->thumbnail = NULL;
+    }
 
   /* query the default cache implementation */
   cache = tumbler_cache_get_default ();
@@ -244,16 +291,10 @@ tumbler_file_info_load (TumblerFileInfo *info,
       if (!tumbler_cache_is_thumbnail (cache, info->uri))
         {
           /* query thumbnail infos for this URI from the current cache */
-          thumbnails = tumbler_cache_get_thumbnails (cache, info->uri);
+          info->thumbnail = tumbler_cache_get_thumbnail (cache, info->uri, info->flavor);
 
-          /* try to load thumbnail infos. the loop will terminate if 
-           * one of them fails */
-          for (tp = thumbnails; err == NULL && tp != NULL; tp = tp->next)
-            tumbler_thumbnail_load (tp->data, cancellable, &err);
-
-          /* add all queried thumbnails to the list */
-          info->thumbnails = g_list_concat (info->thumbnails, 
-                                                  thumbnails);
+          /* try to load thumbnail info */
+          tumbler_thumbnail_load (info->thumbnail, cancellable, &err);
         }
       else
         {
@@ -271,10 +312,9 @@ tumbler_file_info_load (TumblerFileInfo *info,
       /* propagate errors */
       g_propagate_error (error, err);
 
-      /* release thumbnails as we assume not to have any on errors */
-      g_list_foreach (info->thumbnails, (GFunc) g_object_unref, NULL);
-      g_list_free (info->thumbnails);
-      info->thumbnails = NULL;
+      /* release the thumbnail info */
+      g_object_unref (info->thumbnail);
+      info->thumbnail = NULL;
 
       return FALSE;
     }
@@ -295,6 +335,15 @@ tumbler_file_info_get_uri (TumblerFileInfo *info)
 
 
 
+const gchar *
+tumbler_file_info_get_mime_type (TumblerFileInfo *info)
+{
+  g_return_val_if_fail (TUMBLER_IS_FILE_INFO (info), NULL);
+  return info->mime_type;
+}
+
+
+
 guint64
 tumbler_file_info_get_mtime (TumblerFileInfo *info)
 {
@@ -308,16 +357,14 @@ gboolean
 tumbler_file_info_needs_update (TumblerFileInfo *info)
 {
   gboolean needs_update = FALSE;
-  GList   *lp;
 
   g_return_val_if_fail (TUMBLER_IS_FILE_INFO (info), FALSE);
 
-  /* iterate over all thumbnails and check if at least one of them needs an update */
-  for (lp = info->thumbnails; !needs_update && lp != NULL; lp = lp->next)
+  if (info->thumbnail != NULL)
     {
-      needs_update = needs_update || tumbler_thumbnail_needs_update (lp->data, 
-                                                                     info->uri,
-                                                                     info->mtime);
+      /* check if the thumbnail for the URI needs an update */
+      needs_update = tumbler_thumbnail_needs_update (info->thumbnail, 
+                                                     info->uri, info->mtime);
     }
 
   return needs_update;
@@ -325,9 +372,74 @@ tumbler_file_info_needs_update (TumblerFileInfo *info)
 
 
 
-GList *
-tumbler_file_info_get_thumbnails (TumblerFileInfo *info)
+TumblerThumbnail *
+tumbler_file_info_get_thumbnail (TumblerFileInfo *info)
 {
   g_return_val_if_fail (TUMBLER_IS_FILE_INFO (info), NULL);
-  return info->thumbnails;
+  return g_object_ref (info->thumbnail);
+}
+
+
+
+TumblerFileInfo **
+tumbler_file_info_array_new_with_flavor (const gchar *const     *uris,
+                                         const gchar *const     *mime_types,
+                                         TumblerThumbnailFlavor *flavor,
+                                         guint                  *length)
+{
+  TumblerFileInfo **infos = NULL;
+  guint             num_uris;
+  guint             num_mime_types;
+  guint             n;
+
+  g_return_val_if_fail (uris != NULL, NULL);
+
+  num_uris = g_strv_length ((GStrv) uris);
+  num_mime_types = g_strv_length ((GStrv) mime_types);
+
+  if (length != NULL)
+    *length = MIN (num_uris, num_mime_types);
+
+  infos = g_new0 (TumblerFileInfo *, MIN (num_uris, num_mime_types) + 1);
+
+  for (n = 0; n < MIN (num_uris, num_mime_types); ++n)
+    infos[n] = tumbler_file_info_new (uris[n], mime_types[n], flavor);
+
+  infos[n] = NULL;
+
+  return infos;
+}
+
+
+
+TumblerFileInfo **
+tumbler_file_info_array_copy (TumblerFileInfo **infos,
+                              guint             length)
+{
+  TumblerFileInfo **copy;
+  gint              n;
+
+  g_return_val_if_fail (infos != NULL, NULL);
+
+  copy = g_new0 (TumblerFileInfo *, length + 1);
+
+  for (n = 0; infos != NULL && infos[n] != NULL && n < length; ++n)
+    copy[n] = g_object_ref (infos[n]);
+
+  copy[n] = NULL;
+
+  return copy;
+}
+
+
+
+void
+tumbler_file_info_array_free (TumblerFileInfo **infos)
+{
+  gint n;
+
+  for (n = 0; infos != NULL && infos[n] != NULL; ++n)
+    g_object_unref (infos[n]);
+
+  g_free (infos);
 }
diff --git a/tumbler/tumbler-file-info.h b/tumbler/tumbler-file-info.h
index a30f7b6..a1330c3 100644
--- a/tumbler/tumbler-file-info.h
+++ b/tumbler/tumbler-file-info.h
@@ -27,6 +27,8 @@
 
 #include <gio/gio.h>
 
+#include <tumbler/tumbler-thumbnail.h>
+
 G_BEGIN_DECLS;
 
 #define TUMBLER_TYPE_FILE_INFO            (tumbler_file_info_get_type ())
@@ -39,16 +41,27 @@ G_BEGIN_DECLS;
 typedef struct _TumblerFileInfoClass   TumblerFileInfoClass;
 typedef struct _TumblerFileInfo        TumblerFileInfo;
 
-GType            tumbler_file_info_get_type       (void) G_GNUC_CONST;
+GType             tumbler_file_info_get_type              (void) G_GNUC_CONST;
+
+TumblerFileInfo  *tumbler_file_info_new                   (const gchar            *uri,
+                                                           const gchar            *mime_type,
+                                                           TumblerThumbnailFlavor *flavor) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+gboolean          tumbler_file_info_load                  (TumblerFileInfo        *info,
+                                                           GCancellable           *cancellable,
+                                                           GError                **error);
+const gchar      *tumbler_file_info_get_uri               (TumblerFileInfo        *info);
+const gchar      *tumbler_file_info_get_mime_type         (TumblerFileInfo        *info);
+guint64           tumbler_file_info_get_mtime             (TumblerFileInfo        *info);
+gboolean          tumbler_file_info_needs_update          (TumblerFileInfo        *info);
+TumblerThumbnail *tumbler_file_info_get_thumbnail         (TumblerFileInfo        *info) G_GNUC_WARN_UNUSED_RESULT;
 
-TumblerFileInfo *tumbler_file_info_new            (const gchar     *uri) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-gboolean         tumbler_file_info_load           (TumblerFileInfo *info,
-                                                   GCancellable    *cancellable,
-                                                   GError         **error);
-const gchar     *tumbler_file_info_get_uri        (TumblerFileInfo *info);
-guint64          tumbler_file_info_get_mtime      (TumblerFileInfo *info);
-gboolean         tumbler_file_info_needs_update   (TumblerFileInfo *info);
-GList           *tumbler_file_info_get_thumbnails (TumblerFileInfo *info);
+TumblerFileInfo **tumbler_file_info_array_new_with_flavor (const gchar *const     *uris,
+                                                           const gchar *const     *mime_types,
+                                                           TumblerThumbnailFlavor *flavor,
+                                                           guint                  *length) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+TumblerFileInfo **tumbler_file_info_array_copy            (TumblerFileInfo       **infos,
+                                                           guint                   length) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+void              tumbler_file_info_array_free            (TumblerFileInfo       **infos);
 
 G_END_DECLS;
 
diff --git a/tumbler/tumbler-thumbnail-flavor.c b/tumbler/tumbler-thumbnail-flavor.c
new file mode 100644
index 0000000..cb4bdd1
--- /dev/null
+++ b/tumbler/tumbler-thumbnail-flavor.c
@@ -0,0 +1,240 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009 Jannis Pohlmann <jannis at 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <tumbler/tumbler-thumbnail-flavor.h>
+
+
+
+/* property identifiers */
+enum
+{
+  PROP_0,
+  PROP_NAME,
+  PROP_WIDTH,
+  PROP_HEIGHT,
+};
+
+
+
+static void tumbler_thumbnail_flavor_finalize     (GObject      *object);
+static void tumbler_thumbnail_flavor_get_property (GObject      *object,
+                                                   guint         prop_id,
+                                                   GValue       *value,
+                                                   GParamSpec   *pspec);
+static void tumbler_thumbnail_flavor_set_property (GObject      *object,
+                                                   guint         prop_id,
+                                                   const GValue *value,
+                                                   GParamSpec   *pspec);
+
+
+
+struct _TumblerThumbnailFlavorClass
+{
+  GObjectClass __parent__;
+};
+
+struct _TumblerThumbnailFlavor
+{
+  GObject __parent__;
+
+  gchar  *name;
+  gint    width;
+  gint    height;
+};
+
+
+
+G_DEFINE_TYPE (TumblerThumbnailFlavor, tumbler_thumbnail_flavor, G_TYPE_OBJECT)
+
+
+
+static void
+tumbler_thumbnail_flavor_class_init (TumblerThumbnailFlavorClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  /* Determine the parent type class */
+  tumbler_thumbnail_flavor_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = tumbler_thumbnail_flavor_finalize; 
+  gobject_class->get_property = tumbler_thumbnail_flavor_get_property;
+  gobject_class->set_property = tumbler_thumbnail_flavor_set_property;
+
+  g_object_class_install_property (gobject_class, PROP_NAME,
+                                   g_param_spec_string ("name",
+                                                        "name",
+                                                        "name",
+                                                        NULL,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (gobject_class, PROP_WIDTH,
+                                   g_param_spec_int ("width",
+                                                     "width",
+                                                     "width",
+                                                     -1, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (gobject_class, PROP_HEIGHT,
+                                   g_param_spec_int ("height",
+                                                     "height",
+                                                     "height",
+                                                     -1, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT_ONLY));
+}
+
+
+
+static void
+tumbler_thumbnail_flavor_init (TumblerThumbnailFlavor *flavor)
+{
+}
+
+
+
+static void
+tumbler_thumbnail_flavor_finalize (GObject *object)
+{
+  TumblerThumbnailFlavor *flavor = TUMBLER_THUMBNAIL_FLAVOR (object);
+
+  g_free (flavor->name);
+
+  (*G_OBJECT_CLASS (tumbler_thumbnail_flavor_parent_class)->finalize) (object);
+}
+
+
+
+static void
+tumbler_thumbnail_flavor_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  TumblerThumbnailFlavor *flavor = TUMBLER_THUMBNAIL_FLAVOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, flavor->name);
+      break;
+    case PROP_WIDTH:
+      g_value_set_int (value, flavor->width);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_int (value, flavor->height);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+tumbler_thumbnail_flavor_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  TumblerThumbnailFlavor *flavor = TUMBLER_THUMBNAIL_FLAVOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      flavor->name = g_value_dup_string (value);
+      break;
+    case PROP_WIDTH:
+      flavor->width = g_value_get_int (value);
+      break;
+    case PROP_HEIGHT:
+      flavor->height = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+TumblerThumbnailFlavor *
+tumbler_thumbnail_flavor_new (const gchar *name,
+                              gint         width,
+                              gint         height)
+{
+  g_return_val_if_fail (name != NULL && *name != '\0', NULL);
+
+  return g_object_new (TUMBLER_TYPE_THUMBNAIL_FLAVOR, "name", name, 
+                       "width", width, "height", height, NULL);
+}
+
+
+
+TumblerThumbnailFlavor *
+tumbler_thumbnail_flavor_new_normal (void)
+{
+  return g_object_new (TUMBLER_TYPE_THUMBNAIL_FLAVOR, "name", "normal",
+                       "width", 128, "height", 128, NULL);
+}
+
+
+
+TumblerThumbnailFlavor *
+tumbler_thumbnail_flavor_new_large (void)
+{
+  return g_object_new (TUMBLER_TYPE_THUMBNAIL_FLAVOR, "name", "large",
+                       "width", 256, "height", 256, NULL);
+}
+
+
+
+const gchar *
+tumbler_thumbnail_flavor_get_name (TumblerThumbnailFlavor *flavor)
+{
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor), NULL);
+  return flavor->name;
+}
+
+
+
+void
+tumbler_thumbnail_flavor_get_size (TumblerThumbnailFlavor *flavor,
+                                   gint                   *width,
+                                   gint                   *height)
+{
+  g_return_if_fail (TUMBLER_IS_THUMBNAIL_FLAVOR (flavor));
+  
+  if (width != NULL)
+    *width = flavor->width;
+
+  if (height != NULL)
+    *height = flavor->height;
+}
diff --git a/tumbler/tumbler-thumbnail-flavor.h b/tumbler/tumbler-thumbnail-flavor.h
new file mode 100644
index 0000000..f442166
--- /dev/null
+++ b/tumbler/tumbler-thumbnail-flavor.h
@@ -0,0 +1,57 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/*-
+ * Copyright (c) 2009 Jannis Pohlmann <jannis at 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 !defined (TUMBLER_INSIDE_TUMBLER_H) && !defined (TUMBLER_COMPILATION)
+#error "Only <tumbler/tumbler.h> may be included directly. This file might disappear or change contents."
+#endif
+
+#ifndef __TUMBLER_THUMBNAIL_FLAVOR_H__
+#define __TUMBLER_THUMBNAIL_FLAVOR_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define TUMBLER_TYPE_THUMBNAIL_FLAVOR            (tumbler_thumbnail_flavor_get_type ())
+#define TUMBLER_THUMBNAIL_FLAVOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TUMBLER_TYPE_THUMBNAIL_FLAVOR, TumblerThumbnailFlavor))
+#define TUMBLER_THUMBNAIL_FLAVOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TUMBLER_TYPE_THUMBNAIL_FLAVOR, TumblerThumbnailFlavorClass))
+#define TUMBLER_IS_THUMBNAIL_FLAVOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TUMBLER_TYPE_THUMBNAIL_FLAVOR))
+#define TUMBLER_IS_THUMBNAIL_FLAVOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TUMBLER_TYPE_THUMBNAIL_FLAVOR)
+#define TUMBLER_THUMBNAIL_FLAVOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TUMBLER_TYPE_THUMBNAIL_FLAVOR, TumblerThumbnailFlavorClass))
+
+typedef struct _TumblerThumbnailFlavorPrivate TumblerThumbnailFlavorPrivate;
+typedef struct _TumblerThumbnailFlavorClass   TumblerThumbnailFlavorClass;
+typedef struct _TumblerThumbnailFlavor        TumblerThumbnailFlavor;
+
+GType                   tumbler_thumbnail_flavor_get_type   (void) G_GNUC_CONST;
+
+TumblerThumbnailFlavor *tumbler_thumbnail_flavor_new        (const gchar            *name,
+                                                             gint                    width,
+                                                             gint                    height) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+TumblerThumbnailFlavor *tumbler_thumbnail_flavor_new_normal (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+TumblerThumbnailFlavor *tumbler_thumbnail_flavor_new_large  (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+const gchar            *tumbler_thumbnail_flavor_get_name   (TumblerThumbnailFlavor *flavor);
+void                    tumbler_thumbnail_flavor_get_size   (TumblerThumbnailFlavor *flavor,
+                                                             gint                   *width,
+                                                             gint                   *height);
+
+G_END_DECLS
+
+#endif /* !__TUMBLER_THUMBNAIL_FLAVOR_H__ */
diff --git a/tumbler/tumbler-thumbnail.c b/tumbler/tumbler-thumbnail.c
index ecefc51..b8b497a 100644
--- a/tumbler/tumbler-thumbnail.c
+++ b/tumbler/tumbler-thumbnail.c
@@ -77,13 +77,12 @@ tumbler_thumbnail_class_init (TumblerThumbnailIface *klass)
                                                             G_PARAM_CONSTRUCT_ONLY));
   
   g_object_interface_install_property (klass,
-                                       g_param_spec_enum ("flavor",
-                                                          "flavor",
-                                                          "flavor",
-                                                          TUMBLER_TYPE_THUMBNAIL_FLAVOR,
-                                                          TUMBLER_THUMBNAIL_FLAVOR_INVALID,
-                                                          G_PARAM_READWRITE |
-                                                          G_PARAM_CONSTRUCT_ONLY));
+                                       g_param_spec_object ("flavor",
+                                                            "flavor",
+                                                            "flavor",
+                                                            TUMBLER_TYPE_THUMBNAIL_FLAVOR,
+                                                            G_PARAM_READWRITE |
+                                                            G_PARAM_CONSTRUCT_ONLY));
 }
 
 
@@ -168,62 +167,13 @@ tumbler_thumbnail_get_cache (TumblerThumbnail *thumbnail)
 
 
 
-TumblerThumbnailFlavor
+TumblerThumbnailFlavor *
 tumbler_thumbnail_get_flavor (TumblerThumbnail *thumbnail)
 {
-  TumblerThumbnailFlavor flavor;
+  TumblerThumbnailFlavor *flavor;
 
-  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL (thumbnail), TUMBLER_THUMBNAIL_FLAVOR_INVALID);
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAIL (thumbnail), NULL);
 
   g_object_get (thumbnail, "flavor", &flavor, NULL);
   return flavor;
 }
-
-
-
-TumblerThumbnailFlavor *
-tumbler_thumbnail_get_flavors (void)
-{
-  static TumblerThumbnailFlavor flavors[] = 
-  {
-#ifdef ENABLE_NORMAL_THUMBNAILS
-    TUMBLER_THUMBNAIL_FLAVOR_NORMAL,
-#endif
-#ifdef ENABLE_LARGE_THUMBNAILS
-    TUMBLER_THUMBNAIL_FLAVOR_LARGE,
-#endif
-#ifdef ENABLE_CROPPED_THUMBNAILS
-    TUMBLER_THUMBNAIL_FLAVOR_CROPPED,
-#endif
-    TUMBLER_THUMBNAIL_FLAVOR_INVALID, /* this always has to come last */
-  };
-
-  return flavors;
-}
-
-
-
-void
-tumbler_thumbnail_flavor_get_size (TumblerThumbnailFlavor flavor,
-                                   gint                  *width,
-                                   gint                  *height)
-{
-  switch (flavor)
-    {
-    case TUMBLER_THUMBNAIL_FLAVOR_NORMAL:
-      *width = 128;
-      *height = 128;
-      break;
-    case TUMBLER_THUMBNAIL_FLAVOR_LARGE:
-      *width = 256;
-      *height = 256;
-      break;
-    case TUMBLER_THUMBNAIL_FLAVOR_CROPPED:
-      *width = 124;
-      *height = 124;
-      break;
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-}
diff --git a/tumbler/tumbler-thumbnail.h b/tumbler/tumbler-thumbnail.h
index ed72b8f..965c467 100644
--- a/tumbler/tumbler-thumbnail.h
+++ b/tumbler/tumbler-thumbnail.h
@@ -28,7 +28,7 @@
 #include <gio/gio.h>
 
 #include <tumbler/tumbler-enum-types.h>
-#include <tumbler/tumbler-cache.h>
+#include <tumbler/tumbler-thumbnail-flavor.h>
 
 G_BEGIN_DECLS
 
@@ -95,13 +95,7 @@ gboolean                tumbler_thumbnail_save_file       (TumblerThumbnail
                                                            guint64                mtime,
                                                            GCancellable          *cancellable,
                                                            GError               **error);
-TumblerCache           *tumbler_thumbnail_get_cache       (TumblerThumbnail      *thumbnail);
-TumblerThumbnailFlavor  tumbler_thumbnail_get_flavor      (TumblerThumbnail      *thumbnail);
-
-TumblerThumbnailFlavor *tumbler_thumbnail_get_flavors     (void);
-void                    tumbler_thumbnail_flavor_get_size (TumblerThumbnailFlavor flavor,
-                                                           gint                  *width,
-                                                           gint                  *height);
+TumblerThumbnailFlavor *tumbler_thumbnail_get_flavor      (TumblerThumbnail      *thumbnail);
 
 G_END_DECLS
 
diff --git a/tumbler/tumbler-thumbnailer.c b/tumbler/tumbler-thumbnailer.c
index cad9667..3ab45c3 100644
--- a/tumbler/tumbler-thumbnailer.c
+++ b/tumbler/tumbler-thumbnailer.c
@@ -23,6 +23,7 @@
 #endif
 
 #include <tumbler/tumbler-marshal.h>
+#include <tumbler/tumbler-file-info.h>
 #include <tumbler/tumbler-thumbnailer.h>
 
 
@@ -136,22 +137,17 @@ tumbler_thumbnailer_class_init (TumblerThumbnailerIface *klass)
 
 
 void
-tumbler_thumbnailer_create (TumblerThumbnailer      *thumbnailer,
-                            GCancellable            *cancellable,
-                            const gchar             *uri,
-                            const gchar             *mime_hint,
-                            const gchar             *flavor)
+tumbler_thumbnailer_create (TumblerThumbnailer     *thumbnailer,
+                            GCancellable           *cancellable,
+                            TumblerFileInfo        *info)
 {
   g_return_if_fail (TUMBLER_IS_THUMBNAILER (thumbnailer));
-  g_return_if_fail (uri != NULL);
-  g_return_if_fail (mime_hint != NULL);
+  g_return_if_fail (TUMBLER_IS_FILE_INFO (info));
   g_return_if_fail (TUMBLER_THUMBNAILER_GET_IFACE (thumbnailer)->create != NULL);
 
   return (*TUMBLER_THUMBNAILER_GET_IFACE (thumbnailer)->create) (thumbnailer, 
                                                                  cancellable,
-                                                                 uri, 
-                                                                 mime_hint,
-                                                                 flavor);
+                                                                 info);
 }
 
 
@@ -197,14 +193,14 @@ tumbler_thumbnailer_get_uri_schemes (TumblerThumbnailer *thumbnailer)
 
 TumblerThumbnailer **
 tumbler_thumbnailer_array_copy (TumblerThumbnailer **thumbnailers,
-                                gint                 length)
+                                guint                length)
 {
   TumblerThumbnailer **copy;
-  gint                 n;
+  guint                n;
 
   g_return_val_if_fail (thumbnailers != NULL, NULL);
 
-  copy = g_new0 (TumblerThumbnailer *, length+1);
+  copy = g_new0 (TumblerThumbnailer *, length + 1);
 
   for (n = 0; n < length; ++n)
     if (thumbnailers[n] != NULL)
@@ -219,9 +215,9 @@ tumbler_thumbnailer_array_copy (TumblerThumbnailer **thumbnailers,
 
 void
 tumbler_thumbnailer_array_free (TumblerThumbnailer **thumbnailers,
-                                gint                 length)
+                                guint                length)
 {
-  gint n;
+  guint n;
 
   for (n = 0; thumbnailers != NULL && n < length; ++n)
     if (thumbnailers[n] != NULL)
diff --git a/tumbler/tumbler-thumbnailer.h b/tumbler/tumbler-thumbnailer.h
index 89f780b..9194fe4 100644
--- a/tumbler/tumbler-thumbnailer.h
+++ b/tumbler/tumbler-thumbnailer.h
@@ -28,6 +28,8 @@
 #include <glib-object.h>
 #include <gio/gio.h>
 
+#include <tumbler/tumbler-file-info.h>
+
 G_BEGIN_DECLS
 
 #define TUMBLER_TYPE_THUMBNAILER           (tumbler_thumbnailer_get_type ())
@@ -52,29 +54,25 @@ struct _TumblerThumbnailerIface
   void (*unregister) (TumblerThumbnailer *thumbnailer);
 
   /* virtual methods */
-  void (*create) (TumblerThumbnailer *thumbnailer,
-                  GCancellable       *cancellable,
-                  const gchar        *uri,
-                  const gchar        *mime_hint,
-                  const gchar        *flavor);
+  void (*create) (TumblerThumbnailer     *thumbnailer,
+                  GCancellable           *cancellable,
+                  TumblerFileInfo        *info);
 };
 
 GType                tumbler_thumbnailer_get_type        (void) G_GNUC_CONST;
 
-void                 tumbler_thumbnailer_create          (TumblerThumbnailer  *thumbnailer,
-                                                          GCancellable        *cancellable,
-                                                          const gchar         *uri,
-                                                          const gchar         *mime_hint,
-                                                          const gchar         *flavor);
+void                 tumbler_thumbnailer_create          (TumblerThumbnailer     *thumbnailer,
+                                                          GCancellable           *cancellable,
+                                                          TumblerFileInfo        *info);
 
 GStrv                tumbler_thumbnailer_get_hash_keys   (TumblerThumbnailer  *thumbnailer);
 GStrv                tumbler_thumbnailer_get_mime_types  (TumblerThumbnailer  *thumbnailer);
 GStrv                tumbler_thumbnailer_get_uri_schemes (TumblerThumbnailer  *thumbnailer);
 
 TumblerThumbnailer **tumbler_thumbnailer_array_copy      (TumblerThumbnailer **thumbnailers,
-                                                          gint                 length);
+                                                          guint                length);
 void                 tumbler_thumbnailer_array_free      (TumblerThumbnailer **thumbnailers,
-                                                          gint                 length);
+                                                          guint                length);
 
 G_END_DECLS
 
diff --git a/tumbler/tumbler.h b/tumbler/tumbler.h
index 3f086bf..5bff523 100644
--- a/tumbler/tumbler.h
+++ b/tumbler/tumbler.h
@@ -34,9 +34,10 @@
 #include <tumbler/tumbler-marshal.h>
 #include <tumbler/tumbler-provider-factory.h>
 #include <tumbler/tumbler-provider-plugin.h>
+#include <tumbler/tumbler-thumbnail-flavor.h>
+#include <tumbler/tumbler-thumbnail.h>
 #include <tumbler/tumbler-thumbnailer-provider.h>
 #include <tumbler/tumbler-thumbnailer.h>
-#include <tumbler/tumbler-thumbnail.h>
 #include <tumbler/tumbler-util.h>
 
 #undef TUMBLER_INSIDE_TUMBLER_H
diff --git a/tumblerd/tumbler-group-scheduler.c b/tumblerd/tumbler-group-scheduler.c
index 5e0b980..b171384 100644
--- a/tumblerd/tumbler-group-scheduler.c
+++ b/tumblerd/tumbler-group-scheduler.c
@@ -309,10 +309,10 @@ tumbler_group_scheduler_cancel_by_mount (TumblerScheduler *scheduler,
       request = iter->data;
 
       /* iterate over all request URIs */
-      for (n = 0; request->uris != NULL && request->uris[n] != NULL; ++n)
+      for (n = 0; n < request->length; ++n)
         {
           /* determine the enclosing mount for the file */
-          file = g_file_new_for_uri (request->uris[n]);
+          file = g_file_new_for_uri (tumbler_file_info_get_uri (request->infos[n]));
 
           /* cancel the URI if it lies of the mount point */
           if (g_file_has_prefix (file, mount_point))
@@ -452,7 +452,7 @@ tumbler_group_scheduler_thread (gpointer data,
   g_mutex_unlock (scheduler->mutex);
 
   /* process URI by URI */
-  for (n = 0; request->uris[n] != NULL; ++n)
+  for (n = 0; n < request->length; ++n)
     {
       /* finish the request if it was dequeued */
       g_mutex_lock (scheduler->mutex);
@@ -469,52 +469,28 @@ tumbler_group_scheduler_thread (gpointer data,
         continue;
 
       /* create a file infor for the current URI */
-      info = tumbler_file_info_new (request->uris[n]);
       uri_needs_update = FALSE;
 
       G_LOCK (group_access_lock);
 
       /* try to load thumbnail information about the URI */
-      if (tumbler_file_info_load (info, NULL, &error))
+      if (tumbler_file_info_load (request->infos[n], NULL, &error))
         {
           /* check if we have a thumbnailer for the URI */
           if (request->thumbnailers[n] != NULL)
             {
-              /* compute the last modification time of the URI */
-              mtime = tumbler_file_info_get_mtime (info);
-
-              /* get a list of all thumbnails for this URI */
-              thumbnails = tumbler_file_info_get_thumbnails (info);
-
-              /* iterate over them */
-              for (lp = thumbnails; error == NULL && lp != NULL; lp = lp->next)
-                {
-                  /* try to load the thumbnail information */
-                  if (tumbler_thumbnail_load (lp->data, NULL, &error))
-                    {
-                      /* check if the thumbnail needs an update */
-                      outdated = tumbler_thumbnail_needs_update (lp->data, 
-                                                                 request->uris[n],
-                                                                 mtime);
-
-                      /* if at least one thumbnail is out of date, we need to 
-                       * regenerate thumbnails for the URI */
-                      uri_needs_update = uri_needs_update || outdated;
-                    }
-                }
+              /* check if the thumbnail needs an update */
+              uri_needs_update = tumbler_file_info_needs_update (request->infos[n]);
             }
           else
             {
               /* no thumbnailer for this URI, we need to emit an error */
               g_set_error (&error, TUMBLER_ERROR, TUMBLER_ERROR_NO_THUMBNAILER,
                            _("No thumbnailer available for \"%s\""), 
-                           request->uris[n]);
+                           tumbler_file_info_get_uri (request->infos[n]));
             }
         }
 
-      /* release the file info */
-      g_object_unref (info);
-
       G_UNLOCK (group_access_lock);
 
       /* check if the URI is supported */
@@ -524,13 +500,14 @@ tumbler_group_scheduler_thread (gpointer data,
           if (uri_needs_update)
             missing_uris = g_list_prepend (missing_uris, GINT_TO_POINTER (n));
           else
-            cached_uris = g_list_prepend (cached_uris, request->uris[n]);
+            cached_uris = g_list_prepend (cached_uris, request->infos[n]);
         }
       else
         {
           /* emit an error for the URI */
           tumbler_scheduler_emit_uri_error (TUMBLER_SCHEDULER (scheduler), request,
-                                            request->uris[n], error);
+                                            tumbler_file_info_get_uri (request->infos[n]),
+                                            error);
           g_clear_error (&error);
         }
     }
@@ -541,7 +518,7 @@ tumbler_group_scheduler_thread (gpointer data,
       /* allocate a URI array and fill it with all cached URIs */
       uris = g_new0 (const gchar *, g_list_length (cached_uris) + 1);
       for (n = 0, lp = g_list_last (cached_uris); lp != NULL; lp = lp->prev, ++n)
-        uris[n] = lp->data;
+        uris[n] = tumbler_file_info_get_uri (lp->data);
       uris[n] = NULL;
 
       /* notify others that the cached thumbnails are ready */
@@ -585,9 +562,7 @@ tumbler_group_scheduler_thread (gpointer data,
       /* tell the thumbnailer to generate the thumbnail */
       tumbler_thumbnailer_create (request->thumbnailers[n], 
                                   request->cancellables[n],
-                                  request->uris[n], 
-                                  request->mime_hints[n],
-                                  request->flavor);
+                                  request->infos[n]);
 
       /* disconnect from all signals when we're finished */
       g_signal_handlers_disconnect_matched (request->thumbnailers[n],
diff --git a/tumblerd/tumbler-lifo-scheduler.c b/tumblerd/tumbler-lifo-scheduler.c
index 79d9dff..3620496 100644
--- a/tumblerd/tumbler-lifo-scheduler.c
+++ b/tumblerd/tumbler-lifo-scheduler.c
@@ -284,10 +284,10 @@ tumbler_lifo_scheduler_cancel_by_mount (TumblerScheduler *scheduler,
       request = iter->data;
 
       /* iterate over all request URIs */
-      for (n = 0; request->uris != NULL && request->uris[n] != NULL; ++n)
+      for (n = 0; n < request->length; ++n)
         {
           /* determine the enclosing mount for the file */
-          file = g_file_new_for_uri (request->uris[n]);
+          file = g_file_new_for_uri (tumbler_file_info_get_uri (request->infos[n]));
 
           /* cancel the URI if it lies of the mount point */
           if (g_file_has_prefix (file, mount_point))
@@ -307,8 +307,8 @@ tumbler_lifo_scheduler_cancel_by_mount (TumblerScheduler *scheduler,
 
 
 static void
-tumbler_lifo_scheduler_finish_request (TumblerLifoScheduler *scheduler,
-                                       TumblerSchedulerRequest   *request)
+tumbler_lifo_scheduler_finish_request (TumblerLifoScheduler    *scheduler,
+                                       TumblerSchedulerRequest *request)
 {
   g_return_if_fail (TUMBLER_IS_LIFO_SCHEDULER (scheduler));
   g_return_if_fail (request != NULL);
@@ -385,7 +385,7 @@ tumbler_lifo_scheduler_thread (gpointer data,
   g_mutex_unlock (scheduler->mutex);
 
   /* process URI by URI */
-  for (n = 0; request->uris[n] != NULL; ++n)
+  for (n = 0; n < request->length; ++n)
     {
       /* finish the request if it was dequeued */
       g_mutex_lock (scheduler->mutex);
@@ -402,52 +402,28 @@ tumbler_lifo_scheduler_thread (gpointer data,
         continue;
 
       /* create a file info for the current URI */
-      info = tumbler_file_info_new (request->uris[n]);
       uri_needs_update = FALSE;
 
       G_LOCK (plugin_access_lock);
 
       /* try to load thumbnail information about the URI */
-      if (tumbler_file_info_load (info, NULL, &error))
+      if (tumbler_file_info_load (request->infos[n], NULL, &error))
         {
           /* check if we have a thumbnailer for the URI */
           if (request->thumbnailers[n] != NULL)
             {
-              /* compute the last modification time of the URI */
-              mtime = tumbler_file_info_get_mtime (info);
-
-              /* get a list of all thumbnails for this URI */
-              thumbnails = tumbler_file_info_get_thumbnails (info);
-
-              /* iterate over them */
-              for (lp = thumbnails; error == NULL && lp != NULL; lp = lp->next)
-                {
-                  /* try to load the thumbnail information */
-                  if (tumbler_thumbnail_load (lp->data, NULL, &error))
-                    {
-                      /* check if the thumbnail needs an update */
-                      outdated = tumbler_thumbnail_needs_update (lp->data, 
-                                                                 request->uris[n],
-                                                                 mtime);
-
-                      /* if at least one thumbnail is out of date, we need to 
-                       * regenerate thumbnails for the URI */
-                      uri_needs_update = uri_needs_update || outdated;
-                    }
-                }
+              /* check if the thumbnail needs an update */
+              uri_needs_update = tumbler_file_info_needs_update (request->infos[n]);
             }
           else
             {
               /* no thumbnailer for this URI, we need to emit an error */
               g_set_error (&error, TUMBLER_ERROR, TUMBLER_ERROR_NO_THUMBNAILER,
                            _("No thumbnailer available for \"%s\""), 
-                           request->uris[n]);
+                           tumbler_file_info_get_uri (request->infos[n]));
             }
         }
 
-      /* release the file info */
-      g_object_unref (info);
-
       G_UNLOCK (plugin_access_lock);
 
       /* check if the URI is supported */
@@ -457,13 +433,14 @@ tumbler_lifo_scheduler_thread (gpointer data,
           if (uri_needs_update)
             missing_uris = g_list_prepend (missing_uris, GINT_TO_POINTER (n));
           else
-            cached_uris = g_list_prepend (cached_uris, request->uris[n]);
+            cached_uris = g_list_prepend (cached_uris, request->infos[n]);
         }
       else
         {
           /* emit an error for the URI */
           tumbler_scheduler_emit_uri_error (TUMBLER_SCHEDULER (scheduler), request,
-                                            request->uris[n], error);
+                                            tumbler_file_info_get_uri (request->infos[n]),
+                                            error);
           g_clear_error (&error);
         }
     }
@@ -474,12 +451,11 @@ tumbler_lifo_scheduler_thread (gpointer data,
       /* allocate a URI array and fill it with all cached URIs */
       uris = g_new0 (const gchar *, g_list_length (cached_uris) + 1);
       for (n = 0, lp = g_list_last (cached_uris); lp != NULL; lp = lp->prev, ++n)
-        uris[n] = lp->data;
+        uris[n] = tumbler_file_info_get_uri (lp->data);
       uris[n] = NULL;
 
       /* notify others that the cached thumbnails are ready */
-      g_signal_emit_by_name (scheduler, "ready", request->handle, uris, 
-                             request->origin);
+      g_signal_emit_by_name (scheduler, "ready", request->handle, uris, request->origin);
 
       /* free string array and cached list */
       g_list_free (cached_uris);
@@ -517,9 +493,7 @@ tumbler_lifo_scheduler_thread (gpointer data,
       /* tell the thumbnailer to generate the thumbnail */
       tumbler_thumbnailer_create (request->thumbnailers[n], 
                                   request->cancellables[n],
-                                  request->uris[n], 
-                                  request->mime_hints[n],
-                                  request->flavor);
+                                  request->infos[n]);
 
       /* disconnect from all signals when we're finished */
       g_signal_handlers_disconnect_matched (request->thumbnailers[n],
diff --git a/tumblerd/tumbler-registry.c b/tumblerd/tumbler-registry.c
index 9bdd465..2f5d9d2 100644
--- a/tumblerd/tumbler-registry.c
+++ b/tumblerd/tumbler-registry.c
@@ -429,43 +429,35 @@ tumbler_registry_get_thumbnailers (TumblerRegistry *registry)
 
 
 TumblerThumbnailer **
-tumbler_registry_get_thumbnailer_array (TumblerRegistry *registry,
-                                        const GStrv      uris,
-                                        const GStrv      mime_hints,
-                                        gint            *length)
+tumbler_registry_get_thumbnailer_array (TumblerRegistry    *registry,
+                                        TumblerFileInfo   **infos,
+                                        guint               length)
 {
   TumblerThumbnailer **thumbnailers = NULL;
   gchar               *hash_key;
   gchar               *scheme;
-  gint                 num_mime_hints;
-  gint                 num_thumbnailers;
-  gint                 num_uris;
-  gint                 n;
+  guint                num_mime_hints;
+  guint                num_thumbnailers;
+  guint                num_uris;
+  guint                n;
 
   g_return_val_if_fail (TUMBLER_IS_REGISTRY (registry), NULL);
-  g_return_val_if_fail (uris != NULL, NULL);
-  g_return_val_if_fail (mime_hints != NULL, NULL);
+  g_return_val_if_fail (infos != NULL, NULL);
 
-  num_uris = g_strv_length (uris);
-  num_mime_hints = g_strv_length (mime_hints);
-  
-  /* we handle situations silently where num_uris != num_mime_hints */
-  num_thumbnailers = MAX (0, MIN (num_uris, num_mime_hints));
-
-  /* set the length return value */
-  *length = num_thumbnailers;
+  for (length = 0; infos != NULL && infos[length] != NULL; ++length);
 
   /* allocate the thumbnailer array */
-  thumbnailers = g_new0 (TumblerThumbnailer *, num_thumbnailers + 1);
+  thumbnailers = g_new0 (TumblerThumbnailer *, length + 1);
 
   /* iterate over all URIs */
-  for (n = 0; n < num_thumbnailers; ++n)
+  for (n = 0; n < length; ++n)
     {
       g_mutex_lock (registry->mutex);
 
       /* determine the URI scheme and generate a hash key */
-      scheme = g_uri_parse_scheme (uris[n]);
-      hash_key = g_strdup_printf ("%s-%s", scheme, mime_hints[n]);
+      scheme = g_uri_parse_scheme (tumbler_file_info_get_uri (infos[n]));
+      hash_key = g_strdup_printf ("%s-%s", scheme, 
+                                  tumbler_file_info_get_mime_type (infos[n]));
 
       /* see if we can find a thumbnailer to handle this URI/MIME type pair */
       thumbnailers[n] = tumbler_registry_lookup (registry, hash_key);
diff --git a/tumblerd/tumbler-registry.h b/tumblerd/tumbler-registry.h
index 011fd04..dcd3ad3 100644
--- a/tumblerd/tumbler-registry.h
+++ b/tumblerd/tumbler-registry.h
@@ -44,9 +44,8 @@ void                 tumbler_registry_add                   (TumblerRegistry
                                                              TumblerThumbnailer  *thumbnailer);
 GList               *tumbler_registry_get_thumbnailers      (TumblerRegistry     *registry) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
 TumblerThumbnailer **tumbler_registry_get_thumbnailer_array (TumblerRegistry     *registry,
-                                                             const GStrv          uris,
-                                                             const GStrv          mime_hints,
-                                                             gint                *length) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+                                                             TumblerFileInfo    **infos,
+                                                             guint                length) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
 void                 tumbler_registry_update_supported      (TumblerRegistry     *registry);
 void                 tumbler_registry_get_supported         (TumblerRegistry     *registry,
                                                              const gchar *const **uri_schemes,
diff --git a/tumblerd/tumbler-scheduler.c b/tumblerd/tumbler-scheduler.c
index a002c9b..d0f8f09 100644
--- a/tumblerd/tumbler-scheduler.c
+++ b/tumblerd/tumbler-scheduler.c
@@ -255,36 +255,33 @@ tumbler_scheduler_emit_uri_error (TumblerScheduler        *scheduler,
 
 
 TumblerSchedulerRequest *
-tumbler_scheduler_request_new (const GStrv          uris,
-                               const GStrv          mime_hints,
+tumbler_scheduler_request_new (TumblerFileInfo    **infos,
                                TumblerThumbnailer **thumbnailers,
-                               gint                 length,
-                               const gchar         *flavor,
+                               guint                length,
                                const gchar         *origin)
 {
   TumblerSchedulerRequest *request = NULL;
   static gint              handle  = 0;
-  gint                     n;
+  guint                    n;
 
-  g_return_val_if_fail (uris != NULL, NULL);
-  g_return_val_if_fail (mime_hints != NULL, NULL);
+  g_return_val_if_fail (infos != NULL, NULL);
   g_return_val_if_fail (thumbnailers != NULL, NULL);
 
   request = g_new0 (TumblerSchedulerRequest, 1);
   if (origin)
     request->origin = g_strdup (origin);
   request->dequeued = FALSE;
-  request->flavor = g_strdup (flavor);
   request->scheduler = NULL;
   request->handle = handle++;
-  request->uris = g_strdupv (uris);
-  request->mime_hints = g_strdupv (mime_hints);
+  request->infos = tumbler_file_info_array_copy (infos, length);
   request->thumbnailers = tumbler_thumbnailer_array_copy (thumbnailers, length);
   request->length = length;
+
   request->cancellables = g_new0 (GCancellable *, request->length + 1);
 
   for (n = 0; n < request->length; ++n)
     request->cancellables[n] = g_cancellable_new ();
+
   request->cancellables[n] = NULL;
 
   return request;
@@ -299,21 +296,18 @@ tumbler_scheduler_request_free (TumblerSchedulerRequest *request)
 
   g_return_if_fail (request != NULL);
 
-  g_strfreev (request->uris);
-  g_strfreev (request->mime_hints);
+  tumbler_thumbnailer_array_free (request->thumbnailers, request->length);
 
   if (G_LIKELY (request->scheduler != NULL))
     g_object_unref (request->scheduler);
 
-  tumbler_thumbnailer_array_free (request->thumbnailers, request->length);
+  tumbler_file_info_array_free (request->infos);
 
-  for (n = 0; n < request->length; ++n)
+  for (n = 0; request->cancellables != NULL && request->cancellables[n] != NULL; ++n)
     g_object_unref (request->cancellables[n]);
-
   g_free (request->cancellables);
-  g_free (request->origin);
-  g_free (request->flavor);
 
+  g_free (request->origin);
   g_free (request);
 }
 
diff --git a/tumblerd/tumbler-scheduler.h b/tumblerd/tumbler-scheduler.h
index fca90b4..e9969e7 100644
--- a/tumblerd/tumbler-scheduler.h
+++ b/tumblerd/tumbler-scheduler.h
@@ -22,6 +22,7 @@
 #define __TUMBLER_SCHEDULER_H__
 
 #include <gio/gio.h>
+
 #include <tumbler/tumbler.h>
 
 G_BEGIN_DECLS
@@ -77,11 +78,9 @@ void                     tumbler_scheduler_emit_uri_error        (TumblerSchedul
                                                                   TumblerSchedulerRequest *request, 
                                                                   const gchar             *uri,
                                                                   GError                  *error);
-TumblerSchedulerRequest *tumbler_scheduler_request_new           (const GStrv              uris,
-                                                                  const GStrv              mime_hints,
+TumblerSchedulerRequest *tumbler_scheduler_request_new           (TumblerFileInfo        **infos,
                                                                   TumblerThumbnailer     **thumbnailers,
-                                                                  gint                     length,
-                                                                  const gchar             *flavor,
+                                                                  guint                    length,
                                                                   const gchar             *origin);
 void                     tumbler_scheduler_request_free          (TumblerSchedulerRequest *request);
 gint                     tumbler_scheduler_request_compare       (gconstpointer            a,
@@ -94,14 +93,12 @@ struct _TumblerSchedulerRequest
 {
   TumblerThumbnailer **thumbnailers;
   TumblerScheduler    *scheduler;
+  TumblerFileInfo    **infos;
+  GCancellable       **cancellables;
   gboolean             dequeued;
-  GStrv                mime_hints;
-  GStrv                uris;
   guint                handle;
-  gint                 length;
-  GCancellable       **cancellables;
   gchar               *origin;
-  gchar               *flavor;
+  guint                length;
 };
 
 G_END_DECLS
diff --git a/tumblerd/tumbler-service.c b/tumblerd/tumbler-service.c
index 2eb1bdf..ad1644f 100644
--- a/tumblerd/tumbler-service.c
+++ b/tumblerd/tumbler-service.c
@@ -594,43 +594,55 @@ tumbler_service_start (TumblerService *service,
 
 void
 tumbler_service_queue (TumblerService        *service,
-                       const GStrv            uris,
-                       const GStrv            mime_hints,
-                       const gchar           *flavor,
-                       const gchar           *desired_scheduler,
+                       const gchar *const    *uris,
+                       const gchar *const    *mime_hints,
+                       const gchar           *flavor_name,
+                       const gchar           *scheduler_name,
                        guint                  handle_to_dequeue,
                        DBusGMethodInvocation *context)
 {
-  TumblerScheduler        *scheduler = NULL;
   TumblerSchedulerRequest *scheduler_request;
+  TumblerThumbnailFlavor  *flavor;
   TumblerThumbnailer     **thumbnailers;
+  TumblerScheduler        *scheduler = NULL;
+  TumblerFileInfo        **infos;
+  TumblerCache            *cache;
   GList                   *iter;
   gchar                   *name;
   gchar                   *origin;
   guint                    handle;
-  gint                     num_thumbnailers;
+  guint                    length;
 
   dbus_async_return_if_fail (TUMBLER_IS_SERVICE (service), context);
   dbus_async_return_if_fail (uris != NULL, context);
   dbus_async_return_if_fail (mime_hints != NULL, context);
 
   /* if the scheduler is not defined, fall back to "default" */
-  if (desired_scheduler == NULL || *desired_scheduler == '\0')
-    desired_scheduler = "default";
+  if (scheduler_name == NULL || *scheduler_name == '\0')
+    scheduler_name = "default";
 
   g_mutex_lock (service->mutex);
 
+  cache = tumbler_cache_get_default ();
+  flavor = tumbler_cache_get_flavor (cache, flavor_name);
+  g_object_unref (cache);
+
+  /* TODO we need to check if the flavor is supported first and otherwise
+   * emit an error signal */
+  g_assert (cache);
+
+  infos = tumbler_file_info_array_new_with_flavor (uris, mime_hints, flavor,
+                                                   &length);
+
   /* get an array with one thumbnailer for each URI in the request */
-  thumbnailers = tumbler_registry_get_thumbnailer_array (service->registry,
-                                                         uris, mime_hints, 
-                                                         &num_thumbnailers);
+  thumbnailers = tumbler_registry_get_thumbnailer_array (service->registry, infos,
+                                                         length);
 
   origin = dbus_g_method_get_sender (context);
 
   /* allocate a scheduler request */
-  scheduler_request = tumbler_scheduler_request_new (uris, mime_hints, thumbnailers,
-                                                     num_thumbnailers, flavor, 
-                                                     origin);
+  scheduler_request = tumbler_scheduler_request_new (infos, thumbnailers, 
+                                                     length, origin);
 
   g_free (origin);
 
@@ -649,7 +661,7 @@ tumbler_service_queue (TumblerService        *service,
       name = tumbler_scheduler_get_name (TUMBLER_SCHEDULER (iter->data));
 
       /* check if this is the scheduler we are looking for */
-      if (g_strcmp0 (name, desired_scheduler) == 0)
+      if (g_strcmp0 (name, scheduler_name) == 0)
         scheduler = TUMBLER_SCHEDULER (iter->data);
 
       /* free the scheduler name */
@@ -665,7 +677,7 @@ tumbler_service_queue (TumblerService        *service,
   tumbler_scheduler_push (scheduler, scheduler_request);
   
   /* free the thumbnailer array */
-  tumbler_thumbnailer_array_free (thumbnailers, num_thumbnailers);
+  tumbler_thumbnailer_array_free (thumbnailers, length);
 
   g_mutex_unlock (service->mutex);
 
@@ -723,45 +735,47 @@ tumbler_service_get_supported (TumblerService        *service,
   dbus_g_method_return (context, uri_schemes, mime_types);
 }
 
+
+
 void 
 tumbler_service_get_flavors (TumblerService        *service,
                               DBusGMethodInvocation *context)
 {
-  guint                   n;
-  TumblerThumbnailFlavor *flavors;
-  TumblerThumbnailFlavor  flavor;
-  GStrv                   flavors_strv;
+  TumblerCache *cache;
+  const gchar **flavor_strings;
+  GList        *flavors;
+  GList        *iter;
+  guint         n;
 
-  flavors = tumbler_thumbnail_get_flavors ();
+  cache = tumbler_cache_get_default ();
 
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n);
+  if (cache != NULL)
+    {
+      flavors = tumbler_cache_get_flavors (cache);
+      flavor_strings = g_new0 (const gchar *, g_list_length (flavors) + 1);
 
-  flavors_strv = g_new0 (gchar *, n + 1);
+      for (iter = flavors, n = 0; iter != NULL; iter = iter->next, ++n)
+        flavor_strings[n] = tumbler_thumbnail_flavor_get_name (iter->data);
+      flavor_strings[n] = NULL;
+    
+      dbus_g_method_return (context, flavor_strings);
 
-  for (n = 0; flavors[n] != TUMBLER_THUMBNAIL_FLAVOR_INVALID; ++n)
-    {
-      flavor = flavors[n];
+      g_free (flavor_strings);
 
-      switch (flavor)
-        {
-        case TUMBLER_THUMBNAIL_FLAVOR_NORMAL:
-          flavors_strv[n] = "normal";
-          break;
-        case TUMBLER_THUMBNAIL_FLAVOR_LARGE:
-          flavors_strv[n] = "large";
-          break;
-        case TUMBLER_THUMBNAIL_FLAVOR_CROPPED:
-          flavors_strv[n] = "cropped";
-          break;
-        default:
-          g_assert_not_reached ();
-          break;
-        }
+      g_list_foreach (flavors, (GFunc) g_object_unref, NULL);
+      g_list_free (flavors);
+
+      g_object_unref (cache);
     }
+  else
+    {
+      flavor_strings = g_new0 (const gchar *, 1);
+      flavor_strings[0] = NULL;
 
-  dbus_g_method_return (context, flavors_strv);
+      dbus_g_method_return (context, flavor_strings);
 
-  g_free (flavors_strv);
+      g_free (flavor_strings);
+    }
 }
 
 
diff --git a/tumblerd/tumbler-service.h b/tumblerd/tumbler-service.h
index 0ab0f92..e4650ea 100644
--- a/tumblerd/tumbler-service.h
+++ b/tumblerd/tumbler-service.h
@@ -44,10 +44,10 @@ TumblerService *tumbler_service_new            (DBusGConnection       *connectio
 gboolean        tumbler_service_start          (TumblerService        *service,
                                                 GError               **error);
 void            tumbler_service_queue          (TumblerService        *service,
-                                                const GStrv            uris,
-                                                const GStrv            mime_hints,
-                                                const gchar           *flavor,
-                                                const gchar           *s_scheduler,
+                                                const gchar *const    *uris,
+                                                const gchar *const    *mime_hints,
+                                                const gchar           *flavor_name,
+                                                const gchar           *scheduler_name,
                                                 guint                  handle_to_dequeue,
                                                 DBusGMethodInvocation *context);
 void            tumbler_service_dequeue        (TumblerService         *service,
diff --git a/tumblerd/tumbler-specialized-thumbnailer.c b/tumblerd/tumbler-specialized-thumbnailer.c
index b8c4e01..158e501 100644
--- a/tumblerd/tumbler-specialized-thumbnailer.c
+++ b/tumblerd/tumbler-specialized-thumbnailer.c
@@ -58,9 +58,7 @@ static void tumbler_specialized_thumbnailer_set_property    (GObject
                                                              GParamSpec                    *pspec);
 static void tumbler_specialized_thumbnailer_create          (TumblerThumbnailer            *thumbnailer,
                                                              GCancellable                  *cancellable,
-                                                             const gchar                   *uri,
-                                                             const gchar                   *mime_hint,
-                                                             const gchar                   *flavor);
+                                                             TumblerFileInfo               *info);
 static void tumbler_specialized_thumbnailer_proxy_ready     (DBusGProxy                    *proxy,
                                                              const gchar                   *uri,
                                                              TumblerSpecializedThumbnailer *thumbnailer);
@@ -334,9 +332,7 @@ tumbler_specialized_thumbnailer_set_property (GObject      *object,
 static void
 tumbler_specialized_thumbnailer_create (TumblerThumbnailer *thumbnailer,
                                         GCancellable       *cancellable,
-                                        const gchar        *uri,
-                                        const gchar        *mime_hint,
-                                        const gchar        *flavor)
+                                        TumblerFileInfo    *info)
 {
   /* TODO */
 }



More information about the Xfce4-commits mailing list