[libchamplain] Fix various problems with asynchronous tile loading



commit ec6fa72c18daed6b68052d024800db8339f5dc2c
Author: JiÅ?í Techet <techet gmail com>
Date:   Sun Aug 15 19:19:12 2010 +0200

    Fix various problems with asynchronous tile loading
    
    Signed-off-by: JiÅ?í Techet <techet gmail com>

 champlain/champlain-file-cache.c               |   54 +++++----
 champlain/champlain-file-tile-source.c         |   66 ++++++++--
 champlain/champlain-image-renderer.c           |    2 +-
 champlain/champlain-memory-cache.c             |   46 ++++++--
 champlain/champlain-memphis-renderer.c         |    3 +-
 champlain/champlain-network-bbox-tile-source.c |   66 ++++++++--
 champlain/champlain-network-tile-source.c      |  154 ++++++++++++++---------
 champlain/champlain-null-tile-source.c         |   67 +++++++++--
 8 files changed, 327 insertions(+), 131 deletions(-)
---
diff --git a/champlain/champlain-file-cache.c b/champlain/champlain-file-cache.c
index 4f340a7..9722a96 100644
--- a/champlain/champlain-file-cache.c
+++ b/champlain/champlain-file-cache.c
@@ -519,9 +519,9 @@ tile_rendered_cb (ChamplainTile *tile,
 {
   ChamplainMapSource *map_source = user_data->map_source;
   GFile *file;
-  ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE (map_source);
-  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
-  ChamplainFileCachePrivate *priv = file_cache->priv;
+  ChamplainFileCache *file_cache;
+  ChamplainMapSource *next_source;
+  ChamplainFileCachePrivate *priv;
   GFileInfo *info = NULL;
   GTimeVal modified_time = { 0, };
   gchar *filename = NULL;
@@ -529,15 +529,19 @@ tile_rendered_cb (ChamplainTile *tile,
   // this frees user_data
   g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, user_data);
 
-  if (!tile)
+  if (!map_source)
     {
-      DEBUG ("Tile destroyed while loading");
-      goto cleanup;
+      DEBUG ("Map source destroyed while loading");
+      return;
     }
 
+  next_source = champlain_map_source_get_next_source (map_source);
+  file_cache = CHAMPLAIN_FILE_CACHE (map_source);
+  priv = file_cache->priv;
+
   if (data->error)
     {
-      DEBUG ("Tile rendering failed: %s", filename);
+      DEBUG ("Tile rendering failed");
       goto load_next;
     }
 
@@ -662,18 +666,15 @@ file_loaded_cb (GFile *file,
   
   g_object_unref (file);
 
-  if (!tile)
+  if (!tile || !map_source)
     {
-      DEBUG ("Tile destroyed while loading");
-      // data already destroyed by destroy_cb_data
-      return;
-    }
+      if (map_source)
+        g_object_remove_weak_pointer (G_OBJECT (map_source), (gpointer *) &user_data->map_source);
 
-  if (!map_source)
-    {
-      DEBUG ("Map source destroyed while loading");
-      // this destros the data by destroy_cb_data
-      g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, user_data);
+      if (tile)
+        g_object_remove_weak_pointer (G_OBJECT (tile), (gpointer *) &user_data->tile);
+
+      g_slice_free (FileLoadedData, user_data);
       return;
     }
 
@@ -681,6 +682,9 @@ file_loaded_cb (GFile *file,
 
   g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
 
+  g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
+          user_data, (GClosureNotify) destroy_cb_data, 0);
+
   champlain_renderer_set_data (renderer, contents, length);
   champlain_renderer_render (renderer, tile);
 }
@@ -693,6 +697,8 @@ fill_tile (ChamplainMapSource *map_source,
   g_return_if_fail (CHAMPLAIN_IS_FILE_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)
     {
       FileLoadedData *user_data;
@@ -711,17 +717,15 @@ fill_tile (ChamplainMapSource *map_source,
 
       DEBUG ("fill of %s", filename);
 
-      g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
-              user_data, (GClosureNotify) destroy_cb_data, 0);
       g_file_load_contents_async (file, NULL, (GAsyncReadyCallback) file_loaded_cb, user_data);
     }
-  else
+  else 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)
     {
-      ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
-
-      /* Previous cache in the chain is validating its contents */
-      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 */
+      champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
+      champlain_tile_display_content (tile);
     }
 }
 
