Trim trailing white spaces.
[geeqie.git] / src / thumb_standard.c
index 3b1ee6a..f3cd252 100644 (file)
@@ -1,6 +1,7 @@
 /*
- * GQview
- * (C) 2005 John Ellis
+ * Geeqie
+ * (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2012 The Geeqie Team
  *
  * Author: John Ellis
  *
  */
 
 
-#include "gqview.h"
+#include "main.h"
 #include "thumb_standard.h"
 
-#include "cache.h"     /* for cache_ensure_dir_exists */
 #include "image-load.h"
 #include "md5-util.h"
 #include "pixbuf_util.h"
 #include "ui_fileops.h"
+#include "filedata.h"
+#include "exif.h"
+#include "metadata.h"
 
 
 /*
@@ -62,7 +65,7 @@
 
 
 static void thumb_loader_std_error_cb(ImageLoader *il, gpointer data);
-static gint thumb_loader_std_setup(ThumbLoaderStd *tl, const gchar *path);
+static gint thumb_loader_std_setup(ThumbLoaderStd *tl, FileData *fd);
 
 
 ThumbLoaderStd *thumb_loader_std_new(gint width, gint height)
@@ -72,17 +75,9 @@ ThumbLoaderStd *thumb_loader_std_new(gint width, gint height)
        tl = g_new0(ThumbLoaderStd, 1);
 
        tl->standard_loader = TRUE;
-
        tl->requested_width = width;
        tl->requested_height = height;
-
-       tl->pixbuf = NULL;
-       tl->il = NULL;
-       tl->source_path = NULL;
-
-       tl->cache_enable = enable_thumb_caching;
-       tl->cache_local = FALSE;
-       tl->cache_retry = FALSE;
+       tl->cache_enable = options->thumbnails.enable_caching;
 
        return tl;
 }
@@ -103,14 +98,11 @@ void thumb_loader_std_set_callbacks(ThumbLoaderStd *tl,
 
 static void thumb_loader_std_reset(ThumbLoaderStd *tl)
 {
-       if (tl->pixbuf) g_object_unref(tl->pixbuf);
-       tl->pixbuf = NULL;
-
        image_loader_free(tl->il);
        tl->il = NULL;
 
-       g_free(tl->source_path);
-       tl->source_path = NULL;
+       file_data_unref(tl->fd);
+       tl->fd = NULL;
 
        g_free(tl->thumb_path);
        tl->thumb_path = NULL;
@@ -130,58 +122,47 @@ static void thumb_loader_std_reset(ThumbLoaderStd *tl)
        tl->progress = 0.0;
 }
 
-static gchar *thumb_std_cache_path(const gchar *path, const gchar *uri, gint local,
+static gchar *thumb_std_cache_path(const gchar *path, const gchar *uri, gboolean local,
                                   const gchar *cache_subfolder)
 {
        gchar *result = NULL;
-       gchar *cache_base;
        gchar *md5_text;
        guchar digest[16];
+       gchar *name;
 
        if (!path || !uri || !cache_subfolder) return NULL;
 
+       md5_get_digest((guchar *)uri, strlen(uri), digest);
+       md5_text = md5_digest_to_text(digest);
+
+       if (!md5_text) return NULL;
+
+       name = g_strconcat(md5_text, THUMB_NAME_EXTENSION, NULL);
+
        if (local)
                {
-               gchar *base;
+               gchar *base = remove_level_from_path(path);
 
-               base = remove_level_from_path(path);
-               cache_base = g_strconcat(base, "/", THUMB_FOLDER, "/", cache_subfolder, NULL);
+               result = g_build_filename(base, THUMB_FOLDER_LOCAL, cache_subfolder, name, NULL);
                g_free(base);
                }
        else
                {
-               cache_base = g_strconcat(homedir(), "/", THUMB_FOLDER, "/", cache_subfolder, NULL);
+               result = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, cache_subfolder, name, NULL);
                }
 
-       md5_get_digest(uri, strlen(uri), digest);
-       md5_text = md5_digest_to_text(digest);
-
-       if (cache_base && md5_text)
-               {
-               result = g_strconcat(cache_base, "/", md5_text, THUMB_NAME_EXTENSION, NULL);
-               }
-
-       g_free(cache_base);
+       g_free(name);
        g_free(md5_text);
 
        return result;
 }
 
-static gchar *thumb_loader_std_cache_path(ThumbLoaderStd *tl, gint local, GdkPixbuf *pixbuf, gint fail)
+static gchar *thumb_loader_std_cache_path(ThumbLoaderStd *tl, gboolean local, GdkPixbuf *pixbuf, gboolean fail)
 {
-#if 0
-       gchar *result = NULL;
-       gchar *cache_base;
-#endif
-       const gchar *folder_size;
-#if 0
-       const gchar *uri;
-       gchar *md5_text;
-       guchar digest[16];
-#endif
+       const gchar *folder;
        gint w, h;
 
-       if (!tl->source_path || !tl->thumb_uri) return NULL;
+       if (!tl->fd || !tl->thumb_uri) return NULL;
 
        if (pixbuf)
                {
@@ -196,55 +177,26 @@ static gchar *thumb_loader_std_cache_path(ThumbLoaderStd *tl, gint local, GdkPix
 
        if (fail)
                {
-               folder_size = THUMB_FOLDER_FAIL;
+               folder = THUMB_FOLDER_FAIL;
                }
        else if (w > THUMB_SIZE_NORMAL || h > THUMB_SIZE_NORMAL)
                {
-               folder_size = THUMB_FOLDER_LARGE;
+               folder = THUMB_FOLDER_LARGE;
                }
        else
                {
-               folder_size = THUMB_FOLDER_NORMAL;
+               folder = THUMB_FOLDER_NORMAL;
                }
 
-       return thumb_std_cache_path(tl->source_path,
+       return thumb_std_cache_path(tl->fd->path,
                                    (local) ?  tl->local_uri : tl->thumb_uri,
-                                   local, folder_size);
-
-#if 0
-       if (local)
-               {
-               gchar *base;
-
-               base = remove_level_from_path(tl->source_path);
-               cache_base = g_strconcat(base, "/", THUMB_FOLDER, "/", folder_size, NULL);
-               g_free(base);
-               }
-       else
-               {
-               cache_base = g_strconcat(homedir(), "/", THUMB_FOLDER, "/", folder_size, NULL);
-               }
-
-       uri = (local) ? tl->local_uri : tl->thumb_uri;
-       md5_get_digest(uri, strlen(uri), digest);
-       md5_text = md5_digest_to_text(digest);
-
-       if (cache_base && md5_text)
-               {
-               result = g_strconcat(cache_base, "/", md5_text, THUMB_NAME_EXTENSION, NULL);
-               }
-
-       g_free(cache_base);
-       g_free(md5_text);
-
-       return result;
-#endif
+                                   local, folder);
 }
 
-static gint thumb_loader_std_fail_check(ThumbLoaderStd *tl)
+static gboolean thumb_loader_std_fail_check(ThumbLoaderStd *tl)
 {
        gchar *fail_path;
-       gint result = FALSE;
+       gboolean result = FALSE;
 
        fail_path = thumb_loader_std_cache_path(tl, FALSE, NULL, TRUE);
        if (isfile(fail_path))
@@ -272,11 +224,8 @@ static gint thumb_loader_std_fail_check(ThumbLoaderStd *tl)
                        if (mtime_str && strtol(mtime_str, NULL, 10) == tl->source_mtime)
                                {
                                result = TRUE;
-                               if (debug)
-                                       {
-                                       printf("thumb fail valid: %s\n", tl->source_path);
-                                       printf("           thumb: %s\n", fail_path);
-                                       }
+                               DEBUG_1("thumb fail valid: %s", tl->fd->path);
+                               DEBUG_1("           thumb: %s", fail_path);
                                }
 
                        g_object_unref(G_OBJECT(pixbuf));
@@ -289,7 +238,7 @@ static gint thumb_loader_std_fail_check(ThumbLoaderStd *tl)
        return result;
 }
 
-static gint thumb_loader_std_validate(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
+static gboolean thumb_loader_std_validate(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
 {
        const gchar *valid_uri;
        const gchar *uri;
@@ -323,7 +272,7 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
 {
        gchar *base_path;
        gchar *tmp_path;
-       gint fail;
+       gboolean fail;
 
        if (!tl->cache_enable || tl->cache_hit) return;
        if (tl->thumb_path) return;
@@ -333,7 +282,7 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
                /* local failures are not stored */
                if (tl->cache_local) return;
 
