Wed Apr 13 18:16:14 2005 John Ellis <johne@verizon.net>
authorJohn Ellis <johne@verizon.net>
Wed, 13 Apr 2005 22:29:53 +0000 (22:29 +0000)
committerJohn Ellis <johne@verizon.net>
Wed, 13 Apr 2005 22:29:53 +0000 (22:29 +0000)
        * cache-loader.[ch]: New utility to load cache-able data.
        * cache.[ch]: Add embedded (exif) date caching.
        * pan-view.c: Use new cache loading mechanism. Add exif date support
        to timeline and calendar view.
        * src/Makefile.am: Add cache-loader.[c,h].

##### Note: GQview CVS on sourceforge is not always up to date, please use #####
##### an offical release when making enhancements and translation updates. #####

ChangeLog
TODO
src/Makefile.am
src/cache-loader.c [new file with mode: 0644]
src/cache-loader.h [new file with mode: 0644]
src/cache.c
src/cache.h
src/pan-view.c

index 70ba891..7cc7bc5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Wed Apr 13 18:16:14 2005  John Ellis  <johne@verizon.net>
+
+       * cache-loader.[ch]: New utility to load cache-able data.
+       * cache.[ch]: Add embedded (exif) date caching.
+       * pan-view.c: Use new cache loading mechanism. Add exif date support
+       to timeline and calendar view.
+       * src/Makefile.am: Add cache-loader.[c,h].
+
 Tue Apr 12 07:59:20 2005  John Ellis  <johne@verizon.net>
 
        * pan-view.c: Use mostly neutral (gray) colors in the pan view to avoid
diff --git a/TODO b/TODO
index 772164b..9db50cc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,31 +5,22 @@ Major:
 ----------------------------------------------
 
  > pixbuf-renderer.c:
-  d> fix two pass render from corrupting it->qd pointer (need one pointer for each queue?).
-  d> fix image_change_from_image (to do this need a pixbuf_renderer_move_image).
-  d> fix broken zoom out drawing when using source tiles.
-  d> fix 2pass zoom when using source tiles and zoomed out (not always rendering second pass)
-  d> pixbuf_renderer_move should be clearing the tiles of the source image since they are no longer
-     valid and are then wasting memory (for example when full screen is active).
+   > tile dispose order is slightly incorrect, furthest ones from current position should be dropped first
+
 
  > image.c:
-  d> need to keep a list of ImageWindows and provide function to sync options to each object.
   d> test and probably fix delay_flip.
       > UPDATE: works as before (pre pixbuf-renderer), but should be fixed to provide a single redraw
         by pre-rendering any scaled tiles that are visible before signaling 'render_complete'.
    > make this a g_object with signals for completed, changed, etc.
-  d> fix region computation rounding when updating scaled image in 'area ready' signal.
    > fix delay flip 'completed' signal (full screen busy cursor is not always turned off)
-  d> fix slow loading of images when zoomed out - bug could be poor clamping to visible in PixbufRenderer.
+
+ > cache-load.c:
+   > should honor enable_thumbnails setting
 
 
  > work on pan view:
    > Pick a better keyboard shortcut than Control + J :)
-  d> Fix occasional redraw bugs when zoomed out.
-  d> Fix occasional odd requests for non-visible tiles when zoomed out (related to above?).
-  d> Fix slowness in image.c with huge grid size by changing use of pre-allocated tile array
-     to on-demand tile allocation (can this be implemented like source tiles?).
-     OR: use an array so that we do not need to walk a GList of pre-allocated tile containers
   w> Fix search scrolling to try to center image and info popup.
    > Fix info popup location to opposing horizontal side when near edge of grid.
    > Find something to do with middle mouse clicks.
@@ -37,12 +28,16 @@ Major:
    > search should highlight all matching images (with toggle?)
    > should non-thumbnail images have a drop shadow?
   d> optimize pixbuf_draw_line (line endpoints should clip to draw region before draw loop)
