[libshumate] map-source: Make the fill_tile vfunc async



commit 7a490bfce5dd387f419764628885af179079f46a
Author: James Westman <james jwestman net>
Date:   Fri Mar 26 15:45:04 2021 -0500

    map-source: Make the fill_tile vfunc async
    
    Previously, fill_tile_async called the original fill_tile virtual
    method, which did not call a callback when it was finished. The async
    callback was implemented by connecting to the tile's notify::state
    signal.
    
    Now, the virtual method itself is async, not just the wrapper function.
    This required some changes to the functions, most notably in
    ShumateNetworkTileSource.

 demos/shumate-test-tile-source.c      |  16 +-
 shumate/shumate-error-tile-source.c   |  94 ++----------
 shumate/shumate-map-source-chain.c    |  20 ++-
 shumate/shumate-map-source.c          |  49 +-----
 shumate/shumate-map-source.h          |  11 +-
 shumate/shumate-network-tile-source.c | 276 ++++++++++++++--------------------
 shumate/shumate-network-tile-source.h |  28 ++++
 shumate/shumate-tile-source.c         |   2 +-
 8 files changed, 183 insertions(+), 313 deletions(-)
---
diff --git a/demos/shumate-test-tile-source.c b/demos/shumate-test-tile-source.c
index 0ff847c..c0a458a 100644
--- a/demos/shumate-test-tile-source.c
+++ b/demos/shumate-test-tile-source.c
@@ -60,11 +60,14 @@ texture_new_for_surface (cairo_surface_t *surface)
 
 
 static void
-shumate_test_tile_source_fill_tile (ShumateMapSource *map_source,
-                                    ShumateTile      *tile,
-                                    GCancellable     *cancellable)
+shumate_test_tile_source_fill_tile_async (ShumateMapSource *map_source,
+                                          ShumateTile *tile,
+                                          GCancellable *cancellable,
+                                          GAsyncReadyCallback callback,
+                                          gpointer user_data)
 {
   g_autoptr(GdkTexture) texture = NULL;
+  g_autoptr(GTask) task = NULL;
   guint tile_size = shumate_tile_get_size (tile);
   int x = shumate_tile_get_x (tile), y = shumate_tile_get_y (tile);
   int zoom = shumate_tile_get_zoom_level (tile);
@@ -76,6 +79,7 @@ shumate_test_tile_source_fill_tile (ShumateMapSource *map_source,
 
   g_return_if_fail (SHUMATE_IS_TEST_TILE_SOURCE (map_source));
   g_return_if_fail (SHUMATE_IS_TILE (tile));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, tile_size, tile_size);
   cr = cairo_create (surface);
@@ -97,6 +101,10 @@ shumate_test_tile_source_fill_tile (ShumateMapSource *map_source,
   shumate_tile_set_texture (tile, texture);
   shumate_tile_set_fade_in (tile, TRUE);
   shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
+
+  task = g_task_new (map_source, cancellable, callback, user_data);
+  g_task_set_source_tag (task, shumate_test_tile_source_fill_tile_async);
+  g_task_return_boolean (task, TRUE);
 }
 
 
@@ -105,7 +113,7 @@ shumate_test_tile_source_class_init (ShumateTestTileSourceClass *klass)
 {
   ShumateMapSourceClass *map_source_class = SHUMATE_MAP_SOURCE_CLASS (klass);
 
-  map_source_class->fill_tile = shumate_test_tile_source_fill_tile;
+  map_source_class->fill_tile_async = shumate_test_tile_source_fill_tile_async;
 }
 
 
diff --git a/shumate/shumate-error-tile-source.c b/shumate/shumate-error-tile-source.c
index 62bace9..fa47850 100644
--- a/shumate/shumate-error-tile-source.c
+++ b/shumate/shumate-error-tile-source.c
@@ -27,97 +27,25 @@
 #include "shumate-debug.h"
 #include "shumate-enum-types.h"
 #include "shumate-tile.h"
-
-static GdkTexture *
-texture_new_for_surface (cairo_surface_t *surface)
-{
-  g_autoptr(GBytes) bytes = NULL;
-  GdkTexture *texture;
-
-  g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
-  g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
-  g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
-
-  bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
-                                      cairo_image_surface_get_height (surface)
-                                      * cairo_image_surface_get_stride (surface),
-                                      (GDestroyNotify) cairo_surface_destroy,
-                                      cairo_surface_reference (surface));
-  
-  texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
-                                    cairo_image_surface_get_height (surface),
-                                    GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
-                                    bytes,
-                                    cairo_image_surface_get_stride (surface));
-
-  return texture;
-}
+#include "shumate-network-tile-source.h"
 
 
 G_DEFINE_TYPE (ShumateErrorTileSource, shumate_error_tile_source, SHUMATE_TYPE_TILE_SOURCE)
 
 
 static void