-               pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
+               pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
                fail = TRUE;
                }
        else
@@ -359,25 +308,22 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
                        struct stat st;
                        gchar *source_base;
 
-                       source_base = remove_level_from_path(tl->source_path);
+                       source_base = remove_level_from_path(tl->fd->path);
                        if (stat_utf8(source_base, &st))
                                {
-                               cache_ensure_dir_exists(base_path, st.st_mode);
+                               recursive_mkdir_if_not_exists(base_path, st.st_mode);
                                }
                        g_free(source_base);
                        }
                }
        else
                {
-               cache_ensure_dir_exists(base_path, THUMB_PERMS_FOLDER);
+               recursive_mkdir_if_not_exists(base_path, THUMB_PERMS_FOLDER);
                }
        g_free(base_path);
 
-       if (debug)
-               {
-               printf("thumb saving: %s\n", tl->source_path);
-               printf("       saved: %s\n", tl->thumb_path);
-               }
+       DEBUG_1("thumb saving: %s", tl->fd->path);
+       DEBUG_1("       saved: %s", tl->thumb_path);
 
        /* save thumb, using a temp file then renaming into place */
        tmp_path = unique_filename(tl->thumb_path, ".tmp", "_", 2);
@@ -387,11 +333,11 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
                gchar *mark_app;
                gchar *mark_mtime;
                gchar *pathl;
