[libgweather/location-object: 1/2] Turn GWeatherLocation into a proper GObject




commit 513167a23401c24e8a1277e3c4d9fd9b5d91a2f5
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Tue Nov 9 17:35:38 2021 +0000

    Turn GWeatherLocation into a proper GObject
    
    We want to make it easier to use GWeatherLocation inside data structures
    like GListModel implementations. It's also easier to use from language
    bindings, as now objects will have a clear life time.

 libgweather/gweather-location.c      | 285 ++++++++++++++++++++---------------
 libgweather/gweather-location.h      |   4 +-
 libgweather/gweather-private.h       |   6 +-
 libgweather/gweather-timezone.c      |   4 +-
 libgweather/gweather-weather.c       | 185 +++++++++++++----------
 libgweather/tests/test_libgweather.c |  67 ++++----
 libgweather/tools/test_weather.c     |   2 +-
 7 files changed, 313 insertions(+), 240 deletions(-)
---
diff --git a/libgweather/gweather-location.c b/libgweather/gweather-location.c
index e064fc72..c9702342 100644
--- a/libgweather/gweather-location.c
+++ b/libgweather/gweather-location.c
@@ -25,28 +25,82 @@
  * airport to a city, see also test_distance() */
 #define AIRPORT_MAX_DISTANCE 100.0
 
+G_DEFINE_TYPE (GWeatherLocation, gweather_location, G_TYPE_OBJECT)
+
+static void
+gweather_location_finalize (GObject *gobject)
+{
+    GWeatherLocation *self = GWEATHER_LOCATION (gobject);
+
+    /* Remove weak reference from DB object; but only if it points to us.
+     * It may point elsewhere if we are an implicit nearest child. */
+    if (self->db && g_ptr_array_index (self->db->locations, self->db_idx) == self)
+        g_ptr_array_index (self->db->locations, self->db_idx) = NULL;
+
+    g_free (self->_english_name);
+    g_free (self->_local_name);
+    g_free (self->_local_sort_name);
+    g_free (self->_english_sort_name);
+    g_free (self->_country_code);
+    g_free (self->_station_code);
+
+    g_clear_pointer (&self->_timezone, gweather_timezone_unref);
+
+    G_OBJECT_CLASS (gweather_location_parent_class)->finalize (gobject);
+}
+
+static void
+gweather_location_dispose (GObject *gobject)
+{
+    GWeatherLocation *self = GWEATHER_LOCATION (gobject);
+
+    if (self->_children) {
+        for (int i = 0; self->_children[i]; i++) {
+            g_object_unref (self->_children[i]);
+        }
+        g_free (self->_children);
+        self->_children = NULL;
+    }
+
+    g_clear_object (&self->_parent);
+
+    G_OBJECT_CLASS (gweather_location_parent_class)->dispose (gobject);
+}
+
+static void
+gweather_location_class_init (GWeatherLocationClass *klass)
+{
+    G_OBJECT_CLASS (klass)->dispose = gweather_location_dispose;
+    G_OBJECT_CLASS (klass)->finalize = gweather_location_finalize;
+}
+
+static void
+gweather_location_init (GWeatherLocation *self)
+{
+    self->latitude = self->longitude = DBL_MAX;
+    self->db_idx = INVALID_IDX;
+    self->tz_hint_idx = INVALID_IDX;
+}
+
 static inline GWeatherLocation *
 _iter_up (GWeatherLocation *loc)
 {
     GWeatherLocation *tmp;
 
     tmp = gweather_location_get_parent (loc);
-    gweather_location_unref (loc);
+    g_object_unref (loc);
+
     return tmp;
 }
-#define ITER_UP(start, _p) for ((_p) = gweather_location_ref (start); (_p); (_p) = _iter_up (_p))
+#define ITER_UP(start, _p) for ((_p) = g_object_ref (start); (_p); (_p) = _iter_up (_p))
 
 static GWeatherLocation *
 location_new (GWeatherLocationLevel level)
 {
     GWeatherLocation *loc;
 
-    loc = g_slice_new0 (GWeatherLocation);
-    loc->latitude = loc->longitude = DBL_MAX;
+    loc = g_object_new (GWEATHER_TYPE_LOCATION, NULL);
     loc->level = level;
-    loc->ref_count = 1;
-    loc->db_idx = INVALID_IDX;
-    loc->tz_hint_idx = INVALID_IDX;
 
     return loc;
 }
@@ -69,7 +123,7 @@ location_ref_for_idx (GWeatherDb *db,
     if (!nearest_of) {
         loc = g_ptr_array_index (db->locations, idx);
         if (loc) {
-            return gweather_location_ref (loc);
+            return g_object_ref (loc);
         }
     }
 
@@ -132,7 +186,78 @@ _gweather_location_reset_world (void)
     }
 }
 