-fill_tile (ShumateMapSource *map_source,
-           ShumateTile      *tile,
-           GCancellable     *cancellable)
+fill_tile_async (ShumateMapSource *self,
+                 ShumateTile *tile,
+                 GCancellable *cancellable,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
 {
-  g_return_if_fail (SHUMATE_IS_ERROR_TILE_SOURCE (map_source));
+  g_return_if_fail (SHUMATE_IS_ERROR_TILE_SOURCE (self));
   g_return_if_fail (SHUMATE_IS_TILE (tile));
 
-  ShumateMapSource *next_source = shumate_map_source_get_next_source (map_source);
-
-  if (shumate_tile_get_state (tile) == SHUMATE_STATE_DONE)
-    return;
-
-  if (shumate_tile_get_state (tile) != SHUMATE_STATE_LOADED)
-    {
-      g_autoptr(GdkTexture) texture = NULL;
-      guint tile_size = shumate_tile_get_size (tile);
-      cairo_surface_t *surface;
-      cairo_t *cr;
-      cairo_pattern_t *pat;
-
-      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, tile_size, tile_size);
-      cr = cairo_create (surface);
-
-      // draw a linear gray to white pattern
-      pat = cairo_pattern_create_linear (tile_size / 2.0, 0.0, tile_size, tile_size / 2.0);
-      cairo_pattern_add_color_stop_rgb (pat, 0, 0.686, 0.686, 0.686);
-      cairo_pattern_add_color_stop_rgb (pat, 1, 0.925, 0.925, 0.925);
-      cairo_set_source (cr, pat);
-      cairo_rectangle (cr, 0, 0, tile_size, tile_size);
-      cairo_fill (cr);
-
-      cairo_pattern_destroy (pat);
-
-      // draw the red cross
-      cairo_set_source_rgb (cr, 0.424, 0.078, 0.078);
-      cairo_set_line_width (cr, 14.0);
-      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
-      cairo_move_to (cr, 24, 24);
-      cairo_line_to (cr, 50, 50);
-      cairo_move_to (cr, 50, 24);
-      cairo_line_to (cr, 24, 50);
-      cairo_stroke (cr);
-
-      cairo_destroy (cr);
-      cairo_surface_flush (surface);
-
-      texture = texture_new_for_surface (surface);
-      cairo_surface_destroy (surface);
-
-      shumate_tile_set_texture (tile, texture);
-      shumate_tile_set_fade_in (tile, TRUE);
-      shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
-    }
-  else if (SHUMATE_IS_MAP_SOURCE (next_source))
-    shumate_map_source_fill_tile (next_source, tile, cancellable);
-  else if (shumate_tile_get_state (tile) == SHUMATE_STATE_LOADED)
-    {
-      /* if we have some content, use the tile even if it wasn't validated */
-      shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
-    }
+  g_task_report_new_error (self, callback, user_data, fill_tile_async,
+                           SHUMATE_NETWORK_SOURCE_ERROR, SHUMATE_NETWORK_SOURCE_ERROR_FAILED,
+                           "No tile found.");
 }
 
 static void
@@ -125,7 +53,7 @@ shumate_error_tile_source_class_init (ShumateErrorTileSourceClass *klass)
 {
   ShumateMapSourceClass *map_source_class = SHUMATE_MAP_SOURCE_CLASS (klass);
 
-  map_source_class->fill_tile = fill_tile;
+  map_source_class->fill_tile_async = fill_tile_async;
 }
 
 
diff --git a/shumate/shumate-map-source-chain.c b/shumate/shumate-map-source-chain.c
index 2024aec..674c97d 100644
--- a/shumate/shumate-map-source-chain.c
+++ b/shumate/shumate-map-source-chain.c
@@ -50,9 +50,11 @@ static guint get_min_zoom_level (ShumateMapSource *map_source);
 static guint get_max_zoom_level (ShumateMapSource *map_source);
 static guint get_tile_size (ShumateMapSource *map_source);
 