-               gint success;
+               gboolean success;
 
                mark_uri = (tl->cache_local) ? tl->local_uri :tl->thumb_uri;
 
-               mark_app = g_strdup_printf("GQview %s", VERSION);
+               mark_app = g_strdup_printf("%s %s", GQ_APPNAME, VERSION);
                mark_mtime = g_strdup_printf("%lu", tl->source_mtime);
 
                pathl = path_from_utf8(tmp_path);
@@ -412,10 +358,10 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
                g_free(mark_app);
 
                g_free(tmp_path);
-               if (!success && debug)
+               if (!success)
                        {
-                       printf("thumb save failed: %s\n", tl->source_path);
-                       printf("            thumb: %s\n", tl->thumb_path);
+                       DEBUG_1("thumb save failed: %s", tl->fd->path);
+                       DEBUG_1("            thumb: %s", tl->thumb_path);
                        }
 
                }
@@ -423,77 +369,94 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
        g_object_unref(G_OBJECT(pixbuf));
 }
 
-static gint thumb_loader_std_scale_aspect(gint req_w, gint req_h, gint old_w, gint old_h,
-                                         gint *new_w, gint *new_h)
+static void thumb_loader_std_set_fallback(ThumbLoaderStd *tl)
 {
-       if (((gdouble)req_w / old_w) < ((gdouble)req_h / old_h))
-               {
-               *new_w = req_w;
-               *new_h = (gdouble)*new_w / old_w * old_h;
-               if (*new_h < 1) *new_h = 1;
-               }
-       else
-               {
-               *new_h = req_h;
-               *new_w = (gdouble)*new_h / old_h * old_w;
-               if (*new_w < 1) *new_w = 1;
-               }
-
-       return (*new_w != old_w || *new_h != old_h);
+       if (tl->fd->thumb_pixbuf) g_object_unref(tl->fd->thumb_pixbuf);
+       tl->fd->thumb_pixbuf = pixbuf_fallback(tl->fd, tl->requested_width, tl->requested_height);
 }
 
-static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf, gint shrunk)
+static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf, gboolean shrunk)
 {
        GdkPixbuf *pixbuf_thumb = NULL;
        GdkPixbuf *result;
+       GdkPixbuf *rotated = NULL;
        gint sw, sh;
 
-       sw = gdk_pixbuf_get_width(pixbuf);
-       sh = gdk_pixbuf_get_height(pixbuf);
 
-       if (tl->cache_enable && !tl->cache_hit &&
-           (sw >= THUMB_SIZE_NORMAL || sh >= THUMB_SIZE_NORMAL || shrunk))
+       if (!tl->cache_hit && options->image.exif_rotate_enable)
                {
-               gint cache_w, cache_h;
-               gint thumb_w, thumb_h;
-
-               if (tl->requested_width > THUMB_SIZE_NORMAL || tl->requested_height > THUMB_SIZE_NORMAL)
+               if (!tl->fd->exif_orientation)
                        {
-                       cache_w = cache_h = THUMB_SIZE_LARGE;
+                       tl->fd->exif_orientation = metadata_read_int(tl->fd, ORIENTATION_KEY, EXIF_ORIENTATION_TOP_LEFT);
                        }
-               else
+               
+               if (tl->fd->exif_orientation != EXIF_ORIENTATION_TOP_LEFT)
                        {
-                       cache_w = cache_h = THUMB_SIZE_NORMAL;
+                       rotated = pixbuf_apply_orientation(pixbuf, tl->fd->exif_orientation);
+                       pixbuf = rotated;
                        }
+               }
 
-               if (thumb_loader_std_scale_aspect(cache_w, cache_h, sw, sh,
-                                                 &thumb_w, &thumb_h))
+       sw = gdk_pixbuf_get_width(pixbuf);
+       sh = gdk_pixbuf_get_height(pixbuf);
+
+       if (tl->cache_enable)
+               {
+               if (!tl->cache_hit)
                        {
-                       pixbuf_thumb = gdk_pixbuf_scale_simple(pixbuf, thumb_w, thumb_h,
-                                                              (GdkInterpType)thumbnail_quality);
+                       gint cache_w, cache_h;
+
+                       if (tl->requested_width > THUMB_SIZE_NORMAL || tl->requested_height > THUMB_SIZE_NORMAL)
+                               {
+                               cache_w = cache_h = THUMB_SIZE_LARGE;
+                               }
+                       else
+                               {
+                               cache_w = cache_h = THUMB_SIZE_NORMAL;
+                               }
+
+                       if (sw > cache_w || sh > cache_h || shrunk)
+                               {
+                               gint thumb_w, thumb_h;
+                               struct stat st;
+
+                               if (pixbuf_scale_aspect(cache_w, cache_h, sw, sh,
+                                                                 &thumb_w, &thumb_h))
+                                       {
+                                       pixbuf_thumb = gdk_pixbuf_scale_simple(pixbuf, thumb_w, thumb_h,
+                                                                              (GdkInterpType)options->thumbnails.quality);
+                                       }
+                               else
+                                       {
+                                       pixbuf_thumb = pixbuf;
+                                       g_object_ref(G_OBJECT(pixbuf_thumb));
+                                       }
+
+                               /* do not save the thumbnail if the source file has changed meanwhile -
+                                  the thumbnail is most probably broken */
+                               if (stat_utf8(tl->fd->path, &st) &&
+                                   tl->source_mtime == st.st_mtime &&
+                                   tl->source_size == st.st_size)
+                                       {
+                                       thumb_loader_std_save(tl, pixbuf_thumb);
+                                       }
+                               }
                        }
-               else
+               else if (tl->cache_hit &&
+                        tl->cache_local && !tl->thumb_path_local)
                        {
-                       pixbuf_thumb = pixbuf;
-                       g_object_ref(G_OBJECT(pixbuf_thumb));
-                       }
-               
-               thumb_loader_std_save(tl, pixbuf_thumb);
-               }
-       else if (tl->cache_enable && tl->cache_local &&
-                tl->cache_hit && !tl->thumb_path_local)
-               {
-               /* A local cache save was requested, but a valid thumb is in $HOME,
-                * so specifically save as a local thumbnail.
-                */
-               g_free(tl->thumb_path);
-               tl->thumb_path = NULL;
+                       /* A local cache save was requested, but a valid thumb is in $HOME,
+                        * so specifically save as a local thumbnail.
+                        */
+                       g_free(tl->thumb_path);
+                       tl->thumb_path = NULL;
 
-               tl->cache_hit = FALSE;
+                       tl->cache_hit = FALSE;
 
-               if (debug) printf("thumb copied: %s\n", tl->source_path);
+                       DEBUG_1("thumb copied: %s", tl->fd->path);
 
-               thumb_loader_std_save(tl, pixbuf);
+                       thumb_loader_std_save(tl, pixbuf);
+                       }
                }
 
        if (sw <= tl->requested_width && sh <= tl->requested_height)
