[libchamplain] Introduce new memory cache and remove non-persistent file cache



commit 2841c81b38554bebfc8056f76db37a1c5e82cb00
Author: JiÅ?í Techet <techet gmail com>
Date:   Sun Aug 1 13:41:14 2010 +0200

    Introduce new memory cache and remove non-persistent file cache
    
    The new memory cache can store specified number of tiles in
    memory without storing them to the disk. It replaces
    the temporary file cache - it was a bit scary to recursively
    delete some directories and libchamplain shouldn't do things
    like that.
    
    Signed-off-by: JiÅ?í Techet <techet gmail com>

 champlain/Makefile.am                          |    3 +
 champlain/champlain-file-cache.c               |  182 +---------
 champlain/champlain-file-cache.h               |    2 +-
 champlain/champlain-file-tile-source.c         |    2 +-
 champlain/champlain-memory-cache.c             |  422 ++++++++++++++++++++++++
 champlain/champlain-memory-cache.h             |   69 ++++
 champlain/champlain-network-bbox-tile-source.c |    2 +-
 champlain/champlain-network-tile-source.c      |    2 +-
 champlain/champlain-null-tile-source.c         |    2 +-
 champlain/champlain-tile-cache.c               |  117 -------
 champlain/champlain-tile-cache.h               |    4 -
 champlain/champlain.h                          |    1 +
 demos/local-rendering.c                        |    3 +-
 13 files changed, 519 insertions(+), 292 deletions(-)
---
diff --git a/champlain/Makefile.am b/champlain/Makefile.am
index f4142a1..9665126 100644
--- a/champlain/Makefile.am
+++ b/champlain/Makefile.am
@@ -34,6 +34,7 @@ libchamplain_headers = \
 	champlain-map-source-chain.h	\
 	champlain-tile-source.h		\
 	champlain-tile-cache.h		\
+	champlain-memory-cache.h		\
 	champlain-network-tile-source.h	\
 	champlain-file-cache.h		\
 	champlain-map-source-factory.h	\
@@ -70,6 +71,7 @@ libchamplain_ CHAMPLAIN_API_VERSION@_la_SOURCES = \
 	champlain-map-source-chain.c	\
 	champlain-tile-source.c		\
 	champlain-tile-cache.c		\
+	champlain-memory-cache.c		\
 	champlain-network-tile-source.c	\
 	champlain-file-cache.c		\
 	champlain-map-source-factory.c	\
@@ -101,6 +103,7 @@ libchamplain_include_HEADERS = \
 	champlain-map-source-chain.h	\
 	champlain-tile-source.h		\
 	champlain-tile-cache.h		\
+	champlain-memory-cache.h		\
 	champlain-network-tile-source.h	\
 	champlain-file-cache.h		\
 	champlain-map-source-factory.h	\
diff --git a/champlain/champlain-file-cache.c b/champlain/champlain-file-cache.c
index 01dc84b..3aab7c7 100644
--- a/champlain/champlain-file-cache.c
+++ b/champlain/champlain-file-cache.c
@@ -22,8 +22,7 @@
  * @short_description: Stores and loads cached tiles from the file system
  *
  * #ChamplainFileCache is a map source that stores and retrieves tiles from the
- * file system. It can be temporary (deleted when the object is destroyed) or
- * permanent. Tiles most frequently loaded gain in "popularity". This popularity
+ * file system. Tiles most frequently loaded gain in "popularity". This popularity
  * is taken into account when purging the cache.
  */
 
@@ -55,14 +54,12 @@ struct _ChamplainFileCachePrivate
   guint size_limit;
   gchar *cache_dir;
 
-  gchar *real_cache_dir;
   sqlite3 *db;
   sqlite3_stmt *stmt_select;
   sqlite3_stmt *stmt_update;
 };
 
 static void finalize_sql (ChamplainFileCache *file_cache);
-static void delete_temp_cache (ChamplainFileCache *file_cache);
 static void init_cache  (ChamplainFileCache *file_cache);
 static gchar *get_filename (ChamplainFileCache *file_cache,
     ChamplainTile *tile);
@@ -70,7 +67,6 @@ static gboolean tile_is_expired (ChamplainFileCache *file_cache,
     ChamplainTile *tile);
 static void delete_tile (ChamplainFileCache *file_cache,
     const gchar *filename);
-static void delete_dir_recursive (GFile *parent);
 static gboolean create_cache_dir (const gchar *dir_name);
 
 static void fill_tile (ChamplainMapSource *map_source,
@@ -84,7 +80,6 @@ static void refresh_tile_time (ChamplainTileCache *tile_cache,
     ChamplainTile *tile);
 static void on_tile_filled (ChamplainTileCache *tile_cache,
     ChamplainTile *tile);
-static void clean (ChamplainTileCache *tile_cache);
 
 static void
 champlain_file_cache_get_property (GObject *object,
@@ -164,41 +159,16 @@ finalize_sql (ChamplainFileCache *file_cache)
     }
 }
 
