[libchamplain: 22/24] Merge branch 'libchamplain-0-4'



commit 84811366633b33418bfcc4e5226c60391b006034
Merge: a2dea0e ef14ca5
Author: Pierre-Luc Beaudoin <pierre-luc pierlux com>
Date:   Tue Feb 2 16:40:56 2010 -0500

    Merge branch 'libchamplain-0-4'
    
    Conflicts:
    	NEWS
    	champlain/champlain-cache.c
    	champlain/champlain-map-source-factory.h
    	champlain/champlain-network-map-source.c
    	champlain/champlain-view.c
    	configure.ac

 .gitignore                                     |    4 +-
 NEWS                                           |   28 ++-
 bindings/python/champlain/Makefile.am          |    3 +
 bindings/python/champlain/pychamplain.override |   37 +++
 bindings/python/demos/launcher-gtk.py          |   17 +-
 champlain-gtk/gtk-champlain-embed.c            |    4 +-
 champlain/champlain-file-cache.c               |   11 +-
 champlain/champlain-map-source-factory.h       |   10 +
 champlain/champlain-view.c                     |  404 ++++++++++++++----------
 champlain/champlain-view.h                     |    3 +-
 configure.ac                                   |   12 +
 demos/animated-marker.c                        |    2 +-
 demos/launcher-gtk.c                           |    3 +-
 demos/launcher.c                               |    2 +-
 demos/polygons.c                               |    2 +-
 demos/url-marker.c                             |    2 +-
 tidy/tidy-finger-scroll.c                      |   17 +
 17 files changed, 375 insertions(+), 186 deletions(-)
---
diff --cc NEWS
index 97ffd89,2669666..75e1bb0
--- a/NEWS
+++ b/NEWS
@@@ -1,51 -1,34 +1,77 @@@
 +libchamplain 0.5.1 (2010-01-25)
 +===============================
 +
 +Unstable release
 +Bindings have not been updated yet.
 +The API is not settled yet.
 +
 +Fixes:
 +* Disable Python and Instrospection until libMemphis gets bindings
 +  (Pierre-Luc Beaudoin)
 +* Add missing files in tarball (Pierre-Luc Beaudoin)
 +* Remove generated headers from build (Götz Waschk)
 +
 +libchamplain 0.5.0 (2010-01-24)
 +===============================
 +
 +Unstable release
 +Bindings have not been updated yet.
 +The API is not settled yet.
 +
 +Additions:
 +* Add local rendering of OpenStreetMap data (Simon Wenner, Google Summer of
 +  Code 2009)
 +* New map source infrastructure (à la Pipe and Filter) (JiÅ?í Techet)
 +* ChamplainPolygon and ChamplainMapSource inherit from GInitiallyUnowned
 +
- libchamplain 0.4.3 (2010-01-09)
+ libchamplain 0.4.4 (2010-01-28)
  ===============================
  
  Stable release
  GObject-introspection, Python (Victor Godoy Poluceno) and Perl
  (Emmanuel Rodriguez) bindings are in a working state.
  
+ Fixes:
+ * 580372: Get rid of champlain_view_set_size, replaced by
+   clutter_actor_set_size (Pierre-luc Beaudoin)
+ * Fix the Python demos to use ChamplainEmbed instead of ClutterEmbed (Lorenzo
+   Masini)
+ * Ship all the python .defs files in tarballs, fixes the Python bindings
+   (Lorenzo Masini)
+ * Simplification of the internal licence displaying code (Pierre-Luc Beaudoin)
+ * Optimize the initialization process, now loads only the needed tiles
+   (Pierre-Luc Beaudoin)
+ * Limit further more the number of times the scale is redrawn (Pierre-Luc
+   Beaudoin)
+ * Limit the internal operations that happen when the user moves (Pierre-Luc
+   Beaudoin)
+ * Async loading of tiles from the cache (Pierre-Luc Beaudoin)
+ * Use the shared tile cache paths when built with --enable-maemo (Pierre-Luc
+   Beaudoin)
+ * 606393: The display of tiles looks very programmatic, they now load in spiral
+   order (Jason Woofenden)
+ * Limit the number of times notify::latitude and notify::longitude are emited
+   (Pierre-Luc Beaudoin)
+ 
+ libchamplain 0.4.3 (2010-01-09)
+ ===============================
++
 +Additions:
 +* A scale (Tollef Fog Heen and Pierre-Luc Beaudoin)
 +* User configurable additional license text to view
 +* An option to highligh points in Polygons (Andreas Henriksson)
 +
 +Fixes:
 +* Also report as loading when validating the cache
 +* Debian Bug #555082: fix linking with gold (Laurent Bigonville)
 +* 604784: SIGSEGV in function cairo_set_operator
 +* Fix segfault when polygon has been delete before redraw
 +* Make queue_redraw visible to subclasses
 +* Some memory usage improvements by reusing images already loaded
 +* Don't eat clicks on the map when using ChamplainSelectionLayer