@@ -512,11 +475,11 @@ static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf,
                        sh = gdk_pixbuf_get_height(pixbuf);
                        }
 
-               if (thumb_loader_std_scale_aspect(tl->requested_width, tl->requested_height, sw, sh,
+               if (pixbuf_scale_aspect(tl->requested_width, tl->requested_height, sw, sh,
                                                  &thumb_w, &thumb_h))
                        {
                        result = gdk_pixbuf_scale_simple(pixbuf, thumb_w, thumb_h,
-                                                        (GdkInterpType)thumbnail_quality);
+                                                        (GdkInterpType)options->thumbnails.quality);
                        }
                else
                        {
@@ -526,11 +489,12 @@ static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf,
                }
 
        if (pixbuf_thumb) g_object_unref(pixbuf_thumb);
+       if (rotated) g_object_unref(rotated);
 
        return result;
 }
 
-static gint thumb_loader_std_next_source(ThumbLoaderStd *tl, gint remove_broken)
+static gboolean thumb_loader_std_next_source(ThumbLoaderStd *tl, gboolean remove_broken)
 {
        image_loader_free(tl->il);
        tl->il = NULL;
@@ -539,7 +503,7 @@ static gint thumb_loader_std_next_source(ThumbLoaderStd *tl, gint remove_broken)
                {
                if (!tl->thumb_path_local && remove_broken)
                        {
-                       if (debug) printf("thumb broken, unlinking: %s\n", tl->thumb_path);
+                       DEBUG_1("thumb broken, unlinking: %s", tl->thumb_path);
                        unlink_file(tl->thumb_path);
                        }
 
@@ -549,17 +513,23 @@ static gint thumb_loader_std_next_source(ThumbLoaderStd *tl, gint remove_broken)
                if (!tl->thumb_path_local)
                        {
                        tl->thumb_path = thumb_loader_std_cache_path(tl, TRUE, NULL, FALSE);
-                       if (isfile(tl->thumb_path) && thumb_loader_std_setup(tl, tl->thumb_path))
+                       if (isfile(tl->thumb_path))
                                {
-                               tl->thumb_path_local = TRUE;
-                               return TRUE;
+                               FileData *fd = file_data_new_no_grouping(tl->thumb_path);
+                               if (thumb_loader_std_setup(tl, fd))
+                                       {
+                                       file_data_unref(fd);
+                                       tl->thumb_path_local = TRUE;
+                                       return TRUE;
+                                       }
+                               file_data_unref(fd);
                                }
 
                        g_free(tl->thumb_path);
                        tl->thumb_path = NULL;
                        }
 
-               if (thumb_loader_std_setup(tl, tl->source_path)) return TRUE;
+               if (thumb_loader_std_setup(tl, tl->fd)) return TRUE;
                }
 
        thumb_loader_std_save(tl, NULL);
@@ -571,16 +541,13 @@ static void thumb_loader_std_done_cb(ImageLoader *il, gpointer data)
        ThumbLoaderStd *tl = data;
        GdkPixbuf *pixbuf;
 
-       if (debug)
-               {
-               printf("thumb image done: %s\n", tl->source_path);
-               printf("            from: %s\n", tl->il->path);
-               }
+       DEBUG_1("thumb image done: %s", tl->fd ? tl->fd->path : "???");
+       DEBUG_1("            from: %s", image_loader_get_fd(tl->il)->path);
 
        pixbuf = image_loader_get_pixbuf(tl->il);
        if (!pixbuf)
                {
-               if (debug) printf("...but no pixbuf\n");
+               DEBUG_1("...but no pixbuf");
                thumb_loader_std_error_cb(il, data);
                return;
                }
@@ -595,7 +562,11 @@ static void thumb_loader_std_done_cb(ImageLoader *il, gpointer data)
 
        tl->cache_hit = (tl->thumb_path != NULL);
 
-       tl->pixbuf = thumb_loader_std_finish(tl, pixbuf, il->shrunk);
+       if (tl->fd)
+               {
+               if (tl->fd->thumb_pixbuf) g_object_unref(tl->fd->thumb_pixbuf);
+               tl->fd->thumb_pixbuf = thumb_loader_std_finish(tl, pixbuf, image_loader_get_shrunk(il));
+               }
 
        if (tl->func_done) tl->func_done(tl, tl->data);
 }