-static void fill_tile (ShumateMapSource *map_source,
-                       ShumateTile      *tile,
-                       GCancellable     *cancellable);
+static void fill_tile_async (ShumateMapSource *map_source,
+                             ShumateTile *tile,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data);
 static void on_set_next_source_cb (ShumateMapSourceChain *source_chain,
     G_GNUC_UNUSED gpointer user_data);
 
@@ -85,7 +87,7 @@ shumate_map_source_chain_class_init (ShumateMapSourceChainClass *klass)
   map_source_class->get_max_zoom_level = get_max_zoom_level;
   map_source_class->get_tile_size = get_tile_size;
 
-  map_source_class->fill_tile = fill_tile;
+  map_source_class->fill_tile_async = fill_tile_async;
 }
 
 
@@ -203,9 +205,11 @@ get_tile_size (ShumateMapSource *map_source)
 
 
 static void
-fill_tile (ShumateMapSource *map_source,
-           ShumateTile      *tile,
-           GCancellable     *cancellable)
+fill_tile_async (ShumateMapSource *map_source,
+                 ShumateTile *tile,
+                 GCancellable *cancellable,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
 {
   ShumateMapSourceChain *source_chain = SHUMATE_MAP_SOURCE_CHAIN (map_source);
   ShumateMapSourceChainPrivate *priv = shumate_map_source_chain_get_instance_private (source_chain);
@@ -213,7 +217,7 @@ fill_tile (ShumateMapSource *map_source,
   g_return_if_fail (SHUMATE_IS_MAP_SOURCE_CHAIN (map_source));
   g_return_if_fail (priv->stack_top);
 
-  shumate_map_source_fill_tile (priv->stack_top, tile, cancellable);
+  shumate_map_source_fill_tile_async (priv->stack_top, tile, cancellable, callback, user_data);
 }
 
 
diff --git a/shumate/shumate-map-source.c b/shumate/shumate-map-source.c
index 1078993..476e18b 100644
--- a/shumate/shumate-map-source.c
+++ b/shumate/shumate-map-source.c
@@ -143,7 +143,7 @@ shumate_map_source_class_init (ShumateMapSourceClass *klass)
   klass->get_tile_size = NULL;
   klass->get_projection = NULL;
 
-  klass->fill_tile = NULL;
+  klass->fill_tile_async = NULL;
 
   /**
    * ShumateMapSource:next-source:
@@ -532,29 +532,6 @@ shumate_map_source_get_meters_per_pixel (ShumateMapSource *map_source,
 }
 
 
-/**
- * shumate_map_source_fill_tile:
- * @map_source: a #ShumateMapSource
- * @tile: a #ShumateTile
- * @cancellable: a #GCancellable or %NULL
- *
- * Fills the tile with image data (either from cache, network or rendered
- * locally).
- */
-void
-shumate_map_source_fill_tile (ShumateMapSource *map_source,
-                              ShumateTile      *tile,
-                              GCancellable     *cancellable)
-{
-  g_return_if_fail (SHUMATE_IS_MAP_SOURCE (map_source));
-
-  shumate_tile_set_state (tile, SHUMATE_STATE_LOADING);
-  SHUMATE_MAP_SOURCE_GET_CLASS (map_source)->fill_tile (map_source, tile, cancellable);
-}
-
-
-static void on_tile_notify_state (GObject *object, GParamSpec *pspec, gpointer user_data);
-
 /**
  * shumate_map_source_fill_tile_async:
  * @self: a #ShumateMapSource
@@ -572,33 +549,11 @@ shumate_map_source_fill_tile_async (ShumateMapSource *self,
                                     GAsyncReadyCallback callback,
                                     gpointer user_data)
 {
-  g_autoptr(GTask) task = NULL;
-
   g_return_if_fail (SHUMATE_IS_MAP_SOURCE (self));
   g_return_if_fail (SHUMATE_IS_TILE (tile));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_source_tag (task, shumate_map_source_fill_tile_async);
-
-  g_signal_connect (tile, "notify::state", G_CALLBACK (on_tile_notify_state), g_object_ref (task));
-
-  shumate_map_source_fill_tile (self, tile, cancellable);
-}
-
-static void
-on_tile_notify_state (GObject *object, GParamSpec *pspec, gpointer user_data)
-{
-  GTask *task = user_data;
-  ShumateTile *tile = SHUMATE_TILE (object);
-
-  if (shumate_tile_get_state (tile) == SHUMATE_STATE_DONE)
-    {
-      g_signal_handlers_disconnect_by_data (object, user_data);
-      g_task_return_boolean (task, TRUE);
-      g_object_unref (task);
-      return;
-    }
+  return SHUMATE_MAP_SOURCE_GET_CLASS (self)->fill_tile_async (self, tile, cancellable, callback, user_data);
 }
 
 /**
diff --git a/shumate/shumate-map-source.h b/shumate/shumate-map-source.h
index 09ee4e9..78f4a42 100644
--- a/shumate/shumate-map-source.h
+++ b/shumate/shumate-map-source.h
@@ -62,9 +62,11 @@ struct _ShumateMapSourceClass
   guint (*get_tile_size)(ShumateMapSource *map_source);
   ShumateMapProjection (*get_projection)(ShumateMapSource *map_source);
 
-  void (*fill_tile)(ShumateMapSource *map_source,
-                    ShumateTile      *tile,
-                    GCancellable     *cancellable);
+  void (*fill_tile_async)  (ShumateMapSource     *self,
+                            ShumateTile          *tile,
+                            GCancellable         *cancellable,
+                            GAsyncReadyCallback   callback,
+                            gpointer              user_data);
 };
 
 ShumateMapSource *shumate_map_source_get_next_source (ShumateMapSource *map_source);
@@ -101,9 +103,6 @@ double shumate_map_source_get_meters_per_pixel (ShumateMapSource *map_source,
     double latitude,
     double longitude);
 
-void shumate_map_source_fill_tile (ShumateMapSource *map_source,
-                                   ShumateTile      *tile,
-                                   GCancellable     *cancellable);
 void shumate_map_source_fill_tile_async (ShumateMapSource    *self,
                                          ShumateTile         *tile,
                                          GCancellable        *cancellable,
diff --git a/shumate/shumate-network-tile-source.c b/shumate/shumate-network-tile-source.c
index 4519a6b..5f78366 100644
--- a/shumate/shumate-network-tile-source.c
+++ b/shumate/shumate-network-tile-source.c
@@ -81,30 +81,32 @@ G_DEFINE_TYPE_WITH_PRIVATE (ShumateNetworkTileSource, shumate_network_tile_sourc
  */
 #define MAX_CONNS_DEFAULT 2
 
-typedef struct
-{
+
+typedef struct {
   ShumateNetworkTileSource *self;
-  GCancellable *cancellable;
   ShumateTile *tile;
-  SoupMessage *msg;
   GBytes *cached_data;
-} TileLoadedData;
+  char *etag;
+  SoupMessage *msg;
+} FillTileData;
 
-typedef struct
+static void
+fill_tile_data_free (FillTileData *data)
 {
-  ShumateNetworkTileSource *self;
-  GCancellable *cancellable;
-  ShumateTile *tile;
-  char *etag;
-} TileRenderedData;
+  g_clear_object (&data->self);
+  g_clear_object (&data->tile);
+  g_clear_pointer (&data->cached_data, g_bytes_unref);
+  g_clear_pointer (&data->etag, g_free);
+  g_clear_object (&data->msg);
+  g_free (data);
+}
 
 
-static void fill_tile (ShumateMapSource *map_source,
-                       ShumateTile      *tile,
-                       GCancellable     *cancellable);
-static void tile_state_notify (ShumateTile *tile,
-    G_GNUC_UNUSED GParamSpec *pspec,
-    GCancellable *cancellable);
+static void fill_tile_async (ShumateMapSource *map_source,
+                             ShumateTile *tile,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data);
 
 static char *get_tile_uri (ShumateNetworkTileSource *source,
     int x,
@@ -241,7 +243,7 @@ shumate_network_tile_source_class_init (ShumateNetworkTileSourceClass *klass)
   object_class->get_property = shumate_network_tile_source_get_property;
   object_class->set_property = shumate_network_tile_source_set_property;
 
-  map_source_class->fill_tile = fill_tile;
+  map_source_class->fill_tile_async = fill_tile_async;
 
   /**
    * ShumateNetworkTileSource:uri-format:
@@ -671,72 +673,68 @@ on_pixbuf_created (GObject      *source_object,
                    GAsyncResult *res,
                    gpointer      user_data)
 {
-  TileRenderedData *rendered_data = (TileRenderedData *) user_data;
-  g_autoptr(ShumateNetworkTileSource) self = g_steal_pointer (&rendered_data->self);
-  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
-  g_autoptr(GCancellable) cancellable = g_steal_pointer (&rendered_data->cancellable);
-  g_autoptr(ShumateTile) tile = g_steal_pointer (&rendered_data->tile);
-  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  FillTileData *data = g_task_get_task_data (task);
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (data->self);
+  GCancellable *cancellable = g_task_get_cancellable (task);
+  GError *error = NULL;
   g_autoptr(GdkPixbuf) pixbuf = NULL;
   g_autoptr(GdkTexture) texture = NULL;
-  g_autofree char *etag = g_steal_pointer (&rendered_data->etag);
-
-  g_slice_free (TileRenderedData, rendered_data);
-  g_signal_handlers_disconnect_by_func (tile, tile_state_notify, cancellable);
+  g_autofree char *buffer = NULL;
+  gsize buffer_size;
 
   pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
-  if (!pixbuf)
+  if (error != NULL)
     {
-      ShumateMapSource *next_source = shumate_map_source_get_next_source (SHUMATE_MAP_SOURCE (self));
-      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        {
-          DEBUG ("Download of tile %d, %d got cancelled",
-              shumate_tile_get_x (tile), shumate_tile_get_y (tile));
-          return;
-        }
-
-      if (next_source)
-        shumate_map_source_fill_tile (next_source, tile, cancellable);
-
+      g_task_return_error (task, error);
       return;
     }
 
+  if (data->etag != NULL)
+    shumate_tile_set_etag (data->tile, data->etag);
 
-  if (etag != NULL)
-    shumate_tile_set_etag (tile, etag);
-
-  g_autofree char *buffer = NULL;
-  gsize buffer_size;
   if (!gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &buffer_size, "png", &error, NULL))
-    g_warning ("Unable to export tile: %s", error->message);
+    {
+      g_warning ("Unable to export tile: %s", error->message);
+      g_error_free (error);
+    }
   else
     {
       g_autoptr(GBytes) bytes = g_bytes_new_take (g_steal_pointer (&buffer), buffer_size);
-      shumate_file_cache_store_tile_async (priv->file_cache, tile, bytes, etag, cancellable, NULL, NULL);
+      shumate_file_cache_store_tile_async (priv->file_cache, data->tile, bytes, data->etag, cancellable, 
NULL, NULL);
     }
 
   texture = gdk_texture_new_for_pixbuf (pixbuf);
-  shumate_tile_set_texture (tile, texture);
-  shumate_tile_set_fade_in (tile, TRUE);
-  shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
+  shumate_tile_set_texture (data->tile, texture);
+  shumate_tile_set_fade_in (data->tile, TRUE);
+  shumate_tile_set_state (data->tile, SHUMATE_STATE_DONE);
+
+  g_task_return_boolean (task, TRUE);
 }
 
 static void
 on_pixbuf_created_from_cache (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  g_autoptr(ShumateTile) tile = SHUMATE_TILE (user_data);
+  g_autoptr(GTask) task = user_data;
+  FillTileData *data = g_task_get_task_data (task);
   g_autoptr(GdkTexture) texture = NULL;
   g_autoptr(GdkPixbuf) pixbuf = NULL;
+  GError *error = NULL;
 
-  pixbuf = gdk_pixbuf_new_from_stream_finish (res, NULL);
+  pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
 
-  if (pixbuf == NULL)
-    return;
+  if (error != NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
 
   texture = gdk_texture_new_for_pixbuf (pixbuf);
-  shumate_tile_set_texture (tile, texture);
-  shumate_tile_set_fade_in (tile, TRUE);
-  shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
+  shumate_tile_set_texture (data->tile, texture);
+  shumate_tile_set_fade_in (data->tile, TRUE);
+  shumate_tile_set_state (data->tile, SHUMATE_STATE_DONE);
+
+  g_task_return_boolean (task, TRUE);
 }
 
 static void
@@ -744,87 +742,50 @@ on_message_sent (GObject *source_object,
                  GAsyncResult *res,
                  gpointer user_data)
 {
-  TileLoadedData *callback_data = (TileLoadedData *) user_data;
-  g_autoptr(SoupMessage) msg = g_steal_pointer (&callback_data->msg);
-  g_autoptr(ShumateNetworkTileSource) self = g_steal_pointer (&callback_data->self);
-  g_autoptr(GCancellable) cancellable = g_steal_pointer (&callback_data->cancellable);
-  g_autoptr(ShumateTile) tile = g_steal_pointer (&callback_data->tile);
-  g_autoptr(GBytes) cached_data = g_steal_pointer (&callback_data->cached_data);
+  g_autoptr(GTask) task = user_data;
+  FillTileData *data = g_task_get_task_data (task);
+  GCancellable *cancellable = g_task_get_cancellable (task);
   g_autoptr(GInputStream) input_stream = NULL;
-  g_autoptr(GError) error = NULL;
-  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
-  ShumateMapSource *next_source = shumate_map_source_get_next_source (SHUMATE_MAP_SOURCE (self));
+  GError *error = NULL;
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (data->self);
   const char *etag;
-  TileRenderedData *data;
-
-  g_slice_free (TileLoadedData, callback_data);
 
   input_stream = soup_session_send_finish (priv->soup_session, res, &error);
-  if (!input_stream)
+  if (error != NULL)
     {
-      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        {
-          DEBUG ("Download of tile %d, %d got cancelled",
-              shumate_tile_get_x (tile), shumate_tile_get_y (tile));
-
-          g_signal_handlers_disconnect_by_func (tile, tile_state_notify, cancellable);
-          return;
-        }
+      g_task_return_error (task, error);
+      return;
     }
   
-  DEBUG ("Got reply %d", msg->status_code);
+  DEBUG ("Got reply %d", data->msg->status_code);
 
-  if (msg->status_code == SOUP_STATUS_NOT_MODIFIED)
+  if (data->msg->status_code == SOUP_STATUS_NOT_MODIFIED)
     {
       g_autoptr(GInputStream) cache_stream = NULL;
 
-      shumate_file_cache_mark_up_to_date (priv->file_cache, tile);
+      shumate_file_cache_mark_up_to_date (priv->file_cache, data->tile);
 
-      cache_stream = g_memory_input_stream_new_from_bytes (cached_data);
-      gdk_pixbuf_new_from_stream_async (cache_stream, cancellable, on_pixbuf_created_from_cache, 
g_object_ref (tile));
-
-      g_signal_handlers_disconnect_by_func (tile, tile_state_notify, cancellable);
+      cache_stream = g_memory_input_stream_new_from_bytes (data->cached_data);
+      gdk_pixbuf_new_from_stream_async (cache_stream, cancellable, on_pixbuf_created_from_cache, 
g_object_ref (task));
       return;
     }
 
-  if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+  if (!SOUP_STATUS_IS_SUCCESSFUL (data->msg->status_code))
     {
-      DEBUG ("Unable to download tile %d, %d: %s",
-          shumate_tile_get_x (tile),
-          shumate_tile_get_y (tile),
-          soup_status_get_phrase (msg->status_code));
-
-      g_signal_handlers_disconnect_by_func (tile, tile_state_notify, cancellable);
-      if (next_source)
-        shumate_map_source_fill_tile (next_source, tile, cancellable);
-
+      g_task_return_new_error (task, SHUMATE_NETWORK_SOURCE_ERROR,
+                               SHUMATE_NETWORK_SOURCE_ERROR_BAD_RESPONSE,
+                               "Unable to download tile: HTTP %s",
+                               soup_status_get_phrase (data->msg->status_code));
       return;
     }
 
   /* Verify if the server sent an etag and save it */
-  etag = soup_message_headers_get_one (msg->response_headers, "ETag");
+  etag = soup_message_headers_get_one (data->msg->response_headers, "ETag");
   DEBUG ("Received ETag %s", etag);
 
-  data = g_slice_new0 (TileRenderedData);
-  data->self = g_object_ref (self);
-  if (data->cancellable)
-    data->cancellable = g_object_ref (cancellable);
-  data->tile = g_object_ref (tile);
   data->etag = g_strdup (etag);
 
-  gdk_pixbuf_new_from_stream_async (input_stream, cancellable, on_pixbuf_created, data);
-}
-
-static void
-tile_state_notify (ShumateTile *tile,
-    G_GNUC_UNUSED GParamSpec *pspec,
-    GCancellable *cancellable)
-{
-  if (shumate_tile_get_state (tile) == SHUMATE_STATE_DONE)
-    {
-      DEBUG ("Canceling tile download");
-      g_cancellable_cancel (cancellable);
-    }
+  gdk_pixbuf_new_from_stream_async (input_stream, cancellable, on_pixbuf_created, g_object_ref (task));
 }
 
 
@@ -842,65 +803,50 @@ get_modified_time_string (ShumateTile *tile)
   return g_date_time_format (modified_time, "%a, %d %b %Y %T %Z");
 }
 