-static void
-delete_temp_cache (ChamplainFileCache *file_cache)
-{
-  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
-  ChamplainFileCachePrivate *priv = file_cache->priv;
-
-  if (!champlain_tile_cache_get_persistent (tile_cache) && priv->real_cache_dir)
-    {
-      GFile *file = NULL;
-
-      /* delete the directory contents */
-      file = g_file_new_for_path (priv->real_cache_dir);
-      delete_dir_recursive (file);
 
-      /* delete the directory itself */
-      if (!g_file_delete (file, NULL, NULL))
-        g_warning ("Failed to remove temporary cache main directory");
-
-      g_object_unref (file);
-    }
-}
 
 static void
 champlain_file_cache_finalize (GObject *object)
 {
   ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
-  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
   ChamplainFileCachePrivate *priv = file_cache->priv;
 
   finalize_sql (file_cache);
 
-  if (!champlain_tile_cache_get_persistent (tile_cache))
-    delete_temp_cache (file_cache);
-
-  g_free (priv->real_cache_dir);
   g_free (priv->cache_dir);
 
   G_OBJECT_CLASS (champlain_file_cache_parent_class)->finalize (object);
@@ -220,64 +190,18 @@ create_cache_dir (const gchar *dir_name)
   return TRUE;
 }
 
-#ifdef G_OS_WIN32
-#include <io.h>
-#include <glib/gstdio.h>
-static char *mkdtemp(char *template)
-{
-	gunichar2 *wtemplate;
-	gchar *tmpl;
-	if (!template)
-		return NULL;
-	wtemplate = g_utf8_to_utf16 (template, -1, NULL, NULL, NULL);
-	if (!_wmktemp (wtemplate)) {
-		g_free (wtemplate);
-		return NULL;
-	}
-	tmpl = g_utf16_to_utf8 (wtemplate, -1, NULL, NULL, NULL);
-	g_free (wtemplate);
-	if ((strlen (template) != strlen (tmpl)) || g_mkdir (tmpl, 0700)) {
-		g_free (tmpl);
-		return NULL;
-	}
-	strcpy (template,tmpl);
-	g_free (tmpl);
-	return template;
-}
-#endif
 
 static void
 init_cache  (ChamplainFileCache *file_cache)
 {
-  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
   ChamplainFileCachePrivate *priv = file_cache->priv;
   gchar *filename = NULL;
   gchar *error_msg = NULL;
   gint error;
 
-  if (champlain_tile_cache_get_persistent (tile_cache))
-    {
-      priv->real_cache_dir = g_strdup (priv->cache_dir);
-      g_return_if_fail (create_cache_dir (priv->real_cache_dir));
-    }
-  else
-    {
-      /* Create temporary directory for non-persistent caches */
-      gchar *tmplate = NULL;
-
-      tmplate = g_build_filename (priv->cache_dir, "champlain-XXXXXX", NULL);
-
-      priv->real_cache_dir = mkdtemp (tmplate);
-
-      if (!priv->real_cache_dir)
-        {
-          g_warning ("Failed to create filename for temporary cache: template '%s'", tmplate);
-          g_free (tmplate);
-          return;
-        }
-    }
+  g_return_if_fail (create_cache_dir (priv->cache_dir));
 
-  filename = g_build_filename (priv->real_cache_dir,
+  filename = g_build_filename (priv->cache_dir,
                                "cache.db", NULL);
   error = sqlite3_open_v2 (filename, &priv->db,
                            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
@@ -343,23 +267,17 @@ static void
 champlain_file_cache_constructed (GObject *object)
 {
   ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
-  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
   ChamplainFileCachePrivate *priv = file_cache->priv;
 
   if (!priv->cache_dir)
     {
-      if (champlain_tile_cache_get_persistent (tile_cache))
-        {
 #ifdef CHAMPLAIN_HAS_MAEMO
-          priv->cache_dir = g_strdup ("/home/user/MyDocs/.Maps/");
+      priv->cache_dir = g_strdup ("/home/user/MyDocs/.Maps/");
 #else
-          priv->cache_dir = g_build_path (G_DIR_SEPARATOR_S,
-                                g_get_user_cache_dir (),
-                                "champlain", NULL);
+      priv->cache_dir = g_build_path (G_DIR_SEPARATOR_S,
+                            g_get_user_cache_dir (),
+                            "champlain", NULL);
 #endif
-        }
-      else
-        priv->cache_dir = g_strdup (g_get_tmp_dir ());
     }
 
   init_cache (file_cache);
@@ -419,7 +337,6 @@ champlain_file_cache_class_init (ChamplainFileCacheClass *klass)
   tile_cache_class->store_tile = store_tile;
   tile_cache_class->refresh_tile_time = refresh_tile_time;
   tile_cache_class->on_tile_filled = on_tile_filled;
-  tile_cache_class->clean = clean;
 
   map_source_class->fill_tile = fill_tile;
 }
@@ -433,7 +350,7 @@ champlain_file_cache_init (ChamplainFileCache *file_cache)
 
   priv->cache_dir = NULL;
   priv->size_limit = 100000000;
-  priv->real_cache_dir = NULL;
+  priv->cache_dir = NULL;
   priv->db = NULL;
   priv->stmt_select = NULL;
   priv->stmt_update = NULL;
@@ -444,7 +361,7 @@ champlain_file_cache_init (ChamplainFileCache *file_cache)
  *
  * Default constructor of #ChamplainFileCache.
  *
- * Returns: a constructed permanent cache of maximal size 100000000 B inside
+ * Returns: a constructed cache of maximal size 100000000 B inside
  * ~/.cache/champlain.
  *
  * Since: 0.6
@@ -459,10 +376,7 @@ ChamplainFileCache* champlain_file_cache_new (void)
  * @size_limit: maximal size of the cache in bytes
  * @cache_dir: the directory where the cache is created. For temporary caches
  * one more directory with random name is created inside this directory.
- * When cache_dir == NULL, a cache in ~/.cache/champlain is used for permanent
- * caches and /tmp for temporary caches.
- * @persistent: if TRUE, the cache is persistent; otherwise the cache is
- * temporary and will be deleted when the cache object is destructed.
+ * When cache_dir == NULL, a cache in ~/.cache/champlain is used.
  *
  * Constructor of #ChamplainFileCache.
  *
@@ -471,12 +385,11 @@ ChamplainFileCache* champlain_file_cache_new (void)
  * Since: 0.6
  */
 ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
-    const gchar *cache_dir,
-    gboolean persistent)
+    const gchar *cache_dir)
 {
   ChamplainFileCache * cache;
   cache = g_object_new (CHAMPLAIN_TYPE_FILE_CACHE, "size-limit", size_limit,
-              "cache-dir", cache_dir, "persistent-cache", persistent, NULL);
+              "cache-dir", cache_dir, NULL);
   return cache;
 }
 