+     > introduced bug (looks like clamp or rounding error)
   d> optimize pixbuf_draw_triangle
+     > introduced bug (round error?)
    > does new pixbuf_draw_triangle contain line edge rounding error?
-  d> move pixbuf_draw_* stuff into pixbuf_util.c
+   > check ref counting of image when redrawing after finish loading
+   > speed up sorting image.. stage when sorting/merging cache list
+
 
    > time line view:
-     > allow use of file date or EXIF (embedded) date.
+    w> allow use of file date or EXIF (embedded) date.
      > allow horizontal _or_ vertical orientation.
 
    > calendar view:
index 647d9cb..e33c1f7 100644 (file)
@@ -60,6 +60,8 @@ gqview_SOURCES = \
        bar_sort.h      \
        cache.c         \
        cache.h         \
+       cache-loader.c  \
+       cache-loader.h  \
        cache_maint.c   \
        cache_maint.h   \
        cellrenderericon.c      \
diff --git a/src/cache-loader.c b/src/cache-loader.c
new file mode 100644 (file)
index 0000000..05c4e46
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * GQview
+ * (C) 2005 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+#include "gqview.h"
+#include "cache-loader.h"
+
+#include "exif.h"
+#include "md5-util.h"
+#include "ui_fileops.h"
+
+
+static gboolean cache_loader_process(CacheLoader *cl);
+
+
+static void cache_loader_done_cb(ImageLoader *il, gpointer data)
+{
+       CacheLoader *cl = data;
+
+       cache_loader_process(cl);
+}
+
+static void cache_loader_error_cb(ImageLoader *il, gpointer data)
+{
+       CacheLoader *cl = data;
+
+       cl->error = TRUE;
+       cache_loader_done_cb(il, data);
+}
+
+static gboolean cache_loader_process(CacheLoader *cl)
+{
+       if (cl->todo_mask & CACHE_LOADER_SIMILARITY &&
+           !cl->cd->similarity)
+               {
+               GdkPixbuf *pixbuf;
+
+               if (!cl->il && !cl->error)
+                       {
+                       cl->il = image_loader_new(cl->path);
+                       image_loader_set_error_func(cl->il, cache_loader_error_cb, cl);
+                       if (image_loader_start(cl->il, cache_loader_done_cb, cl))
+                               {
+                               return FALSE;
+                               }
+
+                       cl->error = TRUE;
+                       }
+
+               pixbuf = image_loader_get_pixbuf(cl->il);
+               if (pixbuf)
+                       {
+                       if (!cl->error)
+                               {
+                               ImageSimilarityData *sim;
+
+                               sim = image_sim_new_from_pixbuf(pixbuf);
+                               cache_sim_data_set_similarity(cl->cd, sim);
+                               image_sim_free(sim);
+
+                               cl->done_mask |= CACHE_LOADER_SIMILARITY;
+                               }
+
+                       /* we have the dimensions via pixbuf */
+                       if (!cl->cd->dimensions)
+                               {
+                               cache_sim_data_set_dimensions(cl->cd, gdk_pixbuf_get_width(pixbuf),
+                                                                     gdk_pixbuf_get_height(pixbuf));
+                               if (cl->todo_mask & CACHE_LOADER_DIMENSIONS)
+                                       {
+                                       cl->todo_mask &= ~CACHE_LOADER_DIMENSIONS;
+                                       cl->done_mask |= CACHE_LOADER_DIMENSIONS;
+                                       }
+                               }
+                       }
+
+               image_loader_free(cl->il);
+               cl->il = NULL;
+
+               cl->todo_mask &= ~CACHE_LOADER_SIMILARITY;
+               }
+       else if (cl->todo_mask & CACHE_LOADER_DIMENSIONS &&
+                !cl->cd->dimensions)
+               {
+               if (!cl->error &&
+                   image_load_dimensions(cl->path, &cl->cd->width, &cl->cd->height))
+                       {
+                       cl->cd->dimensions = TRUE;
+                       cl->done_mask |= CACHE_LOADER_DIMENSIONS;
+                       }
+               else
+                       {
+                       cl->error = TRUE;
+                       }
+
+               cl->todo_mask &= ~CACHE_LOADER_DIMENSIONS;
+               }
+       else if (cl->todo_mask & CACHE_LOADER_MD5SUM &&
+                !cl->cd->have_md5sum)
+               {
+               if (md5_get_digest_from_file_utf8(cl->path, cl->cd->md5sum))
+                       {
+                       cl->cd->have_md5sum = TRUE;
+                       cl->done_mask |= CACHE_LOADER_MD5SUM;
+                       }
+               else
+                       {
+                       cl->error = TRUE;
+                       }
+
+               cl->todo_mask &= ~CACHE_LOADER_MD5SUM;
+               }
+       else if (cl->todo_mask & CACHE_LOADER_DATE &&
+                !cl->cd->have_date)
+               {
+               time_t date = -1;
+               ExifData *exif;
+
+               exif = exif_read(cl->path);
+               if (exif)
+                       {
+                       gchar *text;
+
+                       text = exif_get_data_as_text(exif, "fDateTime");
+                       if (text)
+                               {
+                               struct tm t = { 0 };
+
+                               if (sscanf(text, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon, &t.tm_mday,
+                                          &t.tm_hour, &t.tm_min, &t.tm_sec) == 6)
+                                       {
+                                       t.tm_year -= 1900;
+                                       t.tm_mon -= 1;
+                                       date = mktime(&t);
+                                       }
+                               g_free(text);
+                               }
+                       exif_free(exif);
+                       }
+
+               cl->cd->date = date;
+               cl->cd->have_date = TRUE;
+
+               cl->done_mask |= CACHE_LOADER_DATE;
+               cl->todo_mask &= ~CACHE_LOADER_DATE;
+               }
+       else
+               {
+               /* done, save then call done function */
+               if (enable_thumb_caching &&
+                   cl->done_mask != CACHE_LOADER_NONE)
+                       {
+                       gchar *base;
+                       mode_t mode = 0755;
+
+                       base = cache_get_location(CACHE_TYPE_SIM, cl->path, FALSE, &mode);
+                       if (cache_ensure_dir_exists(base, mode))
+                               {
+                               g_free(cl->cd->path);
+                               cl->cd->path = cache_get_location(CACHE_TYPE_SIM, cl->path, TRUE, NULL);
+                               if (cache_sim_data_save(cl->cd))
+                                       {
+                                       filetime_set(cl->cd->path, filetime(cl->path));
+                                       }
+                               }
+                       g_free(base);
+                       }
+
+               cl->idle_id = -1;
+
+               if (cl->done_func)
+                       {
+                       cl->done_func(cl, cl->error, cl->done_data);
+                       }
+
+               return FALSE;
+               }
+
+       return TRUE;
+}
+
+static gboolean cache_loader_idle_cb(gpointer data)
+{
+       CacheLoader *cl = data;
+
+       return cache_loader_process(cl);
+}
+
+CacheLoader *cache_loader_new(const gchar *path, CacheDataType load_mask,
+                             CacheLoaderDoneFunc done_func, gpointer done_data)
+{
+       CacheLoader *cl;
+       gchar *found;
+
+       if (!path || !isfile(path)) return NULL;
+
+       cl = g_new0(CacheLoader, 1);
+       cl->path = g_strdup(path);
+
+       cl->done_func = done_func;
+       cl->done_data = done_data;
+
+       found = cache_find_location(CACHE_TYPE_SIM, path);
+       if (found && filetime(found) == filetime(path))
+               {
+               cl->cd = cache_sim_data_load(found);
+               }
+       g_free(found);
+
+       if (!cl->cd) cl->cd = cache_sim_data_new();
+
+       cl->todo_mask = load_mask;
+       cl->done_mask = CACHE_LOADER_NONE;
+
+       cl->il = NULL;
+       cl->idle_id = g_idle_add(cache_loader_idle_cb, cl);
+
+       cl->error = FALSE;
+
+       return cl;
+}
+
+void cache_loader_free(CacheLoader *cl)
+{
+       if (!cl) return;
+
+       if (cl->idle_id != -1)
+               {
+               g_source_remove(cl->idle_id);
+               cl->idle_id = -1;
+               }
+
+       image_loader_free(cl->il);
+       cache_sim_data_free(cl->cd);
+
+       g_free(cl->path);
+       g_free(cl);
+}
+
diff --git a/src/cache-loader.h b/src/cache-loader.h
new file mode 100644 (file)
index 0000000..6095908
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * GQview
+ * (C) 2005 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#ifndef CACHE_LOADER_H
+#define CACHE_LOADER_H
+
+
+#include "cache.h"
+#include "image-load.h"
+
+
+typedef struct _CacheLoader CacheLoader;
+
+typedef void (* CacheLoaderDoneFunc)(CacheLoader *cl, gint error, gpointer data);
+
+
+typedef enum {
+       CACHE_LOADER_NONE       = 0,
+       CACHE_LOADER_DIMENSIONS = 1 << 0,
+       CACHE_LOADER_DATE       = 1 << 1,
+       CACHE_LOADER_MD5SUM     = 1 << 2,
+       CACHE_LOADER_SIMILARITY = 1 << 3
+} CacheDataType;
+
+struct _CacheLoader {
+       gchar *path;
+       CacheData *cd;
+
+       CacheDataType todo_mask;
+       CacheDataType done_mask;
+
+       CacheLoaderDoneFunc done_func;
+       gpointer done_data;
+
+       gint error;
+
+       ImageLoader *il;
+       gint idle_id;
+};
+
+
+CacheLoader *cache_loader_new(const gchar *path, CacheDataType load_mask,
+                             CacheLoaderDoneFunc done_func, gpointer done_data);
+
+void cache_loader_free(CacheLoader *cl);
+
+
+#endif
+
+
index ef983ab..e2bb094 100644 (file)
@@ -27,6 +27,7 @@
  * SIMcache
  * #coment
  * Dimensions=[<width> x <height>]
+ * Date=[<value in time_t format, or -1 if no embedded date>]
  * Checksum=[<value>]
  * MD5sum=[<32 character ascii text digest>]
  * SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
@@ -51,8 +52,7 @@ CacheData *cache_sim_data_new(void)
        CacheData *cd;
 
        cd = g_new0(CacheData, 1);
-       cd->dimensions = FALSE;
-       cd->similarity = FALSE;
+       cd->date = -1;
 
        return cd;
 }
@@ -81,6 +81,15 @@ static gint cache_sim_write_dimensions(FILE *f, CacheData *cd)
        return TRUE;
 }
 
+static gint cache_sim_write_date(FILE *f, CacheData *cd)
+{
+       if (!f || !cd || !cd->have_date) return FALSE;
+
+       fprintf(f, "Date=[%ld]\n", cd->date);
+
+       return TRUE;
+}
+
 static gint cache_sim_write_checksum(FILE *f, CacheData *cd)
 {
        if (!f || !cd || !cd->have_checksum) return FALSE;
@@ -157,6 +166,7 @@ gint cache_sim_data_save(CacheData *cd)
 
        fprintf(f, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
        cache_sim_write_dimensions(f, cd);
+       cache_sim_write_date(f, cd);
        cache_sim_write_checksum(f, cd);
        cache_sim_write_md5sum(f, cd);
        cache_sim_write_similarity(f, cd);
@@ -234,6 +244,46 @@ static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
        return FALSE;
 }
 
+static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
+{
+       if (!f || !buf || !cd) return FALSE;
+
+       if (s < 4 || strncmp("Date", buf, 4) != 0) return FALSE;
+
+       if (fseek(f, - s, SEEK_CUR) == 0)
+               {
+               char b;
+               char buf[1024];
+               gint p = 0;
+
+               b = 'X';
+               while (b != '[')
+                       {
+                       if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
+                       }
+               while (b != ']' && p < 1023)
+                       {
+                       if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
+                       buf[p] = b;
+                       p++;
+                       }
+
+               while (b != '\n')
+                       {
+                       if (fread(&b, sizeof(b), 1, f) != 1) break;
+                       }
+
+               buf[p] = '\0';
+               cd->date = strtol(buf, NULL, 10);
+
+               cd->have_date = TRUE;
+
+               return TRUE;
+               }
+
+       return FALSE;
+}
+
 static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
@@ -414,6 +464,7 @@ CacheData *cache_sim_data_load(const gchar *path)
                        {
                        if (!cache_sim_read_comment(f, buf, s, cd) &&
                            !cache_sim_read_dimensions(f, buf, s, cd) &&
+                           !cache_sim_read_date(f, buf, s, cd) &&
                            !cache_sim_read_checksum(f, buf, s, cd) &&
                            !cache_sim_read_md5sum(f, buf, s, cd) &&
                            !cache_sim_read_similarity(f, buf, s, cd))
@@ -449,6 +500,14 @@ void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h)
        cd->dimensions = TRUE;
 }
 
+void cache_sim_data_set_date(CacheData *cd, time_t date)
+{
+       if (!cd) return;
+
+       cd->date = date;
+       cd->have_date = TRUE;
+}
+
 void cache_sim_data_set_checksum(CacheData *cd, long checksum)
 {
        if (!cd) return;
index ce7afc0..bc1c95f 100644 (file)
@@ -40,11 +40,13 @@ struct _CacheData
        gchar *path;
        gint width;
        gint height;
+       time_t date;
        long checksum;
        guchar md5sum[16];
        ImageSimilarityData *sim;
 
        gint dimensions;
+       gint have_date;
        gint have_checksum;
        gint have_md5sum;
        gint similarity;
@@ -60,6 +62,7 @@ gint cache_sim_data_save(CacheData *cd);
 CacheData *cache_sim_data_load(const gchar *path);
 
 void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h);
+void cache_sim_data_set_date(CacheData *cd, time_t date);
 void cache_sim_data_set_checksum(CacheData *cd, long checksum);
 void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16]);
 void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd);
index b1e2beb..15e5a42 100644 (file)
@@ -14,6 +14,7 @@
 #include "pan-view.h"
 
 #include "cache.h"
+#include "cache-loader.h"
 #include "dnd.h"
 #include "editors.h"
 #include "filelist.h"
 #define PAN_PREF_GROUP "pan_view_options"
 #define PAN_PREF_HIDE_WARNING "hide_performance_warning"
 
+#define SORT_BY_EXIF_DATE 1
+
 
 typedef enum {
        LAYOUT_TIMELINE = 0,
@@ -244,6 +247,7 @@ struct _PanWindow
        gint cache_count;
        gint cache_total;
        gint cache_tick;
+       CacheLoader *cache_cl;
 
        ImageLoader *il;
        ThumbLoader *tl;
@@ -279,6 +283,8 @@ static GList *pan_window_layout_list(const gchar *path, SortType sort, gint asce
 
 static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
 
+static void pan_window_layout_update_idle(PanWindow *pw);
+
 static GtkWidget *pan_popup_menu(PanWindow *pw);
 static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
 
@@ -432,6 +438,9 @@ static void pan_cache_free(PanWindow *pw)
        pw->cache_count = 0;
        pw->cache_total = 0;
        pw->cache_tick = 0;
+
+       cache_loader_free(pw->cache_cl);
+       pw->cache_cl = NULL;
 }
 
 static void pan_cache_fill(PanWindow *pw, const gchar *path)
@@ -446,17 +455,40 @@ static void pan_cache_fill(PanWindow *pw, const gchar *path)
        pw->cache_total = g_list_length(pw->cache_todo);
 }
 
+static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
+{
+       PanWindow *pw = data;
+
+       if (pw->cache_list)
+               {
+               PanCacheData *pc;
+               pc = pw->cache_list->data;
+
+               if (!pc->cd)
+                       {
+                       pc->cd = cl->cd;
+                       cl->cd = NULL;
+                       }
+               }
+
+       cache_loader_free(cl);
+       pw->cache_cl = NULL;
+
+       pan_window_layout_update_idle(pw);
+}
+
 static gint pan_cache_step(PanWindow *pw)
 {
        FileData *fd;
        PanCacheData *pc;
-       CacheData *cd = NULL;
+       CacheDataType load_mask;
 
-       if (!pw->cache_todo) return FALSE;
+       if (!pw->cache_todo) return TRUE;
 
        fd = pw->cache_todo->data;
        pw->cache_todo = g_list_remove(pw->cache_todo, fd);
 
+#if 0
        if (enable_thumb_caching)
                {
                gchar *found;
@@ -495,16 +527,68 @@ static gint pan_cache_step(PanWindow *pw)
 
                pw->cache_tick = 9;
                }
-
+#endif
        pc = g_new0(PanCacheData, 1);
        memcpy(pc, fd, sizeof(FileData));
        g_free(fd);
 
-       pc->cd = cd;
+       pc->cd = NULL;
 
        pw->cache_list = g_list_prepend(pw->cache_list, pc);
 
-       return TRUE;
+       cache_loader_free(pw->cache_cl);
+
+       load_mask = CACHE_LOADER_NONE;
+       if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
+       if (SORT_BY_EXIF_DATE) load_mask |= CACHE_LOADER_DATE;
+       pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
+                                       pan_cache_step_done_cb, pw);
+       return (pw->cache_cl == NULL);
+}
+
+static void pan_cache_sync_date(PanWindow *pw, GList *list)
+{
+       GList *haystack;
+       GList *work;
+
+       haystack = g_list_copy(pw->cache_list);
+
+       work = list;
+       while (work)
+               {
+               FileData *fd;
+               GList *needle;
+
+               fd = work->data;
+               work = work->next;
+
+               needle = haystack;
+               while (needle)
+                       {
+                       PanCacheData *pc;
+                       gchar *path;
+
+                       pc = needle->data;
+                       path = ((FileData *)pc)->path;
+                       if (path && strcmp(path, fd->path) == 0)
+                               {
+                               GList *tmp;
+
+                               if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
+                                       {
+                                       fd->date = pc->cd->date;
+                                       }
+
+                               tmp = needle;
+                               needle = needle->next;
+                               haystack = g_list_delete_link(haystack, tmp);
+                               }
+                       else
+                               {
+                               needle = needle->next;
+                               }
+                       }
+               }
 }
 
 /*
@@ -1832,6 +1916,12 @@ static void pan_window_layout_compute_calendar(PanWindow *pw, const gchar *path,
        list = pan_window_layout_list(path, SORT_NONE, TRUE);
        list = filelist_sort(list, SORT_TIME, TRUE);
 
+       if (pw->cache_list && SORT_BY_EXIF_DATE)
+               {
+               pan_cache_sync_date(pw, list);
+               list = filelist_sort(list, SORT_TIME, TRUE);
+               }
+
        day_max = 0;
        count = 0;
        tc = 0;
@@ -2047,6 +2137,12 @@ static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path,
        list = pan_window_layout_list(path, SORT_NONE, TRUE);
        list = filelist_sort(list, SORT_TIME, TRUE);
 
+       if (pw->cache_list && SORT_BY_EXIF_DATE)
+               {
+               pan_cache_sync_date(pw, list);
+               list = filelist_sort(list, SORT_TIME, TRUE);
+               }
+
        *width = PAN_FOLDER_BOX_BORDER * 2;
        *height = PAN_FOLDER_BOX_BORDER * 2;
 
@@ -2092,6 +2188,7 @@ static void pan_window_layout_compute_timeline(PanWindow *pw, const gchar *path,
                                pi = pan_item_new_text(pw, x, y, buf,
                                                       TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
                                                       PAN_TEXT_COLOR, 255);
+                               g_free(buf);
                                y += pi->height;
 
                                pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
@@ -3004,7 +3101,8 @@ static gint pan_window_layout_update_idle_cb(gpointer data)
        gint scroll_x;
        gint scroll_y;
 
-       if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
+       if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
+           (SORT_BY_EXIF_DATE && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
                {
                if (!pw->cache_list && !pw->cache_todo)
                        {
@@ -3015,7 +3113,7 @@ static gint pan_window_layout_update_idle_cb(gpointer data)
                                return TRUE;
                                }
                        }
-               if (pan_cache_step(pw))
+               if (pw->cache_todo)
                        {
                        pw->cache_count++;
                        pw->cache_tick++;
@@ -3035,7 +3133,10 @@ static gint pan_window_layout_update_idle_cb(gpointer data)
                                pw->cache_tick = 0;
                                }
 
-                       return TRUE;
+                       if (pan_cache_step(pw)) return TRUE;
+
+                       pw->idle_id = -1;
+                       return FALSE;
                        }
                }
 
@@ -3070,7 +3171,6 @@ static gint pan_window_layout_update_idle_cb(gpointer data)
        pan_window_message(pw, NULL);
 
        pw->idle_id = -1;
-
        return FALSE;
 }
 
@@ -3078,11 +3178,16 @@ static void pan_window_layout_update_idle(PanWindow *pw)
 {
        if (pw->idle_id == -1)
                {
-               pan_window_message(pw, _("Sorting images..."));
                pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
                }
 }
 
+static void pan_window_layout_update(PanWindow *pw)
+{
+       pan_window_message(pw, _("Sorting images..."));
+       pan_window_layout_update_idle(pw);
+}
+
 /*
  *-----------------------------------------------------------------------------
  * pan window keyboard
@@ -3998,7 +4103,7 @@ static void pan_window_layout_change_cb(GtkWidget *combo, gpointer data)
        PanWindow *pw = data;
 
        pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
-       pan_window_layout_update_idle(pw);
+       pan_window_layout_update(pw);
 }
 
 static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
@@ -4006,7 +4111,7 @@ static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
        PanWindow *pw = data;
 
        pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
-       pan_window_layout_update_idle(pw);
+       pan_window_layout_update(pw);
 }
 
 static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
@@ -4022,15 +4127,18 @@ static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
                warning_dialog(_("Folder not found"),
                               _("The entered path is not a folder"),
                               GTK_STOCK_DIALOG_WARNING, pw->path_entry);
-               return;
                }
+       else
+               {
+               tab_completion_append_to_history(pw->path_entry, path);
 
-       tab_completion_append_to_history(pw->path_entry, path);
+               g_free(pw->path);
+               pw->path = g_strdup(path);
 
-       g_free(pw->path);
-       pw->path = g_strdup(path);
+               pan_window_layout_update(pw);
+               }
 
-       pan_window_layout_update_idle(pw);
+       g_free(path);
 }
 
 static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
@@ -4058,6 +4166,7 @@ static void pan_window_close(PanWindow *pw)
        gtk_widget_destroy(pw->window);
 
        pan_window_items_free(pw);
+       pan_cache_free(pw);
 
        g_free(pw->path);
 
@@ -4263,7 +4372,7 @@ static void pan_window_new_real(const gchar *path)
 
        gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
 
-       pan_window_layout_update_idle(pw);
+       pan_window_layout_update(pw);
 
        gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
        gtk_widget_show(pw->window);
@@ -4554,7 +4663,7 @@ static void pan_window_get_dnd_data(GtkWidget *widget, GdkDragContext *context,
                        g_free(pw->path);
                        pw->path = g_strdup(path);
 
-                       pan_window_layout_update_idle(pw);
+                       pan_window_layout_update(pw);
                        }
 
                path_list_free(list);