-G_DEFINE_BOXED_TYPE (GWeatherLocation, gweather_location, gweather_location_ref, gweather_location_unref)
+static gpointer
+ensure_world (gpointer dummy G_GNUC_UNUSED)
+{
+    g_autoptr (GError) error = NULL;
+    g_autofree char *filename = NULL;
+    g_autoptr (GMappedFile) map;
+    const char *locations_path;
+    time_t now;
+    struct tm tm;
+    GWeatherDb *db = NULL;
+
+    locations_path = g_getenv ("LIBGWEATHER_LOCATIONS_PATH");
+    if (locations_path != NULL) {
+        filename = g_strdup (locations_path);
+        if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+            g_warning ("User specified database %s does not exist", filename);
+            g_clear_pointer (&filename, g_free);
+        }
+    }
+
+    if (filename == NULL) {
+        filename = g_build_filename (GWEATHER_BIN_LOCATION_DIR, "Locations.bin", NULL);
+    }
+
+    map = g_mapped_file_new (filename, FALSE, &error);
+    if (map == NULL) {
+        g_critical ("Failed to open database %s: %s", filename, error->message);
+        return NULL;
+    }
+
+    db = g_new0 (GWeatherDb, 1);
+    db->world = db_world_from_data (g_mapped_file_get_contents (map), g_mapped_file_get_length (map));
+    /* This is GWthDB01 */
+    if (db_world_get_magic (db->world) != 0x5747687442443130) {
+        g_free (db);
+        return NULL;
+    }
+
+    db->map = g_steal_pointer (&map);
+
+    db->locations_ref = db_world_get_locations (db->world);
+    db->timezones_ref = db_world_get_timezones (db->world);
+
+    db->locations = g_ptr_array_new ();
+    db->timezones = g_ptr_array_new ();
+
+    g_ptr_array_set_size (db->locations, db_arrayof_location_get_length (db->locations_ref));
+    g_ptr_array_set_size (db->timezones, db_world_timezones_get_length (db->timezones_ref));
+
+    /* Get timestamps for the start and end of this year.
+     * This is used to parse timezone information. */
+    now = time (NULL);
+    tm = *gmtime (&now);
+    tm.tm_mon = 0;
+    tm.tm_mday = 1;
+    tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+    db->year_start = mktime (&tm);
+    tm.tm_year++;
+    db->year_end = mktime (&tm);
+
+    world_db = db;
+
+    return db;
+}
+
+static void
+gweather_location_ensure_world (void)
+{
+    static GOnce ensure_world_once = G_ONCE_INIT;
+
+    g_once (&ensure_world_once, ensure_world, NULL);
+}
 
 /**
  * gweather_location_get_world:
@@ -148,63 +273,7 @@ G_DEFINE_BOXED_TYPE (GWeatherLocation, gweather_location, gweather_location_ref,
 GWeatherLocation *
 gweather_location_get_world (void)
 {
-    if (world_db == NULL) {
-        g_autoptr (GError) error = NULL;
-        g_autofree char *filename = NULL;
-        g_autoptr (GMappedFile) map;
-        const char *locations_path;
-        time_t now;
-        struct tm tm;
-
-        locations_path = g_getenv ("LIBGWEATHER_LOCATIONS_PATH");
-        if (locations_path != NULL) {
-            filename = g_strdup (locations_path);
-            if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
-                g_warning ("User specified database %s does not exist", filename);
-                g_clear_pointer (&filename, g_free);
-            }
-        }
-
-        if (filename == NULL) {
-            filename = g_build_filename (GWEATHER_BIN_LOCATION_DIR, "Locations.bin", NULL);
-        }
-
-        map = g_mapped_file_new (filename, FALSE, &error);
-        if (map == NULL) {
-            g_critical ("Failed to open database %s: %s", filename, error->message);
-            return NULL;
-        }
-
-        world_db = g_new0 (GWeatherDb, 1);
-        world_db->world = db_world_from_data (g_mapped_file_get_contents (map), g_mapped_file_get_length 
(map));
-        /* This is GWthDB01 */
-        if (db_world_get_magic (world_db->world) != 0x5747687442443130) {
-            g_free (world_db);
-            return NULL;
-        }
-
-        world_db->map = g_steal_pointer (&map);
-
-        world_db->locations_ref = db_world_get_locations (world_db->world);
-        world_db->timezones_ref = db_world_get_timezones (world_db->world);
-
-        world_db->locations = g_ptr_array_new ();
-        world_db->timezones = g_ptr_array_new ();
-
-        g_ptr_array_set_size (world_db->locations, db_arrayof_location_get_length (world_db->locations_ref));
-        g_ptr_array_set_size (world_db->timezones, db_world_timezones_get_length (world_db->timezones_ref));
-
-        /* Get timestamps for the start and end of this year.
-        * This is used to parse timezone information. */
-        now = time (NULL);
-        tm = *gmtime (&now);
-        tm.tm_mon = 0;
-        tm.tm_mday = 1;
-        tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
-        world_db->year_start = mktime (&tm);
-        tm.tm_year++;
-        world_db->year_end = mktime (&tm);
-    }
+    gweather_location_ensure_world ();
 
     return location_ref_for_idx (world_db, 0, NULL);
 }
@@ -213,18 +282,16 @@ gweather_location_get_world (void)
  * gweather_location_ref:
  * @loc: a location
  *
- * Acquire a reference to the location.
+ * Acquires a reference to the location.
  *
  * Return value: (transfer full): the location, with an additional reference