@@ -545,7 +458,7 @@ get_filename (ChamplainFileCache *file_cache,
 
   g_return_val_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache), NULL);
   g_return_val_if_fail (CHAMPLAIN_IS_TILE (tile), NULL);
-  g_return_val_if_fail (priv->real_cache_dir, NULL);
+  g_return_val_if_fail (priv->cache_dir, NULL);
 
   ChamplainMapSource* map_source = CHAMPLAIN_MAP_SOURCE(file_cache);
 
@@ -553,7 +466,7 @@ get_filename (ChamplainFileCache *file_cache,
                                      "%s" G_DIR_SEPARATOR_S
                                      "%d" G_DIR_SEPARATOR_S
                                      "%d" G_DIR_SEPARATOR_S "%d.png",
-                                     priv->real_cache_dir,
+                                     priv->cache_dir,
                                      champlain_map_source_get_id (map_source),
                                      champlain_tile_get_zoom_level (tile),
                                      champlain_tile_get_x (tile),
@@ -784,9 +697,7 @@ refresh_tile_time (ChamplainTileCache *tile_cache,
   g_object_unref (info);
 
   if (CHAMPLAIN_IS_TILE_CACHE(next_source))
-    {
-      refresh_tile_time (CHAMPLAIN_TILE_CACHE(next_source), tile);
-    }
+    champlain_tile_cache_refresh_tile_time (CHAMPLAIN_TILE_CACHE(next_source), tile);
 }
 
 static void
@@ -863,7 +774,7 @@ store_tile (ChamplainTileCache *tile_cache,
 
 store_next:
   if (CHAMPLAIN_IS_TILE_CACHE(next_source))
-    store_tile (CHAMPLAIN_TILE_CACHE(next_source), tile, contents, size);
+    champlain_tile_cache_store_tile (CHAMPLAIN_TILE_CACHE(next_source), tile, contents, size);
 
   g_free (filename);
   g_free (path);
@@ -906,27 +817,9 @@ on_tile_filled (ChamplainTileCache *tile_cache,
 
 call_next:
   if (CHAMPLAIN_IS_TILE_CACHE(next_source))
-    on_tile_filled (CHAMPLAIN_TILE_CACHE(next_source), tile);
+    champlain_tile_cache_on_tile_filled (CHAMPLAIN_TILE_CACHE(next_source), tile);
 }
 
-static void
-clean (ChamplainTileCache *tile_cache)
-{
-  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (tile_cache));
-
-  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
-  ChamplainFileCachePrivate *priv = file_cache->priv;
-
-  g_return_if_fail (!champlain_tile_cache_get_persistent (tile_cache));
-
-  finalize_sql (file_cache);
-  delete_temp_cache (file_cache);
-
-  g_free (priv->real_cache_dir);
-  priv->real_cache_dir = NULL;
-
-  init_cache (file_cache);
-}
 
 static void
 delete_tile (ChamplainFileCache *file_cache, const gchar *filename)
@@ -1069,45 +962,4 @@ champlain_file_cache_purge (ChamplainFileCache *file_cache)
   sqlite3_free (query);
 }
 