diff --git a/champlain/champlain-file-tile-source.c b/champlain/champlain-file-tile-source.c
index 81c428b..23a6aec 100644
--- a/champlain/champlain-file-tile-source.c
+++ b/champlain/champlain-file-tile-source.c
@@ -141,17 +141,33 @@ champlain_file_tile_source_load_map_data (ChamplainFileTileSource *self,
 }
 
 
+typedef struct
+{
+  ChamplainMapSource *map_source;
+} TileRenderedData;
+
+
 static void
 tile_rendered_cb (ChamplainTile *tile,
     ChamplainRenderCallbackData *data,
-    ChamplainMapSource *map_source)
+    TileRenderedData *user_data)
 {
-  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
-  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
-  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+  ChamplainMapSource *map_source = user_data->map_source;
+  ChamplainMapSource *next_source;
+
+  // frees user_data - must not be used later in the function
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
+  
+  if (!map_source)
+    return;
 
+  next_source = champlain_map_source_get_next_source (map_source);
+  
   if (!data->error)
     {
+      ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
+      ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
+      
       if (tile_cache && data->data)
         champlain_tile_cache_store_tile (tile_cache, tile, data->data, data->size);
 
@@ -161,9 +177,17 @@ tile_rendered_cb (ChamplainTile *tile,
     }
   else if (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
+destroy_cb_data (TileRenderedData *data,
+    G_GNUC_UNUSED GClosure *closure)
+{
+  if (data->map_source)
+    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
+
+  g_slice_free (TileRenderedData, data);
 }
 
 
@@ -174,15 +198,33 @@ fill_tile (ChamplainMapSource *map_source,
   g_return_if_fail (CHAMPLAIN_IS_FILE_TILE_SOURCE (map_source));
   g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
 
-  ChamplainRenderer *renderer;
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+
+  if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADED)
+    {
+      ChamplainRenderer *renderer;
+      TileRenderedData *user_data;
+
+      renderer = champlain_map_source_get_renderer (map_source);
 
-  renderer = champlain_map_source_get_renderer (map_source);
+      g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
 
-  g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
+      user_data = g_slice_new (TileRenderedData);
+      user_data->map_source = map_source;
 
-  g_object_ref (map_source);
+      g_object_add_weak_pointer (G_OBJECT (map_source), (gpointer *) &user_data->map_source);
 
-  g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), map_source);
+      g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
+              user_data, (GClosureNotify) destroy_cb_data, 0);
 
-  champlain_renderer_render (renderer, tile);
+      champlain_renderer_render (renderer, tile);
+    }
+  else 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);
+    }
 }
diff --git a/champlain/champlain-image-renderer.c b/champlain/champlain-image-renderer.c
index e301f99..9dc8130 100644
--- a/champlain/champlain-image-renderer.c
+++ b/champlain/champlain-image-renderer.c
@@ -128,7 +128,7 @@ render (ChamplainRenderer *renderer, ChamplainTile *tile)
           g_warning ("Unable to load the pixbuf: %s", error->message);
           g_error_free (error);
         }
-
+      goto error;
     }
 
   gdk_pixbuf_loader_close (loader, &error);
diff --git a/champlain/champlain-memory-cache.c b/champlain/champlain-memory-cache.c
index 187ef8d..7d03609 100644
--- a/champlain/champlain-memory-cache.c
+++ b/champlain/champlain-memory-cache.c
@@ -254,27 +254,50 @@ delete_queue_member (QueueMember *member, gpointer user_data)
 }
 
 