+ *
+ * Deprecated: 4.0: Use [method GObject Object ref] instead
  **/
 GWeatherLocation *
 gweather_location_ref (GWeatherLocation *loc)
 {
-    g_return_val_if_fail (loc != NULL, NULL);
-
-    loc->ref_count++;
-
-    return loc;
+    return g_object_ref (loc);
 }
 
 /**
@@ -235,40 +302,13 @@ gweather_location_ref (GWeatherLocation *loc)
  *
  * If the reference was the last one held, this function will free
  * the resources allocated by the location.
+ *
+ * Deprecated: 4.0: Use [method@GObject.Object.unref] instead
  **/
 void
 gweather_location_unref (GWeatherLocation *loc)
 {
-    g_return_if_fail (loc != NULL);
-
-    int i;
-
-    if (--loc->ref_count)
-        return;
-
-    /* Remove weak reference from DB object; but only if it points to us.
-     * It may point elsewhere if we are an implicit nearest child. */
-    if (loc->db && g_ptr_array_index (loc->db->locations, loc->db_idx) == loc)
-        g_ptr_array_index (loc->db->locations, loc->db_idx) = NULL;
-
-    g_free (loc->_english_name);
-    g_free (loc->_local_name);
-    g_free (loc->_local_sort_name);
-    g_free (loc->_english_sort_name);
-    g_free (loc->_country_code);
-    g_free (loc->_station_code);
-
-    if (loc->_children) {
-        for (i = 0; loc->_children[i]; i++) {
-            gweather_location_unref (loc->_children[i]);
-        }
-        g_free (loc->_children);
-    }
-
-    g_clear_pointer (&loc->_parent, gweather_location_unref);
-    g_clear_pointer (&loc->_timezone, gweather_timezone_unref);
-
-    g_slice_free (GWeatherLocation, loc);
+    g_object_unref (loc);
 }
 
 /**
@@ -462,7 +502,7 @@ gweather_location_get_parent (GWeatherLocation *loc)
     g_return_val_if_fail (loc != NULL, NULL);
 
     if (loc->_parent)
-        return gweather_location_ref (loc->_parent);
+        return g_object_ref (loc->_parent);
 
     if (loc->level == GWEATHER_LOCATION_WORLD)
         return NULL;
@@ -515,7 +555,7 @@ gweather_location_next_child (GWeatherLocation *loc,
     if (loc->_children) {
         if (child == NULL) {
             if (loc->_children[0])
-                return gweather_location_ref (loc->_children[0]);
+                return g_object_ref (loc->_children[0]);
             else
                 return NULL;
         }
@@ -523,7 +563,7 @@ gweather_location_next_child (GWeatherLocation *loc,
         for (i = 0; loc->_children[i]; i++) {
             if (loc->_children[i] == child) {
                 if (loc->_children[i + 1])
-                    return gweather_location_ref (loc->_children[i + 1]);
+                    return g_object_ref (loc->_children[i + 1]);
                 else
                     return NULL;
             }
@@ -650,8 +690,8 @@ find_nearest_city (GWeatherLocation *location,
     double distance = location_distance (location->latitude, location->longitude, data->latitude, 
data->longitude);
 
     if (data->location == NULL || data->distance > distance) {
-        g_clear_pointer (&data->location, gweather_location_unref);
-        data->location = gweather_location_ref (location);
+        g_clear_object (&data->location);
+        data->location = g_object_ref (location);
         data->distance = distance;
     }
 }
@@ -758,7 +798,7 @@ gweather_location_find_nearest_city_full (GWeatherLocation *loc,
 
     destroy (user_data);
 
-    return gweather_location_ref (data.location);
+    return g_object_ref (data.location);
 }
 
 static void
@@ -797,7 +837,7 @@ _got_place (GObject *source_object,
         GWeatherLocation *location;
         location = _gweather_location_new_detached (data.location, geocode_place_get_town (place), TRUE, 
data.latitude, data.longitude);
 
-        g_task_return_pointer (task, location, (GDestroyNotify) gweather_location_unref);
+        g_task_return_pointer (task, location, (GDestroyNotify) g_object_unref);
     }
 
     g_object_unref (task);
@@ -805,7 +845,7 @@ _got_place (GObject *source_object,
 
 /**
  * gweather_location_detect_nearest_city:
- * @loc: (allow-none): The parent location, which will be searched recursively
+ * @loc: (nullable) (transfer none): The parent location, which will be searched recursively
  * @lat: Latitude, in degrees
  * @lon: Longitude, in degrees
  * @cancellable: optional, NULL to ignore
@@ -853,8 +893,8 @@ gweather_location_detect_nearest_city (GWeatherLocation *loc,
 }
 
 /**
- * gweather_location_detect_nearest_location_finish:
- * @result: 
+ * gweather_location_detect_nearest_city_finish:
+ * @result: the result of the asynchronous operation
  * @error: Stores error if any occurs in retrieving the result
  *
  * Fetches the location from @result.
@@ -863,7 +903,8 @@ gweather_location_detect_nearest_city (GWeatherLocation *loc,
  */
 
 GWeatherLocation *