-static void
-delete_dir_recursive (GFile *parent)
-{
-  g_return_if_fail (parent);
-
-  GError *error = NULL;
-  GFileEnumerator *enumerator;
-  GFileInfo *info;
-  GFile *child;
-
-  enumerator = g_file_enumerate_children (parent, "*",
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error);
-
-  if (!enumerator)
-    {
-      DEBUG ("Failed to create file enumerator in delete_dir_recursive: %s", error->message);
-      g_error_free (error);
-      return;
-    }
-
-  info = g_file_enumerator_next_file (enumerator, NULL, NULL);
-  while (info && !error)
-    {
-      child = g_file_get_child (parent, g_file_info_get_name (info));
-
-      if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
-        delete_dir_recursive (child);
 
-      error = NULL;
-      if (!g_file_delete (child, NULL, &error))
-        {
-          DEBUG ("Deleting tile from disk failed: %s", error->message);
-          g_error_free (error);
-        }
-
-      g_object_unref (child);
-      g_object_unref (info);
-      info = g_file_enumerator_next_file (enumerator, NULL, NULL);
-    }
-
-  g_file_enumerator_close (enumerator, NULL, NULL);
-}
diff --git a/champlain/champlain-file-cache.h b/champlain/champlain-file-cache.h
index e3a5d9d..39243b1 100644
--- a/champlain/champlain-file-cache.h
+++ b/champlain/champlain-file-cache.h
@@ -57,7 +57,7 @@ GType champlain_file_cache_get_type (void);
 
 ChamplainFileCache* champlain_file_cache_new (void);
 ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
-    const gchar *cache_dir, gboolean persistent);
+    const gchar *cache_dir);
 
 guint champlain_file_cache_get_size_limit (ChamplainFileCache *file_cache);
 void champlain_file_cache_set_size_limit (ChamplainFileCache *file_cache,
diff --git a/champlain/champlain-file-tile-source.c b/champlain/champlain-file-tile-source.c
index 191ddc4..aba3f5c 100644
--- a/champlain/champlain-file-tile-source.c
+++ b/champlain/champlain-file-tile-source.c
@@ -149,7 +149,7 @@ tile_rendered_cb (ChamplainTile *tile,
     champlain_map_source_fill_tile (next_source, tile);
 
   g_object_unref (map_source);
-  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, data);
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
 }
 
 static void