++
 +libchamplain 0.4.3 (2010-01-09)
 +===============================
  
  Stable release
  GObject-introspection, Python (Victor Godoy Poluceno) and Perl
diff --cc champlain/champlain-file-cache.c
index a9c69d4,0000000..89a041d
mode 100644,000000..100644
--- a/champlain/champlain-file-cache.c
+++ b/champlain/champlain-file-cache.c
@@@ -1,1010 -1,0 +1,1017 @@@
 +/*
 + * Copyright (C) 2009 Pierre-Luc Beaudoin <pierre-luc pierlux com>
 + * 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
 + */
 +
 +/**
 + * SECTION:champlain-file-cache
 + * @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
 + * is taken into account when purging the cache.
 + */
 +
 +#define DEBUG_FLAG CHAMPLAIN_DEBUG_CACHE
 +#include "champlain-debug.h"
 +
 +#include "champlain-file-cache.h"
 +
 +#include <sqlite3.h>
 +#include <errno.h>
 +#include <glib.h>
 +#include <gio/gio.h>
 +#include <string.h>
 +#include <stdlib.h>
 +
 +G_DEFINE_TYPE (ChamplainFileCache, champlain_file_cache, CHAMPLAIN_TYPE_TILE_CACHE);
 +
 +#define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHAMPLAIN_TYPE_FILE_CACHE, ChamplainFileCachePrivate))
 +
 +enum
 +{
 +  PROP_0,
 +  PROP_SIZE_LIMIT,
 +  PROP_CACHE_DIR
 +};
 +
 +typedef struct _ChamplainFileCachePrivate ChamplainFileCachePrivate;
 +
 +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);
 +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 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 clean (ChamplainTileCache *tile_cache);
 +
 +static void
 +champlain_file_cache_get_property (GObject *object,
 +                                   guint property_id,
 +                                   GValue *value,
 +                                   GParamSpec *pspec)
 +{
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE (object);
 +
 +  switch (property_id)
 +    {
 +    case PROP_SIZE_LIMIT:
 +      g_value_set_uint (value, champlain_file_cache_get_size_limit (file_cache));
 +      break;
 +    case PROP_CACHE_DIR:
 +      g_value_set_string (value, champlain_file_cache_get_cache_dir (file_cache));
 +      break;
 +    default:
 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 +    }
 +}
 +
 +static void
 +champlain_file_cache_set_property (GObject *object,
 +                                   guint property_id,
 +                                   const GValue *value,
 +                                   GParamSpec *pspec)
 +{
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE (object);
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (object);
 +
 +  switch (property_id)
 +    {
 +    case PROP_SIZE_LIMIT:
 +      champlain_file_cache_set_size_limit (file_cache, g_value_get_uint (value));
 +      break;
 +    case PROP_CACHE_DIR:
 +      priv->cache_dir = g_strdup (g_value_get_string (value));
 +      break;
 +    default:
 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 +    }
 +}
 +
 +static void
 +champlain_file_cache_dispose (GObject *object)
 +{
 +  G_OBJECT_CLASS (champlain_file_cache_parent_class)->dispose (object);
 +}
 +
 +static void
 +finalize_sql (ChamplainFileCache *file_cache)
 +{
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  gint error;
 +
 +  if (priv->stmt_select)
 +    {
 +      sqlite3_finalize (priv->stmt_select);
 +      priv->stmt_select = NULL;
 +    }
 +
 +  if (priv->stmt_update)
 +    {
 +      sqlite3_finalize (priv->stmt_update);
 +      priv->stmt_update = NULL;
 +    }
 +
 +  if (priv->db)
 +    {
 +      error = sqlite3_close (priv->db);
 +      if (error != SQLITE_OK)
 +        DEBUG ("Sqlite returned error %d when closing cache.db", error);
 +      priv->db = NULL;
 +    }
 +}
 +
 +static void
 +delete_temp_cache (ChamplainFileCache *file_cache)
 +{
 +  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE(file_cache);
 +
 +  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 = GET_PRIVATE (file_cache);
 +
 +  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);
 +}
 +
 +static void
 +init_cache  (ChamplainFileCache *file_cache)
 +{
 +  ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  gchar *filename = NULL;
 +  gchar *error_msg = NULL;
 +  gint error;
 +
 +  /* If needed, create the cache's dirs */
 +  if (priv->cache_dir)
 +    {
 +      if (g_mkdir_with_parents (priv->cache_dir, 0700) == -1 && errno != EEXIST)
 +        {
 +          g_warning ("Unable to create the image cache path '%s': %s",
 +                     priv->cache_dir, g_strerror (errno));
 +          return;
 +        }
 +    }
 +
 +  if (champlain_tile_cache_get_persistent (tile_cache))
 +    priv->real_cache_dir = g_strdup (priv->cache_dir);
 +  else
 +    {
 +      /* Create temporary directory for non-persistent caches */
 +      gchar *tmplate = NULL;
 +
 +      if (priv->cache_dir)
 +        tmplate = g_build_filename (priv->cache_dir, "champlain-XXXXXX", NULL);
 +      else
 +        tmplate = g_build_filename (g_get_tmp_dir (), "champlain-XXXXXX", NULL);
 +
 +      priv->real_cache_dir = mkdtemp (tmplate);
 +
 +      if (!priv->real_cache_dir)
 +        {
 +          g_warning ("Filed to create filename for temporary cache");
 +          g_free (tmplate);
 +        }
 +    }
 +
 +  g_return_if_fail (priv->real_cache_dir);
 +
 +  filename = g_build_filename (priv->real_cache_dir,
 +                               "cache.db", NULL);
 +  error = sqlite3_open_v2 (filename, &priv->db,
 +                           SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
 +  g_free (filename);
 +
 +  if (error == SQLITE_ERROR)
 +    {
 +      DEBUG ("Sqlite returned error %d when opening cache.db", error);
 +      return;
 +    }
 +
 +  sqlite3_exec (priv->db,
 +                "PRAGMA synchronous=OFF;"
 +                "PRAGMA count_changes=OFF;",
 +                NULL, NULL, &error_msg);
 +  if (error_msg != NULL)
 +    {
 +      DEBUG ("Set PRAGMA: %s", error_msg);
 +      sqlite3_free (error_msg);
 +      return;
 +    }
 +
 +  sqlite3_exec (priv->db,
 +                "CREATE TABLE IF NOT EXISTS tiles ("
 +                "filename TEXT PRIMARY KEY, "
 +                "etag TEXT, "
 +                "popularity INT DEFAULT 1, "
 +                "size INT DEFAULT 0)",
 +                NULL, NULL, &error_msg);
 +  if (error_msg != NULL)
 +    {
 +      DEBUG ("Creating table 'tiles' failed: %s", error_msg);
 +      sqlite3_free (error_msg);
 +      return;
 +    }
 +
 +  error = sqlite3_prepare_v2 (priv->db,
 +                              "SELECT etag FROM tiles WHERE filename = ?", -1,
 +                              &priv->stmt_select, NULL);
 +  if (error != SQLITE_OK)
 +    {
 +      priv->stmt_select = NULL;
 +      DEBUG ("Failed to prepare the select Etag statement, error:%d: %s",
 +             error, sqlite3_errmsg (priv->db));
 +      return;
 +    }
 +
 +  error = sqlite3_prepare_v2 (priv->db,
 +                              "UPDATE tiles SET popularity = popularity + 1 WHERE filename = ?", -1,
 +                              &priv->stmt_update, NULL);
 +  if (error != SQLITE_OK)
 +    {
 +      priv->stmt_update = NULL;
 +      DEBUG ("Failed to prepare the update popularity statement, error: %s",
 +             sqlite3_errmsg (priv->db));
 +      return;
 +    }
 +
 +  g_object_notify (G_OBJECT (file_cache), "cache-dir");
 +}
 +
 +static void
 +champlain_file_cache_constructed  (GObject *object)
 +{
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
 +
 +  init_cache (file_cache);
 +
 +  G_OBJECT_CLASS (champlain_file_cache_parent_class)->constructed (object);
 +}
 +
 +static void
 +champlain_file_cache_class_init (ChamplainFileCacheClass *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 (ChamplainFileCachePrivate));
 +
 +  object_class->finalize = champlain_file_cache_finalize;
 +  object_class->dispose = champlain_file_cache_dispose;
 +  object_class->get_property = champlain_file_cache_get_property;
 +  object_class->set_property = champlain_file_cache_set_property;
 +  object_class->constructed = champlain_file_cache_constructed;
 +
 +  /**
 +  * ChamplainFileCache: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,
 +                             100000000,
 +                             G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
 +  g_object_class_install_property (object_class, PROP_SIZE_LIMIT, pspec);
 +
 +  /**
 +  * ChamplainFileCache:cache-dir:
 +  *
 +  * The directory where the tile database is stored.
 +  *
 +  * Since: 0.6
 +  */
 +  pspec = g_param_spec_string ("cache-dir",
 +                               "Cache Directory",
 +                               "The directory of the cache",
 +                               "",
 +                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
 +  g_object_class_install_property (object_class, PROP_CACHE_DIR, 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;
 +  tile_cache_class->clean = clean;
 +
 +  map_source_class->fill_tile = fill_tile;
 +}
 +
 +static void
 +champlain_file_cache_init (ChamplainFileCache *file_cache)
 +{
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +
 +  priv->size_limit = 0;
 +  priv->cache_dir = NULL;
 +
 +  priv->real_cache_dir = NULL;
 +  priv->db = NULL;
 +  priv->stmt_select = NULL;
 +  priv->stmt_update = NULL;
 +}
 +
 +/**
 + * champlain_file_cache_new:
 + *
 + * Default constructor of #ChamplainFileCache.
 + *
 + * Returns: a constructed permanent cache of maximal size 100000000 B inside
 + * ~/.cache/champlain.
 + *
 + * Since: 0.6
 + */
 +ChamplainFileCache* champlain_file_cache_new (void)
 +{
 +  gchar *cache_path;
 +  ChamplainFileCache *cache;
 +
++#ifdef USE_MAEMO
++  cache_path = g_strdup ("/home/user/MyDocs/.Maps/");
++#else
 +  cache_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "champlain", NULL);
++#endif
++
 +  cache = g_object_new (CHAMPLAIN_TYPE_FILE_CACHE, "size-limit", 100000000,
 +                        "cache-dir", cache_path, "persistent-cache", TRUE, NULL);
 +  g_free (cache_path);
 +
 +  return cache;
 +}
 +
 +/**
 + * champlain_file_cache_new_full:
 + * @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.
 + *
 + * Constructor of #ChamplainFileCache.
 + *
 + * Returns: a constructed #ChamplainFileCache
 + *
 + * Since: 0.6
 + */
 +ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
 +    const gchar *cache_dir, gboolean persistent)
 +{
 +  ChamplainFileCache * cache;
 +  cache = g_object_new (CHAMPLAIN_TYPE_FILE_CACHE, "size-limit", size_limit,
 +                        "cache-dir", cache_dir, "persistent-cache", persistent, NULL);
 +  return cache;
 +}
 +
 +/**
 + * champlain_file_cache_get_size_limit:
 + * @file_cache: a #ChamplainCache
 + *
 + * Gets the cache size limit in bytes.
 + *
 + * Returns: size limit
 + *
 + * Since: 0.4
 + */
 +guint
 +champlain_file_cache_get_size_limit (ChamplainFileCache *file_cache)
 +{
 +  g_return_val_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache), 0);
 +
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  return priv->size_limit;
 +}
 +
 +/**
 + * champlain_file_cache_get_cache_dir:
 + * @file_cache: a #ChamplainCache
 + *
 + * Gets the directory where the cache database is stored.
 + *
 + * Returns: the directory
 + *
 + * Since: 0.6
 + */
 +const gchar *
 +champlain_file_cache_get_cache_dir (ChamplainFileCache *file_cache)
 +{
 +  g_return_val_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache), NULL);
 +
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE(file_cache);
 +  return priv->cache_dir;
 +}
 +
 +/**
 + * champlain_file_cache_set_size_limit:
 + * @file_cache: a #ChamplainCache
 + * @size_limit: the cache limit in bytes
 + *
 + * Sets the cache size limit in bytes.
 + *
 + * Since: 0.4
 + */
 +void
 +champlain_file_cache_set_size_limit (ChamplainFileCache *file_cache,
 +                                     guint size_limit)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
 +
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +
 +  priv->size_limit = size_limit;
 +  g_object_notify (G_OBJECT (file_cache), "size-limit");
 +}
 +
 +static gchar *
 +get_filename (ChamplainFileCache *file_cache, ChamplainTile *tile)
 +{
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (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);
 +
 +  ChamplainMapSource* map_source = CHAMPLAIN_MAP_SOURCE(file_cache);
 +
 +  gchar *filename = g_strdup_printf ("%s" G_DIR_SEPARATOR_S
 +                                     "%s" G_DIR_SEPARATOR_S
 +                                     "%d" G_DIR_SEPARATOR_S
 +                                     "%d" G_DIR_SEPARATOR_S "%d.png",
 +                                     priv->real_cache_dir,
 +                                     champlain_map_source_get_id (map_source),
 +                                     champlain_tile_get_zoom_level (tile),
 +                                     champlain_tile_get_x (tile),
 +                                     champlain_tile_get_y (tile));
 +  return filename;
 +}
 +
 +static gboolean
 +tile_is_expired (ChamplainFileCache *file_cache, ChamplainTile *tile)
 +{
 +  g_return_val_if_fail (CHAMPLAIN_FILE_CACHE (file_cache), FALSE);
 +  g_return_val_if_fail (CHAMPLAIN_TILE (tile), FALSE);
 +
 +  GTimeVal now = {0, };
 +  const GTimeVal *modified_time = champlain_tile_get_modified_time (tile);
 +  gboolean validate_cache = TRUE;
 +
 +  if (modified_time)
 +    {
 +      g_get_current_time (&now);
 +      g_time_val_add (&now, (-24ul * 60ul * 60ul * 1000ul * 1000ul * 7ul)); // Cache expires in 7 days
 +      validate_cache = modified_time->tv_sec < now.tv_sec;
 +    }
 +
 +  DEBUG ("%p is %s expired", tile, (validate_cache ? "": "not"));
 +
 +  return validate_cache;
 +}
 +
 +static void
 +fill_tile (ChamplainMapSource *map_source,
 +           ChamplainTile *tile)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (map_source));
 +  g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
 +
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(map_source);
 +  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  GFileInfo *info = NULL;
 +  GFile *file = NULL;
 +  GError *gerror = NULL;
 +  ClutterActor *actor = NULL;
 +  gchar *filename = NULL;
 +  GTimeVal modified_time = {0,};
 +
 +  if (champlain_tile_get_content (tile))
 +    /* Previous cache in the chain is validating its contents */
 +    goto load_next;
 +
 +  filename = get_filename (file_cache, tile);
 +  DEBUG ("fill of %s", filename);
 +
 +  /* Load the cached version */