-
-typedef struct {
-  ShumateNetworkTileSource *self;
-  ShumateTile *tile;
-  GCancellable *cancellable;
-} FillTileData;
-
-static void
-fill_tile_data_free (FillTileData *data)
-{
-  g_clear_object (&data->self);
-  g_clear_object (&data->tile);
-  g_clear_object (&data->cancellable);
-  g_free (data);
-}
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (FillTileData, fill_tile_data_free);
-
 static void on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user_data);
 
 static void
-fill_tile (ShumateMapSource *map_source,
-           ShumateTile      *tile,
-           GCancellable     *cancellable)
+fill_tile_async (ShumateMapSource *self,
+                 ShumateTile *tile,
+                 GCancellable *cancellable,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
 {
-  ShumateNetworkTileSource *tile_source = SHUMATE_NETWORK_TILE_SOURCE (map_source);
+  g_autoptr(GTask) task = NULL;
+  ShumateNetworkTileSource *tile_source = SHUMATE_NETWORK_TILE_SOURCE (self);
   ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (tile_source);
   FillTileData *data;
 
-  g_return_if_fail (SHUMATE_IS_NETWORK_TILE_SOURCE (map_source));
+  g_return_if_fail (SHUMATE_IS_NETWORK_TILE_SOURCE (self));
   g_return_if_fail (SHUMATE_IS_TILE (tile));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  if (shumate_tile_get_state (tile) == SHUMATE_STATE_DONE)
-    return;
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, fill_tile_async);
 
   if (priv->offline)
     {
-      ShumateMapSource *next_source = shumate_map_source_get_next_source (map_source);
-
-      if (SHUMATE_IS_MAP_SOURCE (next_source))
-        shumate_map_source_fill_tile (next_source, tile, cancellable);
-
+      g_task_return_new_error (task, SHUMATE_NETWORK_SOURCE_ERROR,
+                               SHUMATE_NETWORK_SOURCE_ERROR_OFFLINE,
+                               "The tile source is offline.");
       return;
     }
 
   data = g_new0 (FillTileData, 1);
   data->self = g_object_ref (tile_source);
   data->tile = g_object_ref (tile);
-  if (cancellable)
-    data->cancellable = g_object_ref (cancellable);
+  g_task_set_task_data (task, data, (GDestroyNotify) fill_tile_data_free);
 
-  shumate_file_cache_get_tile_async (priv->file_cache, tile, cancellable, on_file_cache_get_tile, data);
+  shumate_file_cache_get_tile_async (priv->file_cache, tile, cancellable, on_file_cache_get_tile, 
g_object_ref (task));
 }
 
 static void
 on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  g_autoptr(FillTileData) data = user_data;