diff --git a/champlain/champlain-memory-cache.c b/champlain/champlain-memory-cache.c
new file mode 100644
index 0000000..e8f473a
--- /dev/null
+++ b/champlain/champlain-memory-cache.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#define DEBUG_FLAG CHAMPLAIN_DEBUG_CACHE
+#include "champlain-debug.h"
+
+#include "champlain-memory-cache.h"
+
+#include <glib.h>
+#include <string.h>
+
+G_DEFINE_TYPE (ChamplainMemoryCache, champlain_memory_cache, CHAMPLAIN_TYPE_TILE_CACHE);
+
+#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAMPLAIN_TYPE_MEMORY_CACHE, ChamplainMemoryCachePrivate))
+
+enum
+{
+  PROP_0,
+  PROP_SIZE_LIMIT
+};
+
+struct _ChamplainMemoryCachePrivate
+{
+  guint size_limit;
+  GQueue *queue;
+};
+
+typedef struct
+{
+  gchar *key;
+  gchar *data;
+  guint size;
+} QueueMember;
+
+
+static void fill_tile (ChamplainMapSource *map_source,
+    ChamplainTile *tile);
+
+static void store_tile (ChamplainTileCache *tile_cache,
+    ChamplainTile *tile,
+    const gchar *contents,
+    gsize size);
+static void refresh_tile_time (ChamplainTileCache *tile_cache,
+    ChamplainTile *tile);
+static void on_tile_filled (ChamplainTileCache *tile_cache,
+    ChamplainTile *tile);
+
+
+static void
+champlain_memory_cache_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (object);
+
+  switch (property_id)
+    {
+    case PROP_SIZE_LIMIT:
+      g_value_set_uint (value, champlain_memory_cache_get_size_limit (memory_cache));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+
+static void
+champlain_memory_cache_set_property (GObject *object,
+    guint property_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (object);
+
+  switch (property_id)
+    {
+    case PROP_SIZE_LIMIT:
+      champlain_memory_cache_set_size_limit (memory_cache, g_value_get_uint (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+
+static void
+champlain_memory_cache_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_memory_cache_parent_class)->dispose (object);
+}
+
+
+static void
+champlain_memory_cache_finalize (GObject *object)
+{
+  ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (object);
+
+  g_queue_free (memory_cache->priv->queue);
+
+  G_OBJECT_CLASS (champlain_memory_cache_parent_class)->finalize (object);
+}
+
+
+static void
+champlain_memory_cache_class_init (ChamplainMemoryCacheClass *klass)
+{
+  ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
+  ChamplainTileCacheClass *tile_cache_class = CHAMPLAIN_TILE_CACHE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ChamplainMemoryCachePrivate));
+
+  object_class->finalize = champlain_memory_cache_finalize;
+  object_class->dispose = champlain_memory_cache_dispose;
+  object_class->get_property = champlain_memory_cache_get_property;
+  object_class->set_property = champlain_memory_cache_set_property;
+
+  /*
+   * ChamplainMemoryCache:size-limit:
+   *
+   * The cache size limit in bytes.
+   *
+   * Note: this new value will not be applied until you call #champlain_cache_purge
+   *
+   * Since: 0.4
+   */
+  pspec = g_param_spec_uint ("size-limit",
+      "Size Limit",
+      "The cache's size limit (Mb)",
+      1,
+      G_MAXINT,
+      100,
+      G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_SIZE_LIMIT, pspec);
+
+  tile_cache_class->store_tile = store_tile;
+  tile_cache_class->refresh_tile_time = refresh_tile_time;
+  tile_cache_class->on_tile_filled = on_tile_filled;
+
+  map_source_class->fill_tile = fill_tile;
+}
+
+
+ChamplainMemoryCache *
+champlain_memory_cache_new (void)
+{
+  return CHAMPLAIN_MEMORY_CACHE (g_object_new (CHAMPLAIN_TYPE_MEMORY_CACHE, NULL));
+}
+
+
+ChamplainMemoryCache *
+champlain_memory_cache_new_full (guint size_limit)
+{
+  ChamplainMemoryCache *cache;
+
+  cache = g_object_new (CHAMPLAIN_TYPE_MEMORY_CACHE,
+      "size-limit", size_limit,
+      NULL);
+
+  return cache;
+}
+
+
+static void
+reload_tiles_cb (ChamplainMemoryCache *memory_cache, G_GNUC_UNUSED gpointer data)
+{
+  g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache));
+
+  champlain_memory_cache_clean (memory_cache);
+}
+
+
+static void
+champlain_memory_cache_init (ChamplainMemoryCache *memory_cache)
+{
+  ChamplainMemoryCachePrivate *priv = GET_PRIVATE (memory_cache);
+
+  memory_cache->priv = priv;
+
+  g_signal_connect (memory_cache, "reload-tiles",
+      G_CALLBACK (reload_tiles_cb), NULL);
+
+  priv->queue = g_queue_new ();
+}
+
+
+guint
+champlain_memory_cache_get_size_limit (ChamplainMemoryCache *memory_cache)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache), 0);
+
+  return memory_cache->priv->size_limit;
+}
+
+
+void
+champlain_memory_cache_set_size_limit (ChamplainMemoryCache *memory_cache,
+    guint size_limit)
+{
+  g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache));
+
+  ChamplainMemoryCachePrivate *priv = memory_cache->priv;
+
+  priv->size_limit = size_limit;
+  g_object_notify (G_OBJECT (memory_cache), "size-limit");
+}
+
+
+static gchar *
+generate_queue_key (ChamplainMemoryCache *memory_cache,
+    ChamplainTile *tile)
+{
+  g_return_val_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache), NULL);
+  g_return_val_if_fail (CHAMPLAIN_IS_TILE (tile), NULL);
+
+  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE (memory_cache);
+
+  gchar *filename = g_strdup_printf ("%d/%d/%d/%s",
+      champlain_tile_get_zoom_level (tile),
+      champlain_tile_get_x (tile),
+      champlain_tile_get_y (tile),
+      champlain_map_source_get_id (map_source));
+  return filename;
+}
+
+
+static gint
+compare_queue_members (const QueueMember *a, const QueueMember *b)
+{
+  return g_strcmp0 (a->key, b->key);
+}
+
+
+static void
+move_queue_member_to_head (GQueue *queue, GList *link)
+{
+  g_queue_unlink (queue, link);
+  g_queue_push_head_link (queue, link);
+}
+
+
+static void
+delete_queue_member (QueueMember *member, gpointer user_data)
+{
+  if (member)
+    {
+      g_free (member->key);
+      g_free (member->data);
+      g_free (member);
+    }
+}
+
+
+static void
+tile_rendered_cb (ChamplainTile *tile,
+    ChamplainRenderCallbackData *data,
+    ChamplainMapSource *map_source)
+{
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+
+  if (!data->error)
+    {
+      if (CHAMPLAIN_IS_TILE_CACHE (next_source))
+        on_tile_filled (CHAMPLAIN_TILE_CACHE (next_source), tile);
+
+      champlain_tile_set_fade_in (tile, TRUE);
+      champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+      champlain_tile_display_content (tile);
+    }
+  else if (CHAMPLAIN_IS_MAP_SOURCE (next_source))
+    champlain_map_source_fill_tile (next_source, tile);
+
+  g_object_unref (map_source);
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
+}
+
+
+static void
+fill_tile (ChamplainMapSource *map_source,
+    ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (map_source));
+  g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
+
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+
+  if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADED)
+    {
+      ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (map_source);
+      ChamplainMemoryCachePrivate *priv = memory_cache->priv;
+      ChamplainRenderer *renderer;
+      GList *link;
+      QueueMember key;
+
+      key.key = generate_queue_key (memory_cache, tile);
+      link = g_queue_find_custom (priv->queue, &key, (GCompareFunc) compare_queue_members);
+      if (link)
+        {
+          QueueMember *member = link->data;
+
+          move_queue_member_to_head (priv->queue, link);
+
+          renderer = champlain_map_source_get_renderer (map_source);
+
+          g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
+
+          g_object_ref (map_source);
+          g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), map_source);
+          champlain_renderer_set_data (renderer, member->data, member->size);
+          champlain_renderer_render (renderer, tile);
+
+          return;
+        }
+    }
+
+  if (CHAMPLAIN_IS_MAP_SOURCE (next_source))
+    champlain_map_source_fill_tile (next_source, tile);
+  else if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_LOADED)
+    {
+      /* if we have some content, use the tile even if it wasn't validated */
+      champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+      champlain_tile_display_content (tile);
+    }
+}
+
+
+static void
+store_tile (ChamplainTileCache *tile_cache,
+    ChamplainTile *tile,
+    const gchar *contents,
+    gsize size)
+{
+  g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (tile_cache));
+
+  ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (tile_cache);
+  ChamplainMemoryCachePrivate *priv = memory_cache->priv;
+  GList *link;
+  QueueMember key;
+
+  key.key = generate_queue_key (memory_cache, tile);
+  link = g_queue_find_custom (priv->queue, &key, (GCompareFunc) compare_queue_members);
+  if (link)
+    move_queue_member_to_head (priv->queue, link);
+  else
+    {
+      QueueMember *member;
+
+      if (priv->queue->length >= priv->size_limit)
+        {
+          member = g_queue_pop_tail (priv->queue);
+          delete_queue_member (member, NULL);
+        }
+
+      member = g_new0 (QueueMember, 1);
+      member->key = key.key;
+      member->data = g_memdup (contents, size);
+      member->size = size;
+
+      g_queue_push_head (priv->queue, member);
+    }
+}
+
+
+static void
+refresh_tile_time (ChamplainTileCache *tile_cache,
+    ChamplainTile *tile)
+{
+}
+
+
+void
+champlain_memory_cache_clean (ChamplainMemoryCache *memory_cache)
+{
+  ChamplainMemoryCachePrivate *priv = memory_cache->priv;
+
+  g_queue_foreach (priv->queue, (GFunc) delete_queue_member, NULL);
+  g_queue_clear (priv->queue);
+}
+
+
+static void
+on_tile_filled (ChamplainTileCache *tile_cache,
+    ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (tile_cache));
+  g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
+
+  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE (tile_cache);
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+  ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (tile_cache);
+  ChamplainMemoryCachePrivate *priv = memory_cache->priv;
+  GList *link;
+  QueueMember key;
+
+  key.key = generate_queue_key (memory_cache, tile);
+  link = g_queue_find_custom (priv->queue, &key, (GCompareFunc) compare_queue_members);
+  if (link)
+    move_queue_member_to_head (priv->queue, link);
+
+  if (CHAMPLAIN_IS_TILE_CACHE (next_source))
+    champlain_tile_cache_on_tile_filled (CHAMPLAIN_TILE_CACHE (next_source), tile);
+}
diff --git a/champlain/champlain-memory-cache.h b/champlain/champlain-memory-cache.h
new file mode 100644
index 0000000..33a1993
--- /dev/null
+++ b/champlain/champlain-memory-cache.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 Jiri Techet <techet gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#if !defined (__CHAMPLAIN_CHAMPLAIN_H_INSIDE__) && !defined (CHAMPLAIN_COMPILATION)
+#error "Only <champlain/champlain.h> can be included directly."
+#endif
+
+#ifndef _CHAMPLAIN_MEMORY_CACHE_H_
+#define _CHAMPLAIN_MEMORY_CACHE_H_
+
+#include <glib-object.h>
+#include <champlain/champlain-tile-cache.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_MEMORY_CACHE             (champlain_memory_cache_get_type ())
+#define CHAMPLAIN_MEMORY_CACHE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_MEMORY_CACHE, ChamplainMemoryCache))
+#define CHAMPLAIN_MEMORY_CACHE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_MEMORY_CACHE, ChamplainMemoryCacheClass))
+#define CHAMPLAIN_IS_MEMORY_CACHE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_MEMORY_CACHE))
+#define CHAMPLAIN_IS_MEMORY_CACHE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_MEMORY_CACHE))
+#define CHAMPLAIN_MEMORY_CACHE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_MEMORY_CACHE, ChamplainMemoryCacheClass))
+
+typedef struct _ChamplainMemoryCachePrivate ChamplainMemoryCachePrivate;
+
+typedef struct _ChamplainMemoryCache ChamplainMemoryCache;
+typedef struct _ChamplainMemoryCacheClass ChamplainMemoryCacheClass;
+
+struct _ChamplainMemoryCache
+{
+  ChamplainTileCache parent_instance;
+
+  ChamplainMemoryCachePrivate *priv;
+};
+
+struct _ChamplainMemoryCacheClass
+{
+  ChamplainTileCacheClass parent_class;
+};
+
+GType champlain_memory_cache_get_type (void);
+
+ChamplainMemoryCache *champlain_memory_cache_new (void);
+ChamplainMemoryCache *champlain_memory_cache_new_full (guint size_limit);
+
+guint champlain_memory_cache_get_size_limit (ChamplainMemoryCache *memory_cache);
+void champlain_memory_cache_set_size_limit (ChamplainMemoryCache *memory_cache,
+    guint size_limit);
+
+void champlain_memory_cache_clean (ChamplainMemoryCache *memory_cache);
+
+
+G_END_DECLS
+
+#endif /* _CHAMPLAIN_MEMORY_CACHE_H_ */
diff --git a/champlain/champlain-network-bbox-tile-source.c b/champlain/champlain-network-bbox-tile-source.c
index 977d5ec..f626534 100644
--- a/champlain/champlain-network-bbox-tile-source.c
+++ b/champlain/champlain-network-bbox-tile-source.c
@@ -353,7 +353,7 @@ tile_rendered_cb (ChamplainTile *tile,
     champlain_map_source_fill_tile (next_source, tile);
 
   g_object_unref (map_source);
-  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, data);
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
 }
 
 static void