-   actor = clutter_texture_new_from_file (filename, &gerror);
++  actor = clutter_texture_new ();
++  clutter_texture_set_load_async (CLUTTER_TEXTURE (actor), TRUE);
++  clutter_texture_set_from_file (CLUTTER_TEXTURE (actor), filename, &gerror);
 +  if (actor)
 +    {
 +      champlain_tile_set_content (tile, actor, FALSE);
 +      champlain_tile_set_size (tile, champlain_map_source_get_tile_size (map_source));
 +    }
 +  else
 +    {
 +      DEBUG ("Failed to load tile %s, error: %s",
 +             filename, gerror->message);
 +      g_error_free (gerror);
 +      goto load_next;
 +    }
 +
 +  /* Retrieve modification time */
 +  file = g_file_new_for_path (filename);
 +  info = g_file_query_info (file,
-                             G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_ETAG_VALUE,
++                            G_FILE_ATTRIBUTE_TIME_MODIFIED,
 +                            G_FILE_QUERY_INFO_NONE, NULL, NULL);
 +  if (info)
 +    {
 +      g_file_info_get_modification_time (info, &modified_time);
 +      champlain_tile_set_modified_time (tile, &modified_time);
 +    }
 +  g_object_unref (file);
 +  g_object_unref (info);
 +
 +  /* Notify other caches that the tile has been filled */
 +  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
 +    {
 +      on_tile_filled (CHAMPLAIN_TILE_CACHE(next_source), tile);
 +    }
 +
 +  if (tile_is_expired (file_cache, tile))
 +    {
 +      int sql_rc = SQLITE_OK;
 +
 +      /* Retrieve etag */
 +      sql_rc = sqlite3_bind_text (priv->stmt_select, 1, filename, -1, SQLITE_STATIC);
 +      if (sql_rc == SQLITE_ERROR)
 +        {
 +          DEBUG ("Failed to prepare the SQL query for finding the Etag of '%s', error: %s",
 +                 filename, sqlite3_errmsg (priv->db));
 +          goto load_next;
 +        }
 +
 +      sql_rc = sqlite3_step (priv->stmt_select);
 +      if (sql_rc == SQLITE_ROW)
 +        {
 +          const gchar *etag = (const gchar *) sqlite3_column_text (priv->stmt_select, 0);
 +          champlain_tile_set_etag (CHAMPLAIN_TILE (tile), etag);
 +        }
 +      else if (sql_rc == SQLITE_DONE)
 +        {
 +          DEBUG ("'%s' does't have an etag",
 +                 filename);
 +          goto load_next;
 +        }
 +      else if (sql_rc == SQLITE_ERROR)
 +        {
 +          DEBUG ("Failed to finding the Etag of '%s', %d error: %s",
 +                 filename, sql_rc, sqlite3_errmsg (priv->db));
 +          goto load_next;
 +        }
 +
 +      /* Validate the tile */
 +      /* goto load_next; */
 +    }
 +  else
 +    {
 +      /* Tile loaded and no validation needed - done */
 +      champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
 +      goto cleanup;
 +    }
 +
 +load_next:
 +  if (CHAMPLAIN_IS_MAP_SOURCE(next_source))
 +    champlain_map_source_fill_tile (next_source, tile);
 +
 +  /* if we have some content, use the tile even if it wasn't validated */
 +  if (champlain_tile_get_content (tile) &&
 +      champlain_tile_get_state (tile) != CHAMPLAIN_STATE_DONE)
 +    champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
 +
 +cleanup:
 +  sqlite3_reset (priv->stmt_select);
 +  g_free (filename);
 +}
 +
 +static void
 +refresh_tile_time (ChamplainTileCache *tile_cache, ChamplainTile *tile)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (tile_cache));
 +
 +  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(tile_cache);
 +  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
 +  gchar *filename = NULL;
 +  GFile *file;
 +  GFileInfo *info;
 +
 +  filename = get_filename (file_cache, tile);
 +  file = g_file_new_for_path (filename);
 +  g_free (filename);
 +
 +  info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
 +                            G_FILE_QUERY_INFO_NONE, NULL, NULL);
 +
 +  if (info)
 +    {
 +      GTimeVal now = {0, };
 +
 +      g_get_current_time (&now);
 +
 +      g_file_info_set_modification_time (info, &now);
 +      g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, NULL);
 +    }
 +
 +  g_object_unref (file);
 +  g_object_unref (info);
 +
 +  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
 +    {
 +      refresh_tile_time (CHAMPLAIN_TILE_CACHE(next_source), tile);
 +    }
 +}
 +
 +static void
 +store_tile (ChamplainTileCache *tile_cache,
 +            ChamplainTile *tile,
 +            const gchar *contents,
 +            gsize size)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (tile_cache));
 +
 +  ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE(tile_cache);
 +  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  gchar *query = NULL;
 +  gchar *error = NULL;
 +  gchar *path = NULL;
 +  gchar *filename = NULL;
 +  GError *gerror = NULL;
 +  GFile *file;
 +  GFileOutputStream *ostream;
 +  gsize bytes_written;
 +
 +  DEBUG ("Update of %p", tile);
 +
 +  filename = get_filename (file_cache, tile);
 +  file = g_file_new_for_path (filename);
 +
 +  /* If the file exists, delete it */
 +  g_file_delete (file, NULL, NULL);
 +
 +  /* If needed, create the cache's dirs */
 +  path = g_path_get_dirname (filename);
 +  if (g_mkdir_with_parents (path, 0700) == -1)
 +    {
 +      if (errno != EEXIST)
 +        {
 +          g_warning ("Unable to create the image cache path '%s': %s",
 +                     path, g_strerror (errno));
 +          goto store_next;
 +        }
 +    }
 +
 +  ostream = g_file_create (file, G_FILE_CREATE_PRIVATE, NULL, &gerror);
 +  if (!ostream)
 +    {
 +      DEBUG ("GFileOutputStream creation failed: %s", gerror->message);
 +      g_error_free (gerror);
 +      goto store_next;
 +    }
 +
 +  /* Write the cache */
 +  if (!g_output_stream_write_all (G_OUTPUT_STREAM(ostream), contents, size, &bytes_written, NULL, &gerror))
 +    {
 +      DEBUG ("Writing file contents failed: %s", gerror->message);
 +      g_error_free (gerror);
 +      g_object_unref (ostream);
 +      goto store_next;
 +    }
 +
 +  g_object_unref (ostream);
 +
 +  query = sqlite3_mprintf ("REPLACE INTO tiles (filename, etag, size) VALUES (%Q, %Q, %d)",
 +                           filename,
 +                           champlain_tile_get_etag (tile),
 +                           size);
 +  sqlite3_exec (priv->db, query, NULL, NULL, &error);
 +  if (error != NULL)
 +    {
 +      DEBUG ("Saving Etag and size failed: %s", error);
 +      sqlite3_free (error);
 +    }
 +  sqlite3_free (query);
 +
 +store_next:
 +  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
 +    {
 +      store_tile (CHAMPLAIN_TILE_CACHE(next_source), tile, contents, size);
 +    }
 +
 +  g_free (filename);
 +  g_free (path);
 +  g_object_unref (file);
 +}
 +
 +static void
 +on_tile_filled (ChamplainTileCache *tile_cache, ChamplainTile *tile)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_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);
 +  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  int sql_rc = SQLITE_OK;
 +  gchar *filename = NULL;
 +
 +  filename = get_filename (file_cache, tile);
 +
 +  DEBUG ("popularity of %s", filename);
 +
 +  sql_rc = sqlite3_bind_text (priv->stmt_update, 1, filename, -1, SQLITE_STATIC);
 +  if (sql_rc != SQLITE_OK)
 +    {
 +      DEBUG ("Failed to set values to the popularity query of '%s', error: %s",
 +             filename, sqlite3_errmsg (priv->db));
 +      goto call_next;
 +    }
 +
 +  sql_rc = sqlite3_step (priv->stmt_update);
 +  if (sql_rc != SQLITE_DONE)
 +    {
 +      /* may not be present in this cache */
 +      goto call_next;
 +    }
 +
 +call_next:
 +  if (CHAMPLAIN_IS_TILE_CACHE(next_source))
 +    {
 +      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 = GET_PRIVATE (file_cache);
 +
 +  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)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
 +  gchar *query, *error = NULL;
 +  GError *gerror = NULL;
 +  GFile *file;
 +
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +
 +  query = sqlite3_mprintf ("DELETE FROM tiles WHERE filename = %Q", filename);
 +  sqlite3_exec (priv->db, query, NULL, NULL, &error);
 +  if (error != NULL)
 +    {
 +      DEBUG ("Deleting tile from db failed: %s", error);
 +      sqlite3_free (error);
 +    }
 +  sqlite3_free (query);
 +
 +  file = g_file_new_for_path (filename);
 +  if (!g_file_delete (file, NULL, &gerror))
 +    {
 +      DEBUG ("Deleting tile from disk failed: %s", gerror->message);
 +      g_error_free (gerror);
 +    }
 +  g_object_unref (file);
 +}
 +
 +static gboolean
 +purge_on_idle (gpointer data)
 +{
 +  champlain_file_cache_purge (CHAMPLAIN_FILE_CACHE (data));
 +  return FALSE;
 +}
 +
 +/**
 + * champlain_file_cache_purge_on_idle:
 + * @file_cache: a #ChamplainCache
 + *
 + * Purge the cache from the less popular tiles until cache's size limit is reached.
 + * This is a non blocking call as the purge will happen when the application is idle
 + *
 + * Since: 0.4
 + */
 +void
 +champlain_file_cache_purge_on_idle (ChamplainFileCache *file_cache)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
 +  g_idle_add (purge_on_idle, file_cache);
 +}
 +
 +/**
 + * champlain_file_cache_purge:
 + * @file_cache: a #ChamplainCache
 + *
 + * Purge the cache from the less popular tiles until cache's size limit is reached.
 + *
 + * Since: 0.4
 + */
 +void
 +champlain_file_cache_purge (ChamplainFileCache *file_cache)
 +{
 +  g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache));
 +
 +  ChamplainFileCachePrivate *priv = GET_PRIVATE (file_cache);
 +  gchar *query;
 +  sqlite3_stmt *stmt;
 +  int rc = 0;
 +  guint current_size = 0;
 +  guint highest_popularity = 0;
 +  gchar *error;
 +
 +  query = "SELECT SUM (size) FROM tiles";
 +  rc = sqlite3_prepare (priv->db, query, strlen (query), &stmt, NULL);
 +  if (rc != SQLITE_OK)
 +    {
 +      DEBUG ("Can't compute cache size %s", sqlite3_errmsg (priv->db));
 +    }
 +
 +  rc = sqlite3_step (stmt);
 +  if (rc != SQLITE_ROW)
 +    {
 +      DEBUG ("Failed to count the total cache consumption %s",
 +             sqlite3_errmsg (priv->db));
 +      sqlite3_finalize (stmt);
 +      return;
 +    }
 +
 +  current_size = sqlite3_column_int (stmt, 0);
 +  if (current_size < priv->size_limit)
 +    {
 +      DEBUG ("Cache doesn't need to be purged at %d bytes", current_size);
 +      sqlite3_finalize (stmt);
 +      return;
 +    }
 +
 +  sqlite3_finalize (stmt);
 +
 +  /* Ok, delete the less popular tiles until size_limit reached */
 +  query = "SELECT filename, size, popularity FROM tiles ORDER BY popularity";
 +  rc = sqlite3_prepare (priv->db, query, strlen (query), &stmt, NULL);
 +  if (rc != SQLITE_OK)
 +    {
 +      DEBUG ("Can't fetch tiles to delete: %s", sqlite3_errmsg (priv->db));
 +    }
 +
 +  rc = sqlite3_step (stmt);
 +  while (rc == SQLITE_ROW && current_size > priv->size_limit)
 +    {
 +      const char *filename = sqlite3_column_text (stmt, 0);
 +      guint size;
 +
 +      filename = sqlite3_column_text (stmt, 0);
 +      size = sqlite3_column_int (stmt, 1);
 +      highest_popularity = sqlite3_column_int (stmt, 2);
 +      DEBUG ("Deleting %s of size %d", filename, size);
 +
 +      delete_tile (file_cache, filename);
 +
 +      current_size -= size;
 +
 +      rc = sqlite3_step (stmt);
 +    }
 +  DEBUG ("Cache size is now %d", current_size);
 +
 +  sqlite3_finalize (stmt);
 +
 +  query = sqlite3_mprintf ("UPDATE tiles SET popularity = popularity - %d",
 +                           highest_popularity);
 +  sqlite3_exec (priv->db, query, NULL, NULL, &error);
 +  if (error != NULL)
 +    {
 +      DEBUG ("Updating popularity failed: %s", error);
 +      sqlite3_free (error);
 +    }
 +  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, NULL);
 +
 +  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 --cc champlain/champlain-map-source-factory.h
index badfaff,a6e1e0b..accbee5
--- a/champlain/champlain-map-source-factory.h
+++ b/champlain/champlain-map-source-factory.h
@@@ -75,9 -81,8 +83,11 @@@ champlain_map_source_factory_register (
  #define CHAMPLAIN_MAP_SOURCE_OSM_TRANSPORT_MAP "osm-transportmap"
  #define CHAMPLAIN_MAP_SOURCE_OAM "oam"
  #define CHAMPLAIN_MAP_SOURCE_MFF_RELIEF "mff-relief"
+ #endif
+ 
 +#define CHAMPLAIN_MAP_SOURCE_MEMPHIS_LOCAL "memphis-local"
 +#define CHAMPLAIN_MAP_SOURCE_MEMPHIS_NETWORK "memphis-network"
 +
  G_END_DECLS
  
  #endif
diff --cc champlain/champlain-view.c
index bae4953,9e7e2d8..bed6b66
--- a/champlain/champlain-view.c
+++ b/champlain/champlain-view.c
@@@ -1380,17 -1502,16 +1508,19 @@@ champlain_view_init (ChamplainView *vie
        priv->polygon_layer);
    clutter_actor_raise (priv->polygon_layer, priv->map_layer);
  
-   champlain_view_set_size (view, priv->viewport_size.width,
-       priv->viewport_size.height);
- 
-   clutter_actor_raise_top (priv->scale_actor);
    resize_viewport (view);
  
+   /* Setup scale */
+   create_scale (view);
+ 
+   /* Setup license */
+   create_license (view);
+ 
    priv->state = CHAMPLAIN_STATE_DONE;
    g_object_notify (G_OBJECT (view), "state");
 +
 +  g_signal_connect (priv->map_source, "reload-tiles",
 +    G_CALLBACK (view_reload_tiles_cb), view);
  }
  
  static void



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