+  g_autoptr(GTask) task = user_data;
+  FillTileData *data = g_task_get_task_data (task);
+  GCancellable *cancellable = g_task_get_cancellable (task);
   ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (data->self);
-  TileLoadedData *callback_data;
   g_autofree char *uri = NULL;
   g_autofree char *etag = NULL;
   g_autoptr(GBytes) bytes = NULL;
@@ -914,7 +860,7 @@ on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user
        * from the cache. */
 
       g_autoptr(GInputStream) input_stream = g_memory_input_stream_new_from_bytes (bytes);
-      gdk_pixbuf_new_from_stream_async (input_stream, data->cancellable, on_pixbuf_created_from_cache, 
g_object_ref (data->tile));
+      gdk_pixbuf_new_from_stream_async (input_stream, cancellable, on_pixbuf_created_from_cache, 
g_object_ref (task));
       return;
     }
 
@@ -923,18 +869,13 @@ on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user
         shumate_tile_get_y (data->tile),
         shumate_tile_get_zoom_level (data->tile));
 
-  callback_data = g_slice_new0 (TileLoadedData);
-  callback_data->tile = g_object_ref (data->tile);
-  callback_data->self = g_object_ref (data->self);
-  if (data->cancellable)
-    callback_data->cancellable = g_object_ref (data->cancellable);
   if (bytes)