diff --git a/champlain/champlain-network-tile-source.c b/champlain/champlain-network-tile-source.c
index f877367..a18baee 100644
--- a/champlain/champlain-network-tile-source.c
+++ b/champlain/champlain-network-tile-source.c
@@ -548,7 +548,7 @@ tile_rendered_cb (ChamplainTile *tile, ChamplainRenderCallbackData *data, TileRe
 
   g_object_unref (map_source);
   g_free (user_data);
-  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, data);
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, user_data);
 }
 
 
diff --git a/champlain/champlain-null-tile-source.c b/champlain/champlain-null-tile-source.c
index ced41bc..9b8a959 100644
--- a/champlain/champlain-null-tile-source.c
+++ b/champlain/champlain-null-tile-source.c
@@ -104,7 +104,7 @@ tile_rendered_cb (ChamplainTile *tile,
     champlain_map_source_fill_tile (next_source, tile);
 
   g_object_unref (map_source);
-  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, data);
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
 }
 
 
diff --git a/champlain/champlain-tile-cache.c b/champlain/champlain-tile-cache.c
index 25576da..19d75f6 100644
--- a/champlain/champlain-tile-cache.c
+++ b/champlain/champlain-tile-cache.c
@@ -31,16 +31,6 @@ G_DEFINE_TYPE (ChamplainTileCache, champlain_tile_cache, CHAMPLAIN_TYPE_MAP_SOUR
 
 #define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_TILE_CACHE, ChamplainTileCachePrivate))
 
-enum
-{
-  PROP_0,
-  PROP_PERSISTENT_CACHE
-};
-
-struct _ChamplainTileCachePrivate
-{
-  gboolean persistent;
-};
 
 static const gchar *get_id (ChamplainMapSource *map_source);
 static const gchar *get_name (ChamplainMapSource *map_source);
@@ -50,43 +40,7 @@ static guint get_min_zoom_level (ChamplainMapSource *map_source);
 static guint get_max_zoom_level (ChamplainMapSource *map_source);
 static guint get_tile_size (ChamplainMapSource *map_source);
 static ChamplainMapProjection get_projection (ChamplainMapSource *map_source);
-static void reload_tiles_cb (ChamplainTileCache *tile_cache, G_GNUC_UNUSED gpointer data);
-
-static void
-champlain_tile_cache_get_property (GObject *object,
-    guint property_id,
-    GValue *value,
-    GParamSpec *pspec)
-{
-  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE (object);
-
-  switch (property_id)
-    {
-    case PROP_PERSISTENT_CACHE:
-      g_value_set_boolean (value, champlain_tile_cache_get_persistent (tile_cache));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-    }
-}
 
-static void
-champlain_tile_cache_set_property (GObject *object,
-    guint property_id,
-    const GValue *value,
-    GParamSpec *pspec)
-{
-  ChamplainTileCachePrivate *priv = CHAMPLAIN_TILE_CACHE (object)->priv;
-
-  switch (property_id)
-    {
-    case PROP_PERSISTENT_CACHE:
-      priv->persistent = g_value_get_boolean (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-    }
-}
 
 static void
 champlain_tile_cache_dispose (GObject *object)
@@ -112,14 +66,9 @@ champlain_tile_cache_class_init (ChamplainTileCacheClass *klass)
   GObjectClass* object_class = G_OBJECT_CLASS (klass);
   ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
   ChamplainTileCacheClass *tile_cache_class = CHAMPLAIN_TILE_CACHE_CLASS (klass);
-  GParamSpec *pspec;
-
-  g_type_class_add_private (klass, sizeof (ChamplainTileCachePrivate));
 
   object_class->finalize = champlain_tile_cache_finalize;
   object_class->dispose = champlain_tile_cache_dispose;
-  object_class->get_property = champlain_tile_cache_get_property;
-  object_class->set_property = champlain_tile_cache_set_property;
   object_class->constructed = champlain_tile_cache_constructed;
 
   map_source_class->get_id = get_id;
@@ -136,62 +85,13 @@ champlain_tile_cache_class_init (ChamplainTileCacheClass *klass)
   tile_cache_class->refresh_tile_time = NULL;
   tile_cache_class->on_tile_filled = NULL;
   tile_cache_class->store_tile = NULL;
-  tile_cache_class->clean = NULL;
-
-  /**
-  * ChamplainTileCache:persistent-cache
-  *
-  * Determines whether the cache is persistent or temporary (cleaned upon
-  * destruction)
-  *
-  * Since: 0.6
-  */
-  pspec = g_param_spec_boolean ("persistent-cache",
-                                "Persistent Cache",
-                                "Specifies whether the cache is persistent",
-                                TRUE,
-                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
-  g_object_class_install_property (object_class, PROP_PERSISTENT_CACHE, pspec);
 }
 
 static void
 champlain_tile_cache_init (ChamplainTileCache *tile_cache)
 {
-  ChamplainTileCachePrivate *priv = GET_PRIVATE (tile_cache);
-
-  tile_cache->priv = priv;
-
-  priv->persistent = TRUE;
-  g_signal_connect (tile_cache, "reload-tiles",
-      G_CALLBACK (reload_tiles_cb), NULL);
-}
-
-static
-void reload_tiles_cb (ChamplainTileCache *tile_cache, G_GNUC_UNUSED gpointer data)
-{
-  g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache));
-
-  if (!champlain_tile_cache_get_persistent (tile_cache))
-    champlain_tile_cache_clean (tile_cache);
 }
 