@@ -610,15 +581,14 @@ static void thumb_loader_std_error_cb(ImageLoader *il, gpointer data)
                thumb_loader_std_done_cb(il, data);
                return;
                }
-
-       if (debug)
-               {
-               printf("thumb image error: %s\n", tl->source_path);
-               printf("             from: %s\n", tl->il->path);
-               }
+       
+       DEBUG_1("thumb image error: %s", tl->fd->path);
+       DEBUG_1("             from: %s", image_loader_get_fd(tl->il)->path);
 
        if (thumb_loader_std_next_source(tl, TRUE)) return;
 
+       thumb_loader_std_set_fallback(tl);
+       
        if (tl->func_error) tl->func_error(tl, tl->data);
 }
 
@@ -631,31 +601,30 @@ static void thumb_loader_std_progress_cb(ImageLoader *il, gdouble percent, gpoin
        if (tl->func_progress) tl->func_progress(tl, tl->data);
 }
 
-static gint thumb_loader_std_setup(ThumbLoaderStd *tl, const gchar *path)
+static gboolean thumb_loader_std_setup(ThumbLoaderStd *tl, FileData *fd)
 {
-       tl->il = image_loader_new(path);
+       tl->il = image_loader_new(fd);
+       image_loader_set_priority(tl->il, G_PRIORITY_LOW);
 
-       if (thumbnail_fast)
+       /* this will speed up jpegs by up to 3x in some cases */
+       if (tl->requested_width <= THUMB_SIZE_NORMAL &&
+           tl->requested_height <= THUMB_SIZE_NORMAL)
                {
-               /* this will speed up jpegs by up to 3x in some cases */
-               if (tl->requested_width <= THUMB_SIZE_NORMAL &&
-                   tl->requested_height <= THUMB_SIZE_NORMAL)
-                       {
-                       image_loader_set_requested_size(tl->il, THUMB_SIZE_NORMAL, THUMB_SIZE_NORMAL);
-                       }
-               else
-                       {
-                       image_loader_set_requested_size(tl->il, THUMB_SIZE_LARGE, THUMB_SIZE_LARGE);
-                       }
+               image_loader_set_requested_size(tl->il, THUMB_SIZE_NORMAL, THUMB_SIZE_NORMAL);
+               }
+       else
+               {
+               image_loader_set_requested_size(tl->il, THUMB_SIZE_LARGE, THUMB_SIZE_LARGE);
                }
 
-       image_loader_set_error_func(tl->il, thumb_loader_std_error_cb, tl);
+       g_signal_connect(G_OBJECT(tl->il), "error", (GCallback)thumb_loader_std_error_cb, tl);
        if (tl->func_progress)
                {
-               image_loader_set_percent_func(tl->il, thumb_loader_std_progress_cb, tl);
+               g_signal_connect(G_OBJECT(tl->il), "percent", (GCallback)thumb_loader_std_progress_cb, tl);
                }
+       g_signal_connect(G_OBJECT(tl->il), "done", (GCallback)thumb_loader_std_done_cb, tl);
 
-       if (image_loader_start(tl->il, thumb_loader_std_done_cb, tl))
+       if (image_loader_start(tl->il))
                {
                return TRUE;
                }
@@ -669,7 +638,7 @@ static gint thumb_loader_std_setup(ThumbLoaderStd *tl, const gchar *path)
  * Note: Currently local_cache only specifies where to save a _new_ thumb, if
  *       a valid existing thumb is found anywhere the local thumb will not be created.
  */
-void thumb_loader_std_set_cache(ThumbLoaderStd *tl, gint enable_cache, gint local, gint retry_failed)
+void thumb_loader_std_set_cache(ThumbLoaderStd *tl, gboolean enable_cache, gboolean local, gboolean retry_failed)
 {
        if (!tl) return;
 
@@ -678,28 +647,32 @@ void thumb_loader_std_set_cache(ThumbLoaderStd *tl, gint enable_cache, gint loca
        tl->cache_retry = retry_failed;
 }
 
-gint thumb_loader_std_start(ThumbLoaderStd *tl, const gchar *path)
+gboolean thumb_loader_std_start(ThumbLoaderStd *tl, FileData *fd)
 {
        static gchar *thumb_cache = NULL;
        struct stat st;
 
-       if (!tl || !path) return FALSE;
+       if (!tl || !fd) return FALSE;
 
        thumb_loader_std_reset(tl);
 
-       if (!stat_utf8(path, &st)) return FALSE;
 
-       tl->source_path = g_strdup(path);
+       tl->fd = file_data_ref(fd);
+       if (!stat_utf8(fd->path, &st))
+               {
+               thumb_loader_std_set_fallback(tl);
+               return FALSE;
+               }
        tl->source_mtime = st.st_mtime;
        tl->source_size = st.st_size;
        tl->source_mode = st.st_mode;
 
-       if (!thumb_cache) thumb_cache = g_strconcat(homedir(), "/", THUMB_FOLDER, NULL);
-       if (strncmp(tl->source_path, thumb_cache, strlen(thumb_cache)) != 0)
+       if (!thumb_cache) thumb_cache = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, NULL);
+       if (strncmp(tl->fd->path, thumb_cache, strlen(thumb_cache)) != 0)
                {
                gchar *pathl;
 
-               pathl = path_from_utf8(path);
+               pathl = path_from_utf8(fd->path);
                tl->thumb_uri = g_filename_to_uri(pathl, NULL, NULL);
                tl->local_uri = filename_from_path(tl->thumb_uri);
                g_free(pathl);
@@ -709,20 +682,34 @@ gint thumb_loader_std_start(ThumbLoaderStd *tl, const gchar *path)
                {
                gint found;
 
-               if (thumb_loader_std_fail_check(tl)) return FALSE;
-
                tl->thumb_path = thumb_loader_std_cache_path(tl, FALSE, NULL, FALSE);
                tl->thumb_path_local = FALSE;
 
                found = isfile(tl->thumb_path);
-               if (found && thumb_loader_std_setup(tl, tl->thumb_path)) return TRUE;
+               if (found)
+                       {
+                       FileData *fd = file_data_new_no_grouping(tl->thumb_path);
+                       if (thumb_loader_std_setup(tl, fd))
+                               {
+                               file_data_unref(fd);
+                               return TRUE;
+                               }
+                       file_data_unref(fd);
+                       }
 
-               return thumb_loader_std_next_source(tl, found);
+               if (thumb_loader_std_fail_check(tl) ||
+                   !thumb_loader_std_next_source(tl, found))
+                       {
+                       thumb_loader_std_set_fallback(tl);
+                       return FALSE;
+                       }
+               return TRUE;
                }
 
-       if (!thumb_loader_std_setup(tl, tl->source_path))
+       if (!thumb_loader_std_setup(tl, tl->fd))
                {
                thumb_loader_std_save(tl, NULL);
+               thumb_loader_std_set_fallback(tl);
                return FALSE;
                }
 
@@ -737,41 +724,18 @@ void thumb_loader_std_free(ThumbLoaderStd *tl)
        g_free(tl);
 }
 
-GdkPixbuf *thumb_loader_std_get_pixbuf(ThumbLoaderStd *tl, gint with_fallback)
+GdkPixbuf *thumb_loader_std_get_pixbuf(ThumbLoaderStd *tl)
 {
        GdkPixbuf *pixbuf;
 
-       if (tl && tl->pixbuf)
+       if (tl && tl->fd && tl->fd->thumb_pixbuf)
                {
-               pixbuf = tl->pixbuf;
+               pixbuf = tl->fd->thumb_pixbuf;
                g_object_ref(pixbuf);
                }
-       else if (with_fallback)
-               {
-               gint w, h;
-
-               pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
-               w = gdk_pixbuf_get_width(pixbuf);
-               h = gdk_pixbuf_get_height(pixbuf);
-
-               if (w > tl->requested_width || h > tl->requested_height)
-                       {
-                       gint nw, nh;
-
-                       if (thumb_loader_std_scale_aspect(tl->requested_width, tl->requested_height,
-                                                         w, h, &nw, &nh))
-                               {
-                               GdkPixbuf *tmp;
-
-                               tmp = pixbuf;
-                               pixbuf = gdk_pixbuf_scale_simple(tmp, nw, nh, GDK_INTERP_TILES);
-                               g_object_unref(G_OBJECT(tmp));
-                               }
-                       }
-               }
        else
                {
-               pixbuf = NULL;
+               pixbuf = pixbuf_fallback(NULL, tl->requested_width, tl->requested_height);
                }
 
        return pixbuf;
@@ -785,10 +749,10 @@ struct _ThumbValidate
        gchar *path;
        gint days;
 
-       void (*func_valid)(const gchar *path, gint valid, gpointer data);
+       void (*func_valid)(const gchar *path, gboolean valid, gpointer data);
        gpointer data;
 
-       gint idle_id;
+       guint idle_id; /* event source id */
 };
 
 static void thumb_loader_std_thumb_file_validate_free(ThumbValidate *tv)
@@ -806,13 +770,16 @@ void thumb_loader_std_thumb_file_validate_cancel(ThumbLoaderStd *tl)
 
        tv = tl->data;
 
-       if (tv->idle_id != -1) g_source_remove(tv->idle_id);
-       tv->idle_id = -1;
+       if (tv->idle_id)
+               {
+               g_source_remove(tv->idle_id);
+               tv->idle_id = 0;
+               }
 
        thumb_loader_std_thumb_file_validate_free(tv);
 }
 
-static void thumb_loader_std_thumb_file_validate_finish(ThumbValidate *tv, gint valid)
+static void thumb_loader_std_thumb_file_validate_finish(ThumbValidate *tv, gboolean valid)
 {
        if (tv->func_valid) tv->func_valid(tv->path, valid, tv->data);
 
@@ -823,9 +790,11 @@ static void thumb_loader_std_thumb_file_validate_done_cb(ThumbLoaderStd *tl, gpo
 {
        ThumbValidate *tv = data;
        GdkPixbuf *pixbuf;
-       gint valid = FALSE;
+       gboolean valid = FALSE;
 
-       pixbuf = thumb_loader_std_get_pixbuf(tv->tl, FALSE);
+       /* get the original thumbnail pixbuf (unrotated, with original options)
+          this is called from image_loader done callback, so tv->tl->il must exist*/
+       pixbuf = image_loader_get_pixbuf(tv->tl->il);
        if (pixbuf)
                {
                const gchar *uri;
@@ -852,7 +821,7 @@ static void thumb_loader_std_thumb_file_validate_done_cb(ThumbLoaderStd *tl, gpo
                                {
                                struct stat st;
 
-                               if (debug) printf("thumb uri foreign, doing day check: %s\n", uri);
+                               DEBUG_1("thumb uri foreign, doing day check: %s", uri);
 
                                if (stat_utf8(tv->path, &st))
                                        {
@@ -866,8 +835,10 @@ static void thumb_loader_std_thumb_file_validate_done_cb(ThumbLoaderStd *tl, gpo
                                        }
                                }
                        }
-
-               g_object_unref(pixbuf);
+               else
+                       {
+                       DEBUG_1("invalid image found in std cache: %s", tv->path);
+                       }
                }
 
        thumb_loader_std_thumb_file_validate_finish(tv, valid);
@@ -880,18 +851,18 @@ static void thumb_loader_std_thumb_file_validate_error_cb(ThumbLoaderStd *tl, gp
        thumb_loader_std_thumb_file_validate_finish(tv, FALSE);
 }
 
-static gint thumb_loader_std_thumb_file_validate_idle_cb(gpointer data)
+static gboolean thumb_loader_std_thumb_file_validate_idle_cb(gpointer data)
 {
        ThumbValidate *tv = data;
 
-       tv->idle_id = -1;
+       tv->idle_id = 0;
        thumb_loader_std_thumb_file_validate_finish(tv, FALSE);
 
        return FALSE;
 }
 
 ThumbLoaderStd *thumb_loader_std_thumb_file_validate(const gchar *thumb_path, gint allowed_days,
-                                                    void (*func_valid)(const gchar *path, gint valid, gpointer data),
+                                                    void (*func_valid)(const gchar *path, gboolean valid, gpointer data),
                                                     gpointer data)
 {
        ThumbValidate *tv;
@@ -911,19 +882,21 @@ ThumbLoaderStd *thumb_loader_std_thumb_file_validate(const gchar *thumb_path, gi
        tv->func_valid = func_valid;
        tv->data = data;
 
-       if (!thumb_loader_std_setup(tv->tl, thumb_path))
+       FileData *fd = file_data_new_no_grouping(thumb_path);
+       if (!thumb_loader_std_setup(tv->tl, fd))
                {
                tv->idle_id = g_idle_add(thumb_loader_std_thumb_file_validate_idle_cb, tv);
                }
        else
                {
-               tv->idle_id = -1;
+               tv->idle_id = 0;
                }
 
+       file_data_unref(fd);
        return tv->tl;
 }
 
-static void thumb_std_maint_remove_one(const gchar *source, const gchar *uri, gint local,
+static void thumb_std_maint_remove_one(const gchar *source, const gchar *uri, gboolean local,
                                       const gchar *subfolder)
 {
        gchar *thumb_path;
@@ -933,7 +906,7 @@ static void thumb_std_maint_remove_one(const gchar *source, const gchar *uri, gi
                                          local, subfolder);
        if (isfile(thumb_path))
                {
-               if (debug) printf("thumb removing: %s\n", thumb_path);
+               DEBUG_1("thumb removing: %s", thumb_path);
                unlink_file(thumb_path);
                }
        g_free(thumb_path);
@@ -978,15 +951,17 @@ static GList *thumb_std_maint_move_tail = NULL;
 
 
 static void thumb_std_maint_move_step(TMaintMove *tm);
-static gint thumb_std_maint_move_idle(gpointer data);
+static gboolean thumb_std_maint_move_idle(gpointer data);
 
 
-static void thumb_std_maint_move_validate_cb(const gchar *path, gint valid, gpointer data)
+static void thumb_std_maint_move_validate_cb(const gchar *path, gboolean valid, gpointer data)
 {
        TMaintMove *tm = data;
        GdkPixbuf *pixbuf;
 
-       pixbuf = thumb_loader_std_get_pixbuf(tm->tl, FALSE);
+       /* get the original thumbnail pixbuf (unrotated, with original options)
+          this is called from image_loader done callback, so tm->tl->il must exist*/
+       pixbuf = image_loader_get_pixbuf(tm->tl->il);
        if (pixbuf)
                {
                const gchar *uri;
@@ -1007,12 +982,11 @@ static void thumb_std_maint_move_validate_cb(const gchar *path, gint valid, gpoi
                        tm->tl->cache_enable = TRUE;
                        tm->tl->cache_hit = FALSE;
                        tm->tl->cache_local = FALSE;
-
-                       g_free(tm->tl->source_path);
-                       tm->tl->source_path = g_strdup(tm->dest);
+                       file_data_unref(tm->tl->fd);
+                       tm->tl->fd = file_data_new_group(tm->dest);
                        tm->tl->source_mtime = strtol(mtime_str, NULL, 10);
 
-                       pathl = path_from_utf8(tm->tl->source_path);
+                       pathl = path_from_utf8(tm->tl->fd->path);
                        g_free(tm->tl->thumb_uri);
                        tm->tl->thumb_uri = g_filename_to_uri(pathl, NULL, NULL);
                        tm->tl->local_uri = filename_from_path(tm->tl->thumb_uri);
@@ -1022,12 +996,12 @@ static void thumb_std_maint_move_validate_cb(const gchar *path, gint valid, gpoi
                        tm->tl->thumb_path = NULL;
                        tm->tl->thumb_path_local = FALSE;
 
-                       if (debug) printf("thumb move attempting save:\n");
+                       DEBUG_1("thumb move attempting save:");
 
                        thumb_loader_std_save(tm->tl, pixbuf);
                        }
 
-               if (debug) printf("thumb move unlink: %s\n", tm->thumb_path);
+               DEBUG_1("thumb move unlink: %s", tm->thumb_path);
                unlink_file(tm->thumb_path);
                }
 
@@ -1056,14 +1030,14 @@ static void thumb_std_maint_move_step(TMaintMove *tm)
                }
 
        folder = (tm->pass == 1) ? THUMB_FOLDER_NORMAL : THUMB_FOLDER_LARGE;
-       
+
        g_free(tm->thumb_path);
        tm->thumb_path = thumb_std_cache_path(tm->source, tm->source_uri, FALSE, folder);
        tm->tl = thumb_loader_std_thumb_file_validate(tm->thumb_path, 0,
                                                      thumb_std_maint_move_validate_cb, tm);
 }
 
-static gint thumb_std_maint_move_idle(gpointer data)
+static gboolean thumb_std_maint_move_idle(gpointer data)
 {
        TMaintMove *tm;
        gchar *pathl;
@@ -1112,7 +1086,7 @@ void thumb_std_maint_moved(const gchar *source, const gchar *dest)
 
        if (thumb_std_maint_move_tail)
                {
-               g_list_append(thumb_std_maint_move_tail, tm);
+               thumb_std_maint_move_tail = g_list_append(thumb_std_maint_move_tail, tm);
                thumb_std_maint_move_tail = thumb_std_maint_move_tail->next;
                }
        else
@@ -1121,5 +1095,4 @@ void thumb_std_maint_moved(const gchar *source, const gchar *dest)
                thumb_std_maint_move_tail = thumb_std_maint_move_list;
                }
 }
-
-
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */