champlain-zoom-level.c \
champlain-tile.c \
champlain-map-source.c \
- champlain-network-map-source.c
+ champlain-network-map-source.c \
+ champlain-cache.c
noinst_HEADERS = \
champlain-debug.h \
champlain-enum-types.h \
champlain-map-source.h \
champlain-network-map-source.h \
- champlain-version.h
+ champlain-version.h \
+ champlain-cache.h
libchamplain_include_HEADERS = \
champlain.h \
champlain-zoom-level.h \
champlain-base-marker.h \
champlain-marker.h \
- champlain-version.h
+ champlain-version.h \
+ champlain-cache.h
libchamplain_0_3_la_LIBADD = $(DEPS_LIBS) $(SOUP_LIBS) ../tidy/libtidy-1.0.la
--- /dev/null
+/*
+* Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.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
+ */
+
+#include "champlain-cache.h"
+
+#include "champlain-enum-types.h"
+#include "champlain-private.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_DEFINE_TYPE (ChamplainCache, champlain_cache, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHAMPLAIN_TYPE_CACHE, ChamplainCachePrivate))
+
+enum
+{
+ PROP_0,
+ PROP_SIZE_LIMIT,
+};
+
+typedef struct _ChamplainCachePrivate ChamplainCachePrivate;
+
+struct _ChamplainCachePrivate {
+ guint size_limit;
+};
+
+static void
+champlain_cache_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ChamplainCache *self = CHAMPLAIN_CACHE (object);
+ switch (property_id)
+ {
+ case PROP_SIZE_LIMIT:
+ g_value_set_uint (value, champlain_cache_get_size_limit (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+champlain_cache_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ChamplainCache *self = CHAMPLAIN_CACHE (object);
+ switch (property_id)
+ {
+ case PROP_SIZE_LIMIT:
+ champlain_cache_set_size_limit (self, g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+champlain_cache_dispose (GObject *object)
+{
+// ChamplainCachePrivate *priv = GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (champlain_cache_parent_class)->dispose (object);
+}
+
+static void
+champlain_cache_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (champlain_cache_parent_class)->finalize (object);
+}
+
+static void
+champlain_cache_class_init (ChamplainCacheClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ChamplainCachePrivate));
+
+ object_class->get_property = champlain_cache_get_property;
+ object_class->set_property = champlain_cache_set_property;
+ object_class->dispose = champlain_cache_dispose;
+ object_class->finalize = champlain_cache_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_SIZE_LIMIT,
+ g_param_spec_uint ("size-limit",
+ "Size Limit",
+ "The cache's size limit (Mb)",
+ 1,
+ G_MAXINT,
+ 10,
+ G_PARAM_READWRITE));
+}
+
+static void
+champlain_cache_init (ChamplainCache *self)
+{
+ champlain_cache_set_size_limit (self, 10);
+}
+
+ChamplainCache*
+champlain_cache_get_default (void)
+{
+ static ChamplainCache * instance = NULL;
+
+ if (G_UNLIKELY (instance == NULL))
+ {
+ instance = g_object_new (CHAMPLAIN_TYPE_CACHE, NULL);
+ return instance;
+ }
+
+ return g_object_ref (instance);
+}
+
+guint
+champlain_cache_get_size_limit (ChamplainCache *self)
+{
+ g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE);
+
+ ChamplainCachePrivate *priv = GET_PRIVATE (self);
+
+ return priv->size_limit;
+}
+
+void
+champlain_cache_set_size_limit (ChamplainCache *self,
+ guint size_limit)
+{
+ g_return_if_fail(CHAMPLAIN_CACHE (self));
+
+ ChamplainCachePrivate *priv = GET_PRIVATE (self);
+
+ priv->size_limit = size_limit;
+ g_object_notify (G_OBJECT (self), "size-limit");
+}
+
+gboolean
+champlain_cache_fill_tile (ChamplainCache *self,
+ ChamplainTile *tile)
+{
+ g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE);
+ g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE);
+
+ GFileInfo *info;
+ GFile *file;
+ GError *error = NULL;
+ ClutterActor *actor;
+ const gchar *etag = NULL;
+ const gchar *filename;
+ GTimeVal *modified_time = g_new0 (GTimeVal, 1);
+
+ filename = champlain_tile_get_filename (tile);
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ return FALSE;
+
+ 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_QUERY_INFO_NONE, NULL, NULL);
+ g_file_info_get_modification_time (info, modified_time);
+ champlain_tile_set_modified_time (tile, modified_time);
+
+ //TODO: retrieve etag
+ if (etag != NULL)
+ champlain_tile_set_etag (tile, etag);
+
+ /* Load the cached version */
+ actor = clutter_texture_new_from_file (filename, &error);
+ champlain_tile_set_actor (tile, actor);
+
+ g_object_unref (file);
+ g_object_unref (info);
+
+ return TRUE;
+}
+
+gboolean
+champlain_cache_tile_is_expired (ChamplainCache *self,
+ ChamplainTile *tile)
+{
+ g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE);
+ g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE);
+
+ GTimeVal *now = g_new0 (GTimeVal, 1);
+ const GTimeVal *modified_time = champlain_tile_get_modified_time (tile);
+ gboolean validate_cache = FALSE;
+
+ g_get_current_time (now);
+ g_time_val_add (now, (-60ul * 60ul * 1000ul * 1000ul)); // Cache expires 1 hour
+ validate_cache = modified_time->tv_sec < now->tv_sec;
+
+ g_free (now);
+ return validate_cache;
+}
+
+void
+champlain_cache_update_tile (ChamplainCache *self,
+ ChamplainTile *tile)
+{
+
+ //Save the etag
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.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
+ */
+
+#ifndef CHAMPLAIN_CACHE_H
+#define CHAMPLAIN_CACHE_H
+
+#include <champlain/champlain-defines.h>
+
+#include "champlain-tile.h"
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_CACHE champlain_cache_get_type()
+
+#define CHAMPLAIN_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_CACHE, ChamplainCache))
+
+#define CHAMPLAIN_CACHE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_CACHE, ChamplainCacheClass))
+
+#define CHAMPLAIN_IS_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_CACHE))
+
+#define CHAMPLAIN_IS_CACHE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_CACHE))
+
+#define CHAMPLAIN_CACHE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_CACHE, ChamplainCacheClass))
+
+typedef struct {
+ GObject parent;
+} ChamplainCache;
+
+typedef struct {
+ GObjectClass parent_class;
+} ChamplainCacheClass;
+
+GType champlain_cache_get_type (void);
+
+ChamplainCache* champlain_cache_get_default (void);
+
+void champlain_cache_update_tile (ChamplainCache *self,
+ ChamplainTile *tile);
+gboolean champlain_cache_fill_tile (ChamplainCache *self,
+ ChamplainTile *tile);
+gboolean champlain_cache_tile_is_expired (ChamplainCache *self,
+ ChamplainTile *tile);
+
+void champlain_cache_set_size_limit (ChamplainCache *self,
+ guint size_limit);
+guint champlain_cache_get_size_limit (ChamplainCache *self);
+
+void champlain_cache_purge (ChamplainCache *self);
+
+G_END_DECLS
+
+#endif /* CHAMPLAIN_CACHE_H */
+
#include "champlain-debug.h"
#include "champlain.h"
+#include "champlain-cache.h"
#include "champlain-defines.h"
#include "champlain-enum-types.h"
#include "champlain-map-source.h"
ClutterActor *actor, *previous_actor = NULL;
GFile *file;
GFileInfo *info;
+ ChamplainCache *cache = champlain_cache_get_default ();
filename = champlain_tile_get_filename (ctx->tile);
if (etag != NULL)
{
- file = g_file_new_for_path (filename);
- info = g_file_query_info (file, G_FILE_ATTRIBUTE_ETAG_VALUE,
- G_FILE_QUERY_INFO_NONE, NULL, NULL);
- g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
- g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL,
- NULL);
-
- g_object_unref (file);
- g_object_unref (info);
+ champlain_tile_set_etag (ctx->tile, etag);
}
+ champlain_cache_update_tile (cache, ctx->tile);
/* Load the image into clutter */
GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
gchar* filename;
gboolean in_cache = FALSE;
gboolean validate_cache = FALSE;
- GTimeVal *modified_time = g_new0 (GTimeVal, 1);
- gchar * etag = NULL;
ChamplainNetworkMapSource *network_map_source = CHAMPLAIN_NETWORK_MAP_SOURCE (map_source);
ChamplainNetworkMapSourcePrivate *priv = network_map_source->priv;
+ ChamplainCache *cache = champlain_cache_get_default ();
/* Try the cached version first */
filename = get_filename (network_map_source, zoom_level, tile);
champlain_tile_set_filename (tile, filename);
champlain_tile_set_size (tile, champlain_map_source_get_tile_size (map_source));
- if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ in_cache = champlain_cache_fill_tile (cache, tile);
+
+ if (in_cache == TRUE)
{
- GTimeVal *now = g_new0 (GTimeVal, 1);
- GFileInfo *info;
- GFile *file;
- GError *error = NULL;
- ClutterActor *actor;
- const gchar *etag_tmp;
-
- in_cache = TRUE;
-
- /* Verify since when is the file in cache */
- 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_QUERY_INFO_NONE, NULL, NULL);
- g_file_info_get_modification_time (info, modified_time);
- etag_tmp = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE);
- if (etag_tmp != NULL)
- etag = g_strdup (etag_tmp);
-
- g_get_current_time (now);
- g_time_val_add (now, (-60ul * 60ul * 1000ul * 1000ul)); // Cache expires 1 hour
- validate_cache = modified_time->tv_sec < now->tv_sec;
- validate_cache = TRUE;
-
- g_object_unref (file);
- g_object_unref (info);
- g_free (now);
-
- /* Load the cached version */
- actor = clutter_texture_new_from_file (filename, &error);
- champlain_tile_set_actor (tile, actor);
+ validate_cache = champlain_cache_tile_is_expired (cache, tile);
if (validate_cache == TRUE)
champlain_tile_set_state (tile, CHAMPLAIN_STATE_VALIDATING_CACHE);
if (in_cache == TRUE)
{
- char value [100];
- struct tm *other_time = gmtime (&modified_time->tv_sec);
-
- strftime (value, 100, "%a, %d %b %Y %T %Z", other_time);
+ const gchar *etag;
+ gchar *date;
- DEBUG("If-Modified-Since %s", value);
+ date = champlain_tile_get_modified_time_string (tile);
+ DEBUG("If-Modified-Since %s", date);
soup_message_headers_append (msg->request_headers,
- "If-Modified-Since", value);
+ "If-Modified-Since", date);
+ g_free (date);
+ etag = champlain_tile_get_etag (tile);
if (etag != NULL)
{
DEBUG("If-None-Match: %s", etag);
soup_message_headers_append (msg->request_headers,
"If-None-Match", etag);
}
-
}
soup_session_queue_message (soup_session, msg,
/* If a tile is neither in cache or can be fetched, do nothing, it'll show up
* as empty
*/
- g_free (modified_time);
- g_free (etag);
}
PROP_URI,
PROP_FILENAME,
PROP_STATE,
- PROP_ACTOR
+ PROP_ACTOR,
+ PROP_ETAG
};
typedef struct _ChamplainTilePrivate ChamplainTilePrivate;
ChamplainState state;
gchar *filename;
ClutterActor *actor;
+
+ GTimeVal *modified_time;
+ gchar* etag;
};
static void
case PROP_ACTOR:
g_value_set_object (value, champlain_tile_get_actor (self));
break;
+ case PROP_ETAG:
+ g_value_set_string (value, champlain_tile_get_etag (self));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
case PROP_ACTOR:
champlain_tile_set_actor (self, g_value_get_object (value));
break;
+ case PROP_ETAG:
+ champlain_tile_set_etag (self, g_value_get_string (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
"The tile's actor",
CLUTTER_TYPE_ACTOR,
G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_ETAG,
+ g_param_spec_string ("etag",
+ "Entity Tag",
+ "The entity tag of the tile",
+ NULL,
+ G_PARAM_READWRITE));
+
}
static void
priv->size = 0;
priv->uri = g_strdup ("");
priv->filename = g_strdup ("");
+ priv->etag = NULL;
/* Can't call champlain_tile_set_actor (self, NULL); because the assertion will fail */
priv->actor = NULL;
priv->actor = g_object_ref (actor);
g_object_notify (G_OBJECT (self), "actor");
}
+
+const GTimeVal *
+champlain_tile_get_modified_time (ChamplainTile *self)
+{
+ g_return_val_if_fail (CHAMPLAIN_TILE(self), NULL);
+
+ ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+ return priv->modified_time;
+}
+
+void
+champlain_tile_set_modified_time (ChamplainTile *self,
+ GTimeVal *time)
+{
+ g_return_if_fail (CHAMPLAIN_TILE(self));
+ g_return_if_fail (time != NULL);
+
+ ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+ priv->modified_time = time;
+}
+
+char *
+champlain_tile_get_modified_time_string (ChamplainTile *self)
+{
+ g_return_val_if_fail(CHAMPLAIN_TILE(self), NULL);
+ ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+ struct tm *other_time = gmtime (&priv->modified_time->tv_sec);
+ char value [100];
+
+ strftime (value, 100, "%a, %d %b %Y %T %Z", other_time);
+
+ return g_strdup (value);
+}
+
+const char *
+champlain_tile_get_etag (ChamplainTile *self)
+{
+ g_return_val_if_fail(CHAMPLAIN_TILE(self), "");
+ ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+ return priv->etag;
+}
+
+void
+champlain_tile_set_etag (ChamplainTile *self,
+ const gchar *etag)
+{
+ g_return_if_fail(CHAMPLAIN_TILE(self));
+
+ ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+ priv->etag = g_strdup (etag);
+ g_object_notify (G_OBJECT (self), "etag");
+}
G_CONST_RETURN gchar * champlain_tile_get_uri (ChamplainTile *self);
G_CONST_RETURN gchar * champlain_tile_get_filename (ChamplainTile *self);
ClutterActor * champlain_tile_get_actor (ChamplainTile *self);
+const GTimeVal * champlain_tile_get_modified_time (ChamplainTile *self);
+char * champlain_tile_get_modified_time_string (ChamplainTile *self);
+const char * champlain_tile_get_etag (ChamplainTile *self);
void champlain_tile_set_x (ChamplainTile *self, gint x);
void champlain_tile_set_y (ChamplainTile *self, gint y);
void champlain_tile_set_uri (ChamplainTile *self, const gchar *uri);
void champlain_tile_set_filename (ChamplainTile *self, const gchar *filename);
void champlain_tile_set_actor (ChamplainTile *self, ClutterActor* actor);
+void champlain_tile_set_etag (ChamplainTile *self, const gchar *etag);
+void champlain_tile_set_modified_time (ChamplainTile *self, GTimeVal *time);
G_END_DECLS