-/**
- * champlain_tile_cache_get_persistent:
- * @tile_cache: a #ChamplainTileCache
- *
- * Gets cache persistency information.
- *
- * Returns: TRUE when the cache is persistent; FALSE otherwise.
- *
- * Since: 0.6
- */
-gboolean
-champlain_tile_cache_get_persistent (ChamplainTileCache *tile_cache)
-{
-  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache), FALSE);
-
-  return tile_cache->priv->persistent;
-}
 
 /**
  * champlain_tile_cache_store_tile:
@@ -257,23 +157,6 @@ champlain_tile_cache_on_tile_filled (ChamplainTileCache *tile_cache,
   CHAMPLAIN_TILE_CACHE_GET_CLASS (tile_cache)->on_tile_filled (tile_cache, tile);
 }
 
-/**
- * champlain_tile_cache_clean:
- * @tile_cache: a #ChamplainTileCache
- *
- * Erases the contents of the cache. This function will fail when the cache is
- * not temporary.
- *
- * Since: 0.6
- */
-void
-champlain_tile_cache_clean (ChamplainTileCache *tile_cache)
-{
-  g_return_if_fail (CHAMPLAIN_IS_TILE_CACHE (tile_cache));
-
-  CHAMPLAIN_TILE_CACHE_GET_CLASS (tile_cache)->clean (tile_cache);
-}
-
 static const gchar *
 get_id (ChamplainMapSource *map_source)
 {
diff --git a/champlain/champlain-tile-cache.h b/champlain/champlain-tile-cache.h
index d8aa0b6..ff8ffc7 100644
--- a/champlain/champlain-tile-cache.h
+++ b/champlain/champlain-tile-cache.h
@@ -59,13 +59,10 @@ struct _ChamplainTileCacheClass
       ChamplainTile *tile);
   void (*on_tile_filled) (ChamplainTileCache *tile_cache,
       ChamplainTile *tile);