+typedef struct
+{
+  ChamplainMapSource *map_source;
+} TileRenderedData;
+
+
 static void
 tile_rendered_cb (ChamplainTile *tile,
     ChamplainRenderCallbackData *data,
-    ChamplainMapSource *map_source)
+    TileRenderedData *user_data)
 {
-  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+  ChamplainMapSource *map_source = user_data->map_source;
+  ChamplainMapSource *next_source;
+
+  // frees user_data - must not be used later in the function
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
+  
+  if (!map_source)
+    return;
 
+  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_fade_in (tile, FALSE);
       champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
       champlain_tile_display_content (tile);
     }
-  else if (CHAMPLAIN_IS_MAP_SOURCE (next_source))
+  else if (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
+destroy_cb_data (TileRenderedData *data,
+    G_GNUC_UNUSED GClosure *closure)
+{
+  if (data->map_source)
+    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
+
+  g_slice_free (TileRenderedData, data);
 }
 
 
@@ -300,6 +323,7 @@ fill_tile (ChamplainMapSource *map_source,
       if (link)
         {
           QueueMember *member = link->data;
+          TileRenderedData *user_data;
 
           move_queue_member_to_head (priv->queue, link);
 
@@ -307,8 +331,14 @@ fill_tile (ChamplainMapSource *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);
+          user_data = g_slice_new (TileRenderedData);
+          user_data->map_source = map_source;
+
+          g_object_add_weak_pointer (G_OBJECT (map_source), (gpointer *) &user_data->map_source);
+
+          g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
+                  user_data, (GClosureNotify) destroy_cb_data, 0);
+                  
           champlain_renderer_set_data (renderer, member->data, member->size);
           champlain_renderer_render (renderer, tile);
 
diff --git a/champlain/champlain-memphis-renderer.c b/champlain/champlain-memphis-renderer.c
index d5d1eee..9fecca6 100644
--- a/champlain/champlain-memphis-renderer.c
+++ b/champlain/champlain-memphis-renderer.c
@@ -352,7 +352,8 @@ error:
   callback_data.size = 0;
 
 finish:
-  g_signal_emit_by_name (tile, "render-complete", &callback_data);
+  if (tile)
+    g_signal_emit_by_name (tile, "render-complete", &callback_data);
 
   if (pixbuf)
     g_object_unref (pixbuf);
diff --git a/champlain/champlain-network-bbox-tile-source.c b/champlain/champlain-network-bbox-tile-source.c
index 4b0746c..392fe4d 100644
--- a/champlain/champlain-network-bbox-tile-source.c
+++ b/champlain/champlain-network-bbox-tile-source.c
@@ -366,17 +366,33 @@ champlain_network_bbox_tile_source_load_map_data (
 }
 
 
+typedef struct
+{
+  ChamplainMapSource *map_source;
+} TileRenderedData;
+
+
 static void
 tile_rendered_cb (ChamplainTile *tile,
     ChamplainRenderCallbackData *data,
-    ChamplainMapSource *map_source)
+    TileRenderedData *user_data)
 {
-  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
-  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
-  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+  ChamplainMapSource *map_source = user_data->map_source;
+  ChamplainMapSource *next_source;
+
+  // frees user_data - must not be used later in the function
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
+  
+  if (!map_source)
+    return;
 
+  next_source = champlain_map_source_get_next_source (map_source);
+  
   if (!data->error)
     {
+      ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
+      ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
+      
       if (tile_cache && data->data)
         champlain_tile_cache_store_tile (tile_cache, tile, data->data, data->size);
 
@@ -386,9 +402,17 @@ tile_rendered_cb (ChamplainTile *tile,
     }
   else if (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
+destroy_cb_data (TileRenderedData *data,
+    G_GNUC_UNUSED GClosure *closure)
+{
+  if (data->map_source)
+    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
+
+  g_slice_free (TileRenderedData, data);
 }
 
 
@@ -399,17 +423,35 @@ fill_tile (ChamplainMapSource *map_source,
   g_return_if_fail (CHAMPLAIN_IS_NETWORK_BBOX_TILE_SOURCE (map_source));
   g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
 
-  ChamplainRenderer *renderer;
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+
+  if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADED)
+    {
+      ChamplainRenderer *renderer;
+      TileRenderedData *user_data;
+
+      renderer = champlain_map_source_get_renderer (map_source);
 
-  renderer = champlain_map_source_get_renderer (map_source);
+      g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
 
-  g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
+      user_data = g_slice_new (TileRenderedData);
+      user_data->map_source = map_source;
 
-  g_object_ref (map_source);
+      g_object_add_weak_pointer (G_OBJECT (map_source), (gpointer *) &user_data->map_source);
 
-  g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), map_source);
+      g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
+              user_data, (GClosureNotify) destroy_cb_data, 0);
 
-  champlain_renderer_render (renderer, tile);
+      champlain_renderer_render (renderer, tile);
+    }
+  else 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);
+    }
 }
 
 