-    callback_data->cached_data = g_bytes_ref (bytes);
-  callback_data->msg = soup_message_new (SOUP_METHOD_GET, uri);
+    data->cached_data = g_bytes_ref (bytes);
+  data->msg = soup_message_new (SOUP_METHOD_GET, uri);
 
-  if (etag || shumate_tile_get_state (data->tile) == SHUMATE_STATE_LOADED)
+  if (etag)
     {
-      char *date = get_modified_time_string (data->tile);
+      g_autofree char *date = get_modified_time_string (data->tile);
 
       /* If an etag is available, only use it.
        * OSM servers seems to send now as the modified time for all tiles
@@ -943,19 +884,26 @@ on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user
       if (etag)
         {
           DEBUG ("If-None-Match: %s", etag);
-          soup_message_headers_append (callback_data->msg->request_headers,
+          soup_message_headers_append (data->msg->request_headers,
               "If-None-Match", etag);
         }
       else if (date)
         {
           DEBUG ("If-Modified-Since %s", date);
-          soup_message_headers_append (callback_data->msg->request_headers,
+          soup_message_headers_append (data->msg->request_headers,
               "If-Modified-Since", date);
         }
-
-      g_free (date);
     }
 
-  g_signal_connect_object (data->tile, "notify::state", G_CALLBACK (tile_state_notify), data->cancellable, 
0);
-  soup_session_send_async (priv->soup_session, callback_data->msg, data->cancellable, on_message_sent, 
callback_data);
+  soup_session_send_async (priv->soup_session, data->msg, cancellable, on_message_sent, g_object_ref (task));
 }
+
+
+/**
+ * shumate_network_source_error_quark:
+ *
+ * Gets the #ShumateNetworkTileSource error quark.
+ *
+ * Returns: a #GQuark
+ */
+G_DEFINE_QUARK (shumate-network-source-error-quark, shumate_network_source_error);
diff --git a/shumate/shumate-network-tile-source.h b/shumate/shumate-network-tile-source.h
index f97af4d..f9eb0cf 100644
--- a/shumate/shumate-network-tile-source.h
+++ b/shumate/shumate-network-tile-source.h
@@ -29,6 +29,34 @@
 
 G_BEGIN_DECLS
 
+
+/**
+ * SHUMATE_NETWORK_SOURCE_ERROR:
+ *
+ * Error domain for errors that may occur while fetching tiles from the network
+ * using #ShumateNetworkTileSource. Errors in this domain will be from the
+ * #ShumateNetworkTileError enum.
+ */
+#define SHUMATE_NETWORK_SOURCE_ERROR shumate_network_source_error_quark ()
+GQuark shumate_network_source_error_quark (void);
+
+/**
+ * ShumateNetworkSourceError:
+ * @SHUMATE_NETWORK_SOURCE_ERROR_FAILED: An unspecified error occurred during the operation.
+ * @SHUMATE_NETWORK_SOURCE_ERROR_BAD_RESPONSE: An unsuccessful HTTP response was received from the server.
+ * @SHUMATE_NETWORK_SOURCE_ERROR_COULD_NOT_CONNECT: The server could not be reached.
+ * @SHUMATE_NETWORK_SOURCE_ERROR_OFFLINE: The tile source has been marked as offline.
+ *
+ * Error codes in the #SHUMATE_NETWORK_SOURCE_ERROR domain.
+ */
+typedef enum {
+  SHUMATE_NETWORK_SOURCE_ERROR_FAILED,
+  SHUMATE_NETWORK_SOURCE_ERROR_BAD_RESPONSE,
+  SHUMATE_NETWORK_SOURCE_ERROR_COULD_NOT_CONNECT,
+  SHUMATE_NETWORK_SOURCE_ERROR_OFFLINE,
+} ShumateNetworkSourceError;
+
+
 #define SHUMATE_TYPE_NETWORK_TILE_SOURCE shumate_network_tile_source_get_type ()
 G_DECLARE_DERIVABLE_TYPE (ShumateNetworkTileSource, shumate_network_tile_source, SHUMATE, 
NETWORK_TILE_SOURCE, ShumateTileSource)
 
diff --git a/shumate/shumate-tile-source.c b/shumate/shumate-tile-source.c
index bf9750e..64f9ca5 100644
--- a/shumate/shumate-tile-source.c
+++ b/shumate/shumate-tile-source.c
@@ -202,7 +202,7 @@ shumate_tile_source_class_init (ShumateTileSourceClass *klass)
   map_source_class->get_tile_size = get_tile_size;
   map_source_class->get_projection = get_projection;
 
-  map_source_class->fill_tile = NULL;
+  map_source_class->fill_tile_async = NULL;
 
   /**
    * ShumateTileSource:id:


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