-  void (*clean) (ChamplainTileCache *tile_cache);
 };
 
 GType champlain_tile_cache_get_type (void);
 
-gboolean champlain_tile_cache_get_persistent (ChamplainTileCache *tile_cache);
-
 void champlain_tile_cache_store_tile (ChamplainTileCache *tile_cache,
     ChamplainTile *tile,
     const gchar *contents,
@@ -74,7 +71,6 @@ void champlain_tile_cache_refresh_tile_time (ChamplainTileCache *tile_cache,
     ChamplainTile *tile);
 void champlain_tile_cache_on_tile_filled (ChamplainTileCache *tile_cache,
     ChamplainTile *tile);
-void champlain_tile_cache_clean (ChamplainTileCache *tile_cache);
 
 G_END_DECLS
 
diff --git a/champlain/champlain.h b/champlain/champlain.h
index a183286..8c87235 100644
--- a/champlain/champlain.h
+++ b/champlain/champlain.h
@@ -37,6 +37,7 @@
 #include "champlain/champlain-map-source-chain.h"
 #include "champlain/champlain-tile-source.h"
 #include "champlain/champlain-tile-cache.h"
+#include "champlain/champlain-memory-cache.h"
 #include "champlain/champlain-network-tile-source.h"
 #include "champlain/champlain-file-cache.h"
 #include "champlain/champlain-null-tile-source.h"
diff --git a/demos/local-rendering.c b/demos/local-rendering.c
index 694e547..e9d57c7 100644
--- a/demos/local-rendering.c
+++ b/demos/local-rendering.c
@@ -444,7 +444,8 @@ map_source_changed (GtkWidget *widget, ChamplainView *view)
       champlain_map_source_chain_push (source_chain, src);
       champlain_map_source_chain_push (source_chain, tile_source);
 
-      src = CHAMPLAIN_MAP_SOURCE(champlain_file_cache_new_full (100000000, NULL, FALSE));
+      src = CHAMPLAIN_MAP_SOURCE(champlain_memory_cache_new_full (100));
+      champlain_map_source_set_renderer (src, CHAMPLAIN_RENDERER (champlain_image_renderer_new ()));
 
       champlain_map_source_chain_push (source_chain, src);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]