diff --git a/champlain/champlain-network-tile-source.c b/champlain/champlain-network-tile-source.c
index 2c5a78f..9eeff25 100644
--- a/champlain/champlain-network-tile-source.c
+++ b/champlain/champlain-network-tile-source.c
@@ -85,13 +85,13 @@ typedef struct
 {
   ChamplainMapSource *map_source;
   ChamplainTile *tile;
-} TileLoadedCallbackData;
+} TileLoadedData;
 
 typedef struct
 {
   ChamplainMapSource *map_source;
   gchar *etag;
-} TileRenderedCallbackData;
+} TileRenderedData;
 
 typedef struct
 {
@@ -524,41 +524,29 @@ get_tile_uri (ChamplainNetworkTileSource *tile_source,
 
 
 static void
-tile_destroyed_cb (G_GNUC_UNUSED ChamplainTile *tile,
-    TileDestroyedCbData *data)
-{
-  if (data->map_source && data->msg)
-    {
-      DEBUG ("Canceling tile download");
-      ChamplainNetworkTileSourcePrivate *priv = CHAMPLAIN_NETWORK_TILE_SOURCE (data->map_source)->priv;
-
-      soup_session_cancel_message (priv->soup_session, data->msg, SOUP_STATUS_CANCELLED);
-    }
-}
-
-
-static void
-destroy_cb_data (TileDestroyedCbData *data,
-    G_GNUC_UNUSED GClosure *closure)
-{
-  if (data->map_source)
-    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
-
-  g_slice_free (TileDestroyedCbData, data);
-}
-
-
-static void
-tile_rendered_cb (ChamplainTile *tile, ChamplainRenderCallbackData *data, TileRenderedCallbackData *user_data)
+tile_rendered_cb (ChamplainTile *tile,
+    ChamplainRenderCallbackData *data,
+    TileRenderedData *user_data)
 {
   ChamplainMapSource *map_source = user_data->map_source;
-  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
-  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
-  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
-  gchar *etag = user_data->etag;
+  ChamplainMapSource *next_source;
+  gchar *etag;
+  
+  etag = g_strdup (user_data->etag);
+
+  // frees user_data - must not be used later in the function
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
+  
+  if (!map_source)
+    return;
 
+  next_source = champlain_map_source_get_next_source (map_source);
+  
   if (!data->error)
     {
+      ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
+      ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
+
       if (etag != NULL)
         champlain_tile_set_etag (tile, etag);
 
@@ -569,15 +557,24 @@ tile_rendered_cb (ChamplainTile *tile, ChamplainRenderCallbackData *data, TileRe
       champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
       champlain_tile_display_content (tile);
     }
-  else
-    {
-      if (next_source)
-        champlain_map_source_fill_tile (next_source, tile);
-    }
+  else if (next_source)
+    champlain_map_source_fill_tile (next_source, tile);
+    
+  g_free (etag);
+}
 
-  g_object_unref (map_source);
-  g_slice_free (TileRenderedCallbackData, user_data);
-  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, user_data);
+
+static void
+destroy_render_complete_data (TileRenderedData *data,
+    G_GNUC_UNUSED GClosure *closure)
+{
+  if (data->map_source)
+    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
+
+  if (data->etag)
+    g_free (data->etag);
+
+  g_slice_free (TileRenderedData, data);
 }
 
 
@@ -586,33 +583,40 @@ tile_loaded_cb (G_GNUC_UNUSED SoupSession *session,
     SoupMessage *msg,
     gpointer user_data)
 {
-  TileLoadedCallbackData *callback_data = (TileLoadedCallbackData *) user_data;
+  TileLoadedData *callback_data = (TileLoadedData *) user_data;
   ChamplainMapSource *map_source = callback_data->map_source;
   ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
   ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
   ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
   ChamplainTile *tile = callback_data->tile;
   const gchar *etag;
-  TileRenderedCallbackData *data;
+  TileRenderedData *data;
   ChamplainRenderer *renderer;
 
-
   if (tile)
     g_object_remove_weak_pointer (G_OBJECT (tile), (gpointer *) &callback_data->tile);
 
-  g_slice_free (TileLoadedCallbackData, callback_data);
+  if (map_source)
+    g_object_remove_weak_pointer (G_OBJECT (map_source), (gpointer *) &callback_data->map_source);
+
+  g_slice_free (TileLoadedData, callback_data);
 
   DEBUG ("Got reply %d", msg->status_code);
 
-  if (!tile || msg->status_code == SOUP_STATUS_CANCELLED)
+  if (!tile)
     {
-      if (!tile)
-        DEBUG ("Tile destroyed while loading");
-      else
-        DEBUG ("Download of tile %d, %d got cancelled",
-            champlain_tile_get_x (tile), champlain_tile_get_y (tile));
-
-      g_object_unref (map_source);
+      DEBUG ("Tile destroyed while loading");
+      return;
+    }
+  else if (msg->status_code == SOUP_STATUS_CANCELLED)
+    {
+      DEBUG ("Download of tile %d, %d got cancelled",
+          champlain_tile_get_x (tile), champlain_tile_get_y (tile));
+      return;
+    }
+  else if (!map_source)
+    {
+      DEBUG ("Map source destroyed");
       return;
     }
 
@@ -637,13 +641,17 @@ tile_loaded_cb (G_GNUC_UNUSED SoupSession *session,
   etag = soup_message_headers_get (msg->response_headers, "ETag");
   DEBUG ("Received ETag %s", etag);
 
-  data = g_slice_new (TileRenderedCallbackData);
+  renderer = champlain_map_source_get_renderer (map_source);
+  g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
+
+  data = g_slice_new (TileRenderedData);
   data->map_source = map_source;
   data->etag = g_strdup (etag);
 
-  renderer = champlain_map_source_get_renderer (map_source);
+  g_object_add_weak_pointer (G_OBJECT (map_source), (gpointer *) &data->map_source);
 
-  g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), data);
+  g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
+          data, (GClosureNotify) destroy_render_complete_data, 0);
 
   champlain_renderer_set_data (renderer, msg->response_body->data, msg->response_body->length);
   champlain_renderer_render (renderer, tile);
@@ -653,14 +661,40 @@ tile_loaded_cb (G_GNUC_UNUSED SoupSession *session,
 load_next:
   if (next_source)
     champlain_map_source_fill_tile (next_source, tile);
-  g_object_unref (map_source);
   return;
 
 finish:
   champlain_tile_set_fade_in (tile, TRUE);
   champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
   champlain_tile_display_content (tile);
-  g_object_unref (map_source);
+}
+
+
+static void
+destroy_cb_data (TileDestroyedCbData *data,
+    G_GNUC_UNUSED GClosure *closure)
+{
+  if (data->map_source)
+    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
+    
+  if (data->msg)
+    g_object_remove_weak_pointer (G_OBJECT (data->msg), (gpointer *) &data->msg);
+
+  g_slice_free (TileDestroyedCbData, data);
+}
+
+
+static void
+tile_destroyed_cb (G_GNUC_UNUSED ChamplainTile *tile,
+    TileDestroyedCbData *data)
+{
+  if (data->map_source && data->msg)
+    {
+      DEBUG ("Canceling tile download");
+      ChamplainNetworkTileSourcePrivate *priv = CHAMPLAIN_NETWORK_TILE_SOURCE (data->map_source)->priv;
+
+      soup_session_cancel_message (priv->soup_session, data->msg, SOUP_STATUS_CANCELLED);
+    }
 }
 
 
@@ -701,7 +735,7 @@ fill_tile (ChamplainMapSource *map_source,
 
   if (!priv->offline)
     {
-      TileLoadedCallbackData *callback_data;
+      TileLoadedData *callback_data;
       SoupMessage *msg;
       gchar *uri;
 
@@ -749,12 +783,12 @@ fill_tile (ChamplainMapSource *map_source,
       g_signal_connect_data (tile, "destroy", G_CALLBACK (tile_destroyed_cb),
           tile_destroyed_cb_data, (GClosureNotify) destroy_cb_data, 0);
 
-      callback_data = g_slice_new (TileLoadedCallbackData);
+      callback_data = g_slice_new (TileLoadedData);
       callback_data->tile = tile;
       callback_data->map_source = map_source;
 
       g_object_add_weak_pointer (G_OBJECT (tile), (gpointer *) &callback_data->tile);
-      g_object_ref (map_source);
+      g_object_add_weak_pointer (G_OBJECT (map_source), (gpointer *) &callback_data->map_source);
 
       soup_session_queue_message (priv->soup_session, msg,
           tile_loaded_cb,
diff --git a/champlain/champlain-null-tile-source.c b/champlain/champlain-null-tile-source.c
index a5e3db9..eec204e 100644
--- a/champlain/champlain-null-tile-source.c
+++ b/champlain/champlain-null-tile-source.c
@@ -75,17 +75,33 @@ champlain_null_tile_source_new_full (ChamplainRenderer *renderer)
 }
 
 
+typedef struct
+{
+  ChamplainMapSource *map_source;
+} TileRenderedData;
+
+
 static void
 tile_rendered_cb (ChamplainTile *tile,
     ChamplainRenderCallbackData *data,
-    ChamplainMapSource *map_source)
+    TileRenderedData *user_data)
 {
-  ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
-  ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
-  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+  ChamplainMapSource *map_source = user_data->map_source;
+  ChamplainMapSource *next_source;
+
+  // frees user_data - must not be used later in the function
+  g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
+  
+  if (!map_source)
+    return;
 
+  next_source = champlain_map_source_get_next_source (map_source);
+  
   if (!data->error)
     {
+      ChamplainTileSource *tile_source = CHAMPLAIN_TILE_SOURCE (map_source);
+      ChamplainTileCache *tile_cache = champlain_tile_source_get_cache (tile_source);
+      
       if (tile_cache && data->data)
         champlain_tile_cache_store_tile (tile_cache, tile, data->data, data->size);
 
@@ -95,9 +111,17 @@ tile_rendered_cb (ChamplainTile *tile,
     }
   else if (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
+destroy_cb_data (TileRenderedData *data,
+    G_GNUC_UNUSED GClosure *closure)
+{
+  if (data->map_source)
+    g_object_remove_weak_pointer (G_OBJECT (data->map_source), (gpointer *) &data->map_source);
+
+  g_slice_free (TileRenderedData, data);
 }
 
 
@@ -108,15 +132,34 @@ fill_tile (ChamplainMapSource *map_source,
   g_return_if_fail (CHAMPLAIN_IS_NULL_TILE_SOURCE (map_source));
   g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
 
-  ChamplainRenderer *renderer;
+  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
+
+  if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADED)
+    {
+      ChamplainRenderer *renderer;
+      TileRenderedData *user_data;
 
-  renderer = champlain_map_source_get_renderer (map_source);
+      renderer = champlain_map_source_get_renderer (map_source);
 
-  g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
+      g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
 
-  g_object_ref (map_source);
+      user_data = g_slice_new (TileRenderedData);
+      user_data->map_source = map_source;
 
-  g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), map_source);
+      g_object_add_weak_pointer (G_OBJECT (map_source), (gpointer *) &user_data->map_source);
 
-  champlain_renderer_render (renderer, tile);
+      g_signal_connect_data (tile, "render-complete", G_CALLBACK (tile_rendered_cb),
+              user_data, (GClosureNotify) destroy_cb_data, 0);
+
+      champlain_renderer_render (renderer, tile);
+    }
+  else 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);
+    }
 }
+



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