-gweather_location_detect_nearest_city_finish (GAsyncResult *result, GError **error)
+gweather_location_detect_nearest_city_finish (GAsyncResult *result,
+                                              GError **error)
 {
     GTask *task;
 
@@ -1196,7 +1237,7 @@ _gweather_location_update_weather_location (GWeatherLocation *gloc,
             start = first_child;
     }
     if (!start)
-        start = gweather_location_ref (gloc);
+        start = g_object_ref (gloc);
 
     ITER_UP (start, l) {
         if (!code)
@@ -1383,7 +1424,7 @@ gweather_location_format_two_serialize (GWeatherLocation *location)
         is_city = FALSE;
     }
     if (!real_loc)
-        real_loc = gweather_location_ref (location);
+        real_loc = g_object_ref (location);
 
     parent = gweather_location_get_parent (real_loc);
 
@@ -1478,7 +1519,7 @@ gweather_location_common_deserialize (GWeatherLocation *world,
         if (found && (g_strcmp0 (name, gweather_location_get_english_name (found)) == 0 ||
                       g_strcmp0 (name, gweather_location_get_name (found)) == 0))
             return g_steal_pointer (&found);
-        g_clear_pointer (&found, gweather_location_unref);
+        g_clear_object (&found);
     }
 
     if (station_code[0] == '\0')
@@ -1546,9 +1587,9 @@ gweather_location_common_deserialize (GWeatherLocation *world,
             if (name == NULL ||
                 g_str_equal (name, gweather_location_get_english_name (found)) ||
                 g_str_equal (name, gweather_location_get_name (found)))
-                found = gweather_location_ref (found);
+                found = g_object_ref (found);
             else
-                found = _gweather_location_new_detached (gweather_location_ref (ws), name, TRUE, latitude, 
longitude);
+                found = _gweather_location_new_detached (g_object_ref (ws), name, TRUE, latitude, longitude);
 
             return found;
         }
@@ -1697,7 +1738,7 @@ gweather_location_deserialize (GWeatherLocation *world,
 }
 
 /**
- * gweather_location_new_detached:
+ * gweather_location_new_detached: (constructor)
  * @name: the user visible location name
  * @icao: (nullable): the ICAO code of the location
  * @latitude: the latitude of the location
@@ -1705,6 +1746,8 @@ gweather_location_deserialize (GWeatherLocation *world,
  *
  * Construct a new location from the given data, supplementing
  * any missing information from the static database.
+ *
+ * Returns: (transfer full): the newly created detached location
  */
 GWeatherLocation *
 gweather_location_new_detached (const char *name,
@@ -1713,7 +1756,6 @@ gweather_location_new_detached (const char *name,
                                 gdouble longitude)
 {
     g_autoptr (GWeatherLocation) world = NULL;
-    g_autoptr (GWeatherLocation) city = NULL;
 
     g_return_val_if_fail (name != NULL, NULL);
 
@@ -1725,10 +1767,11 @@ gweather_location_new_detached (const char *name,
     if (icao != NULL) {
         return gweather_location_common_deserialize (world, name, icao, FALSE, TRUE, latitude, longitude, 
FALSE, 0, 0);
     } else {
-        city = gweather_location_find_nearest_city (world, latitude, longitude);
+        g_autoptr (GWeatherLocation) city = gweather_location_find_nearest_city (world, latitude, longitude);
 
         latitude = DEGREES_TO_RADIANS (latitude);
         longitude = DEGREES_TO_RADIANS (longitude);
+
         return _gweather_location_new_detached (g_steal_pointer (&city), name, TRUE, latitude, longitude);
     }
 }
diff --git a/libgweather/gweather-location.h b/libgweather/gweather-location.h
index 8d3b0570..0166025a 100644
--- a/libgweather/gweather-location.h
+++ b/libgweather/gweather-location.h
@@ -85,7 +85,7 @@ typedef enum { /*< underscore_name=gweather_location_level >*/
 #define GWEATHER_TYPE_LOCATION (gweather_location_get_type ())
 
 GWEATHER_AVAILABLE_IN_ALL
-GType gweather_location_get_type (void);
+G_DECLARE_FINAL_TYPE (GWeatherLocation, gweather_location, GWEATHER, LOCATION, GObject)
 
 GWEATHER_AVAILABLE_IN_ALL
 GWeatherLocation *      gweather_location_get_world             (void);
@@ -94,8 +94,6 @@ GWeatherLocation *      gweather_location_ref                   (GWeatherLocatio
 GWEATHER_AVAILABLE_IN_ALL
 void                    gweather_location_unref                 (GWeatherLocation  *loc);
 
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (GWeatherLocation, gweather_location_unref);
-
 GWEATHER_AVAILABLE_IN_ALL
 const char *            gweather_location_get_name              (GWeatherLocation  *loc);
 GWEATHER_AVAILABLE_IN_ALL
diff --git a/libgweather/gweather-private.h b/libgweather/gweather-private.h
index 68d5665b..282112d7 100644
--- a/libgweather/gweather-private.h
+++ b/libgweather/gweather-private.h
@@ -40,8 +40,10 @@ typedef struct {
 } GWeatherDb;
 
 struct _GWeatherLocation {
+    GObject parent_instance;
+
     GWeatherDb *db;
-    guint       db_idx;
+    guint db_idx;
     DbLocationRef ref;
 
     /* Attributes with _ may be fetched/filled from the database on the fly */
@@ -66,8 +68,6 @@ struct _GWeatherLocation {
     double longitude;
 
     gboolean latlon_valid;
-
-    int ref_count;
 };
 
 #define WEATHER_LOCATION_CODE_LEN 4
diff --git a/libgweather/gweather-timezone.c b/libgweather/gweather-timezone.c
index a75b82e8..cd8f4054 100644
--- a/libgweather/gweather-timezone.c
+++ b/libgweather/gweather-timezone.c
@@ -185,7 +185,7 @@ _gweather_timezone_ref_for_idx (GWeatherDb *db,
 GWeatherTimezone *
 gweather_timezone_get_by_tzid (const char *tzid)
 {
-    GWeatherLocation *world;
+    g_autoptr (GWeatherLocation) world = NULL;
     GWeatherDb *db;
     gsize idx;
 
@@ -194,7 +194,6 @@ gweather_timezone_get_by_tzid (const char *tzid)
     /* TODO: Get the DB directly */
     world = gweather_location_get_world ();
     db = world->db;
-    gweather_location_unref (world);
 
     if (!db_world_timezones_lookup (db->timezones_ref, tzid, &idx, NULL))
         return NULL;
@@ -216,6 +215,7 @@ gweather_timezone_ref (GWeatherTimezone *zone)
     g_return_val_if_fail (zone != NULL, NULL);
 
     zone->ref_count++;
+
     return zone;
 }
 
diff --git a/libgweather/gweather-weather.c b/libgweather/gweather-weather.c
index 6b2c8820..8aaed414 100644
--- a/libgweather/gweather-weather.c
+++ b/libgweather/gweather-weather.c
@@ -61,6 +61,8 @@ enum
 
 static guint gweather_info_signals[SIGNAL_LAST];
 
+static GParamSpec *gweather_info_pspecs[PROP_LAST];
+
 G_DEFINE_TYPE (GWeatherInfo, gweather_info, G_TYPE_OBJECT);
 
 void
@@ -763,10 +765,9 @@ gweather_info_finalize (GObject *object)
     GWeatherInfo *info = GWEATHER_INFO (object);
 
     _weather_location_free (&info->location);
-    g_clear_object (&info->settings);
 
-    if (info->glocation)
-        gweather_location_unref (info->glocation);
+    g_clear_object (&info->settings);
+    g_clear_object (&info->glocation);
 
     g_clear_pointer (&info->application_id, g_free);
     g_clear_pointer (&info->contact_info, g_free);
@@ -2092,13 +2093,13 @@ gweather_info_set_location_internal (GWeatherInfo *info,
     if (info->glocation == location)
         return;
 
-    if (info->glocation)
-        gweather_location_unref (info->glocation);
+    if (info->glocation != NULL)
+        g_object_unref (info->glocation);
 
     info->glocation = location;
 
-    if (info->glocation) {
-        gweather_location_ref (location);
+    if (info->glocation != NULL) {
+        g_object_ref (info->glocation);
     } else {
         g_autoptr (GWeatherLocation) world = NULL;
         const gchar *station_code;
@@ -2116,8 +2117,7 @@ gweather_info_set_location_internal (GWeatherInfo *info,
 
     if (info->glocation) {
         _weather_location_free (&info->location);
-        _gweather_location_update_weather_location (info->glocation,
-                                                    &info->location);
+        _gweather_location_update_weather_location (info->glocation, &info->location);
     }
 
     if (name) {
@@ -2138,11 +2138,12 @@ gweather_info_set_location_internal (GWeatherInfo *info,
 /**
  * gweather_info_set_location:
  * @info: a #GWeatherInfo
- * @location: (allow-none): a location for which weather is desired
+ * @location: (nullable): a location for which weather is desired
  *
- * Changes @info to report weather for @location.
- * Note that this will clear any forecast or current conditions from
- * @info, you must call gweather_info_update() to obtain the new data.
+ * Changes the location of the weather report.
+ *
+ * Note that this will clear any forecast or current conditions, and
+ * you must call [method GWeather Info.update] to obtain the new data.
  */
 void
 gweather_info_set_location (GWeatherInfo *info,
@@ -2199,7 +2200,8 @@ gweather_info_set_enabled_providers (GWeatherInfo *info,
     info->providers = providers;
 
     gweather_info_abort (info);
-    g_object_notify (G_OBJECT (info), "enabled-providers");
+
+    g_object_notify_by_pspec (G_OBJECT (info), gweather_info_pspecs[PROP_ENABLED_PROVIDERS]);
 }
 
 /**
@@ -2304,7 +2306,7 @@ gweather_info_set_property (GObject *object,
 
     switch (property_id) {
         case PROP_LOCATION:
-            gweather_info_set_location_internal (self, (GWeatherLocation *) g_value_get_boxed (value));
+            gweather_info_set_location_internal (self, g_value_get_object (value));
             break;
         case PROP_ENABLED_PROVIDERS:
             gweather_info_set_enabled_providers (self, g_value_get_flags (value));
@@ -2330,7 +2332,7 @@ gweather_info_get_property (GObject *object,
 
     switch (property_id) {
         case PROP_LOCATION:
-            g_value_set_boxed (value, self->glocation);
+            g_value_set_object (value, self->glocation);
             break;
         case PROP_ENABLED_PROVIDERS:
             g_value_set_flags (value, self->providers);
@@ -2347,25 +2349,27 @@ gweather_info_get_property (GObject *object,
 }
 
 static void
-gweather_info_constructed (GObject *object)
+gweather_info_constructed (GObject *gobject)
 {
-    GWeatherInfo *info = GWEATHER_INFO (object);
-    GApplication *app;
+    GWeatherInfo *self = GWEATHER_INFO (gobject);
 
-    if (info->application_id != NULL)
-        return;
-    app = g_application_get_default ();
-    if (!app)
-        return;
+    /* Use the application id of the default GApplication instance as the
+     * fallback value
+     */
+    if (self->application_id == NULL) {
+        GApplication *app = g_application_get_default ();
 
-    gweather_info_set_application_id (info,
-                                      g_application_get_application_id (app));
+        if (app != NULL) {
+            gweather_info_set_application_id (self, g_application_get_application_id (app));
+        }
+    }
+
+    G_OBJECT_CLASS (gweather_info_parent_class)->constructed (gobject);
 }
 
 void
 gweather_info_class_init (GWeatherInfoClass *klass)
 {
-    GParamSpec *pspec;
     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
     gobject_class->dispose = gweather_info_dispose;
@@ -2374,80 +2378,109 @@ gweather_info_class_init (GWeatherInfoClass *klass)
     gobject_class->get_property = gweather_info_get_property;
     gobject_class->constructed = gweather_info_constructed;
 
-    pspec = g_param_spec_boxed ("location",
-                                "Location",
-                                "The location this info represents",
-                                GWEATHER_TYPE_LOCATION,
-                                G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
-    g_object_class_install_property (gobject_class, PROP_LOCATION, pspec);
-
-    pspec = g_param_spec_flags ("enabled-providers",
-                                "Enabled providers",
-                                "A bitmask of enabled weather service providers",
-                                GWEATHER_TYPE_PROVIDER,
-                                GWEATHER_PROVIDER_NONE,
-                                G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
-    g_object_class_install_property (gobject_class, PROP_ENABLED_PROVIDERS, pspec);
-
-    pspec = g_param_spec_string ("application-id",
-                                 "Application ID",
-                                 "An unique reverse-DNS application ID",
-                                 NULL,
-                                 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
-    g_object_class_install_property (gobject_class, PROP_APPLICATION_ID, pspec);
-
-    pspec = g_param_spec_string ("contact-info",
-                                 "Contact information",
-                                 "An email address or contact form URL",
-                                 NULL,
-                                 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
-    g_object_class_install_property (gobject_class, PROP_CONTACT_INFO, pspec);
+    /**
+     * GWeatherInfo:location:
+     *
+     * The location of the weather information.
+     */
+    gweather_info_pspecs[PROP_LOCATION] =
+        g_param_spec_object ("location",
+                             "Location",
+                             "The location this info represents",
+                             GWEATHER_TYPE_LOCATION,
+                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+    /**
+     * GWeatherInfo:enabled-providers:
+     *
+     * The enabled weather providers.
+     */
+    gweather_info_pspecs[PROP_ENABLED_PROVIDERS] =
+        g_param_spec_flags ("enabled-providers",
+                            "Enabled providers",
+                            "A bitmask of enabled weather service providers",
+                            GWEATHER_TYPE_PROVIDER,
+                            GWEATHER_PROVIDER_NONE,
+                            G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+    /**
+     * GWeatherInfo:application-id:
+     *
+     * A unique identifier, typically in the form of reverse DNS notation,
+     * for the application that is querying the weather information.
+     *
+     * Weather providers require this information.
+     */
+    gweather_info_pspecs[PROP_APPLICATION_ID] =
+        g_param_spec_string ("application-id",
+                             "Application ID",
+                             "An unique reverse-DNS application ID",
+                             NULL,
+                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+    /**
+     * GWeatherInfo:contact-info:
+     *
+     * An email address or any other contact form URL.
+     *
+     * Weather providers require this information.
+     */
+    gweather_info_pspecs[PROP_CONTACT_INFO] =
+        g_param_spec_string ("contact-info",
+                             "Contact information",
+                             "An email address or contact form URL",
+                             NULL,
+                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+    g_object_class_install_properties (gobject_class, PROP_LAST, gweather_info_pspecs);
 
     /**
      * GWeatherInfo::updated:
-     * @object: the emitter of the signal.
+     * @object: the object that emitted the signal
      *
      * This signal is emitted after the initial fetch of the weather
      * data from upstream services, and after every successful call
-     * to @gweather_info_update().
+     * to [method GWeather Info.update].
      */
-    gweather_info_signals[SIGNAL_UPDATED] = g_signal_new ("updated",
-                                                          GWEATHER_TYPE_INFO,
-                                                          G_SIGNAL_RUN_FIRST,
-                                                          0,
-                                                          NULL, /* accumulator */
-                                                          NULL, /* accu_data */
-                                                          g_cclosure_marshal_VOID__VOID,
-                                                          G_TYPE_NONE,
-                                                          0);
+    gweather_info_signals[SIGNAL_UPDATED] =
+        g_signal_new (g_intern_static_string ("updated"),
+                      GWEATHER_TYPE_INFO,
+                      G_SIGNAL_RUN_FIRST,
+                      0,
+                      NULL, /* accumulator */
+                      NULL, /* accu_data */
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
 
     _gweather_gettext_init ();
 }
 
 /**
  * gweather_info_new:
- * @location: (allow-none): the desidered #GWeatherLocation (%NULL for default)
+ * @location: (nullable): the desidered location
  *
- * Builds a new #GWeatherInfo that will provide weather information about
- * @location.
+ * Builds a new `GWeatherInfo` that will provide weather information about
+ * the given location.
  *
- * Since version 40, this does not automatically call gweather_info_update().
- * gweather_info_update() must be called manually once all the necessary set up
- * has been made, such as setting the enabled providers.
+ * In order to retrieve the weather information, you will need to enable
+ * the desired providers and then call [method GWeather Info.update]. If
+ * you want to be notified of the completion of the weather information
+ * update, you should connect to the [signal GWeather Info::updated]
+ * signal before updating the `GWeatherInfo` instance.
  *
- * Returns: (transfer full): a new #GWeatherInfo
+ * Returns: (transfer full): a new weather information instance
  */
 GWeatherInfo *
 gweather_info_new (GWeatherLocation *location)
 {
-    if (location != NULL)
-        return g_object_new (GWEATHER_TYPE_INFO, "location", location, NULL);
-    return g_object_new (GWEATHER_TYPE_INFO, NULL);
+    g_return_val_if_fail (location == NULL || GWEATHER_IS_LOCATION (location), NULL);
+
+    return g_object_new (GWEATHER_TYPE_INFO, "location", location, NULL);
 }
 
 GWeatherInfo *
 _gweather_info_new_clone (GWeatherInfo *original)
 {
+    g_return_val_if_fail (GWEATHER_IS_INFO (original), NULL);
+
     return g_object_new (GWEATHER_TYPE_INFO,
                          "application-id",
                          original->application_id,
diff --git a/libgweather/tests/test_libgweather.c b/libgweather/tests/test_libgweather.c
index ece4851a..dd86b93e 100644
--- a/libgweather/tests/test_libgweather.c
+++ b/libgweather/tests/test_libgweather.c
@@ -48,7 +48,7 @@ test_named_timezones (void)
         g_assert_true (code[0] == '@');
     }
 
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     _gweather_location_reset_world ();
 }
 
@@ -126,7 +126,7 @@ test_named_timezones_deserialized (void)
 
     list = get_list_from_configuration (world, CONFIGURATION, 3);
     for (l = list; l != NULL; l = l->next)
-        gweather_location_unref (l->data);
+        g_object_unref (l->data);
     g_list_free (list);
 
     list = get_list_from_configuration (world, CONFIGURATION, 3);
@@ -141,11 +141,11 @@ test_named_timezones_deserialized (void)
         g_assert_nonnull (tzid);
         gweather_location_get_level (loc);
 
-        gweather_location_unref (loc);
+        g_object_unref (loc);
     }
     g_list_free (list);
 
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     /* test_timezones will clear the DB */
     test_timezones ();
 }
@@ -179,9 +179,9 @@ test_no_code_serialize (void)
     g_assert_cmpstr (gweather_location_get_name (loc), ==, gweather_location_get_name (new_loc));
     g_assert_true (gweather_location_equal (loc, new_loc));
 
-    g_clear_pointer (&world, gweather_location_unref);
-    g_clear_pointer (&loc, gweather_location_unref);
-    g_clear_pointer (&new_loc, gweather_location_unref);
+    g_clear_object (&world);
+    g_clear_object (&loc);
+    g_clear_pointer (&new_loc, g_object_unref);
     _gweather_location_reset_world ();
 }
 
@@ -240,7 +240,7 @@ test_timezones (void)
 
     test_timezones_children (world);
 
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     _gweather_location_reset_world ();
 }
 
@@ -291,7 +291,7 @@ test_airport_distance_sanity (void)
     if (g_test_failed ())
         g_warning ("Maximum city to airport distance is %.1f km", max_distance);
 
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     _gweather_location_reset_world ();
 }
 
@@ -448,7 +448,7 @@ test_metar_weather_stations (void)
 
     g_hash_table_unref (stations_ht);
 
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     _gweather_location_reset_world ();
 }
 
@@ -522,8 +522,8 @@ test_utc_sunset (void)
 
     g_object_unref (info);
 
-    g_clear_pointer (&world, gweather_location_unref);
-    g_clear_pointer (&utc, gweather_location_unref);
+    g_clear_object (&world);
+    g_clear_object (&utc);
     _gweather_location_reset_world ();
 }
 
@@ -577,10 +577,10 @@ test_bad_duplicate_weather_stations_children (GWeatherLocation *location,
 
             stations = g_hash_table_lookup (stations_ht, code);
             if (!stations) {
-                stations = g_ptr_array_new_with_free_func ((GDestroyNotify) gweather_location_unref);
+                stations = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
                 g_hash_table_insert (stations_ht, g_strdup (code), stations);
             }
-            g_ptr_array_add (stations, gweather_location_ref (child));
+            g_ptr_array_add (stations, g_object_ref (child));
         } else {
             test_bad_duplicate_weather_stations_children (child, stations_ht);
         }
@@ -605,7 +605,7 @@ test_bad_duplicate_weather_stations (void)
     g_hash_table_unref (stations_ht);
 
     g_unsetenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST");
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     _gweather_location_reset_world ();
 }
 
@@ -657,7 +657,7 @@ test_duplicate_weather_stations (void)
     test_duplicate_weather_stations_children (world);
 
     g_unsetenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST");
-    g_clear_pointer (&world, gweather_location_unref);
+    g_clear_object (&world);
     _gweather_location_reset_world ();
 }
 
@@ -683,8 +683,8 @@ test_location_names (void)
         return;
     }
 
-    g_clear_pointer (&world, gweather_location_unref);
-    g_clear_pointer (&brussels, gweather_location_unref);
+    g_clear_object (&world);
+    g_clear_object (&brussels);
     _gweather_location_reset_world ();
 
     world = gweather_location_get_world ();
@@ -695,11 +695,11 @@ test_location_names (void)
     g_assert_cmpstr (gweather_location_get_name (brussels), ==, "Bruxelles");
     g_assert_cmpstr (gweather_location_get_sort_name (brussels), ==, "bruxelles");
     g_assert_cmpstr (gweather_location_get_english_name (brussels), ==, "Brussels");
-    gweather_location_unref (brussels);
+    g_object_unref (brussels);
 
     setlocale (LC_ALL, old_locale);
-    g_clear_pointer (&world, gweather_location_unref);
-    g_clear_pointer (&brussels, gweather_location_unref);
+    g_clear_object (&world);
+    g_clear_object (&brussels);
     _gweather_location_reset_world ();
 }
 
@@ -715,7 +715,7 @@ find_loc_children (GWeatherLocation *location,
 
             code = gweather_location_get_code (child);
             if (g_strcmp0 (search_str, code) == 0) {
-                *ret = gweather_location_ref (child);
+                *ret = g_object_ref (child);
                 return TRUE;
             }
         } else {
@@ -785,7 +785,7 @@ test_weather_loop_use_after_free (void)
     gweather_info_update (info);
     g_object_unref (info);
 
-    gweather_location_unref (loc);
+    g_object_unref (loc);
 
     g_timeout_add_seconds (5, stop_loop_cb, loop);
     g_main_loop_run (loop);
@@ -801,31 +801,30 @@ test_walk_world (void)
     next = gweather_location_get_world ();
     while (next) {
         /* Update cur pointer. */
-        g_clear_pointer (&cur, gweather_location_unref);
+        g_clear_object (&cur);
+
         cur = g_steal_pointer (&next);
         visited += 1;
-        g_assert_cmpint (cur->ref_count, ==, 1);
 
         /* Select next item, which is in this order:
-        *  1. The first child
-        *  2. Walk up the parent tree and try to find a sibbling
-        * Note that cur remains valid after the loop and points to the world
-        * again.
-        */
+            *  1. The first child
+            *  2. Walk up the parent tree and try to find a sibbling
+            * Note that cur remains valid after the loop and points to the world
+            * again.
+            */
         if ((next = gweather_location_next_child (cur, NULL)))
             continue;
 
         while (TRUE) {
             g_autoptr (GWeatherLocation) child = NULL;
+
             /* Move cur to the parent, keeping the child as reference. */
             child = g_steal_pointer (&cur);
             cur = gweather_location_get_parent (child);
             if (!cur)
                 break;
-            g_assert_cmpint (cur->ref_count, ==, 1);
-            g_assert_cmpint (child->ref_count, ==, 1);
 
-            if ((next = gweather_location_next_child (cur, gweather_location_ref (child))))
+            if ((next = gweather_location_next_child (cur, g_object_ref (child))))
                 break;
         }
     }
@@ -838,7 +837,7 @@ test_walk_world (void)
      * of DB entries. */
     cur = gweather_location_get_world ();
     g_assert_cmpint (visited, >, cur->db->locations->len);
-    g_clear_pointer (&cur, gweather_location_unref);
+    g_clear_object (&cur);
 
     /* noop, but asserts we did not leak */
     _gweather_location_reset_world ();
diff --git a/libgweather/tools/test_weather.c b/libgweather/tools/test_weather.c
index 99dd1875..569307ae 100644
--- a/libgweather/tools/test_weather.c
+++ b/libgweather/tools/test_weather.c
@@ -24,7 +24,7 @@ find_loc_children (GWeatherLocation *location,
 
             code = gweather_location_get_code (child);
             if (g_strcmp0 (search_str, code) == 0) {
-                *ret = gweather_location_ref (child);
+                *ret = g_object_ref (child);
                 return TRUE;
             }
         } else {


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