Initial weather service.
This commit introduces two new classes: 1. GCalWeatherService 2. GCalWeatherInfo GcalWeatherservice picks-up the current location and periodically fetches weather reports represented by GcalWeatherInfo. The weather part is not complete yet.
This commit is contained in:
committed by
Georges Basile Stavracas Neto
parent
fcd9fa1c31
commit
83bec27ad5
@@ -2,6 +2,7 @@ subdir('icons')
|
||||
subdir('appdata')
|
||||
|
||||
# Desktop files
|
||||
# Note: This name is registered in gcal-weather-service.c.
|
||||
desktop = 'org.gnome.Calendar.desktop'
|
||||
|
||||
i18n.merge_file(
|
||||
|
||||
+5
-1
@@ -130,7 +130,8 @@ common_flags = [
|
||||
'-DPACKAGE_DATA_DIR="@0@"'.format(calendar_pkgdatadir),
|
||||
'-DUI_DATA_DIR="@0@"'.format(join_paths(calendar_datadir), 'style'),
|
||||
'-DEDS_DISABLE_DEPRECATED',
|
||||
'-DGOA_API_IS_SUBJECT_TO_CHANGE'
|
||||
'-DGOA_API_IS_SUBJECT_TO_CHANGE',
|
||||
'-DGWEATHER_I_KNOW_THIS_IS_UNSTABLE'
|
||||
]
|
||||
|
||||
test_cflags = ['-Wno-sign-compare']
|
||||
@@ -175,6 +176,9 @@ glib_dep = dependency('glib-2.0', version: '>= 2.43.4')
|
||||
gtk_dep = dependency('gtk+-3.0', version: '>= 3.22.20')
|
||||
gio_dep = dependency('gio-2.0', version: '>= 2.43.4')
|
||||
goa_dep = dependency('goa-1.0', version: '>= 3.2.0')
|
||||
gweather_dep = dependency('gweather-3.0', version: '>= 3.24.0')
|
||||
geoclue_dep = dependency('libgeoclue-2.0', version: '>=2.4')
|
||||
geocode_dep = dependency('geocode-glib-1.0', version: '>=3.23')
|
||||
m_dep = cc.find_library('m')
|
||||
|
||||
configure_file(
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/* gcal-weather-info.c
|
||||
*
|
||||
* Copyright (C) 2017 Florian Brosch <flo.brosch@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "gcal-weather-info.h"
|
||||
|
||||
static void gcal_weather_info_set_date (GcalWeatherInfo *self,
|
||||
GDate *date);
|
||||
|
||||
static void gcal_weather_info_set_temperature (GcalWeatherInfo *self,
|
||||
const gchar *temperature);
|
||||
|
||||
static void gcal_weather_info_set_icon_name (GcalWeatherInfo *self,
|
||||
const gchar *icon_name);
|
||||
|
||||
|
||||
/* _GcalWeatherInfo:
|
||||
* @date: (not nullable): The day this information belongs to.
|
||||
* @name: (not nullable): Icon name if weather informations are available or NULL.
|
||||
* @temperature: (not nullable): Temperature string including right units.
|
||||
*
|
||||
* Data type holding weather information.
|
||||
*
|
||||
* Geometric information and @image should only be set
|
||||
* inside widgets.
|
||||
*/
|
||||
struct _GcalWeatherInfo {
|
||||
GObject parent_instance;
|
||||
|
||||
/* <private> */
|
||||
GDate date; /* owned, non-null */
|
||||
gchar *icon_name; /* owned, non-null */
|
||||
gchar *temperature; /* owned, non-null */
|
||||
};
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GcalWeatherInfo, gcal_weather_info, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_DATE,
|
||||
PROP_ICON_NAME,
|
||||
PROP_TEMPERATURE,
|
||||
PROP_NUM,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* < gobject setup > */
|
||||
|
||||
static void
|
||||
gcal_weather_info_finalize (GObject *object)
|
||||
{
|
||||
GcalWeatherInfo *self; /* unowned */
|
||||
|
||||
self = (GcalWeatherInfo *) object;
|
||||
|
||||
g_date_clear (&self->date, 1);
|
||||
g_free (self->icon_name);
|
||||
g_free (self->temperature);
|
||||
|
||||
G_OBJECT_CLASS (gcal_weather_info_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_info_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalWeatherInfo *self; /* unowned */
|
||||
|
||||
self = GCAL_WEATHER_INFO (object);
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_DATE:
|
||||
g_value_set_boxed (value, &self->date);
|
||||
break;
|
||||
case PROP_ICON_NAME:
|
||||
g_value_set_string (value, gcal_weather_info_get_icon_name (self));
|
||||
break;
|
||||
case PROP_TEMPERATURE:
|
||||
g_value_set_string (value, gcal_weather_info_get_temperature (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_info_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalWeatherInfo *self; /* unowned */
|
||||
|
||||
self = GCAL_WEATHER_INFO (object);
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_DATE:
|
||||
gcal_weather_info_set_date (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
gcal_weather_info_set_icon_name (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_TEMPERATURE:
|
||||
gcal_weather_info_set_temperature (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_info_class_init (GcalWeatherInfoClass *klass)
|
||||
{
|
||||
GObjectClass *object_class; /* unowned */
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = gcal_weather_info_finalize;
|
||||
object_class->get_property = gcal_weather_info_get_property;
|
||||
object_class->set_property = gcal_weather_info_set_property;
|
||||
|
||||
const gint pflags_rwc = G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
|
||||
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
|
||||
|
||||
/**
|
||||
* GcalWeatherInfo:date:
|
||||
*
|
||||
* The non-nullable date weather information belongs to.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_DATE,
|
||||
g_param_spec_boxed ("date", "date", "date", G_TYPE_DATE, pflags_rwc));
|
||||
/**
|
||||
* GcalWeatherInfo:icon-name:
|
||||
*
|
||||
* Non-nullable Icon name representing the weather.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_ICON_NAME,
|
||||
g_param_spec_string ("icon-name", "icon-name", "icon-name", NULL, pflags_rwc));
|
||||
/**
|
||||
* GcalWeatherInfo:temperature:
|
||||
*
|
||||
* The temperature as string or %NULL.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_TEMPERATURE,
|
||||
g_param_spec_string ("temperature", "temperature", "temperature", NULL, pflags_rwc));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_info_init (GcalWeatherInfo *self)
|
||||
{
|
||||
g_date_clear (&self->date, 1);
|
||||
self->icon_name = NULL;
|
||||
self->temperature = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* < private > */
|
||||
|
||||
/* gcal_weather_info_set_date:
|
||||
* @self: A #GcalWeatherInfo instance.
|
||||
* @date: The date to set.
|
||||
*
|
||||
* Setter for :date.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_info_set_date (GcalWeatherInfo *self,
|
||||
GDate *date)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_INFO (self));
|
||||
g_return_if_fail (date != NULL);
|
||||
|
||||
self->date = *date;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_info_set_temperature:
|
||||
* @self: A #GcalWeatherInfo instance.
|
||||
* @temperature: A weather string.
|
||||
*
|
||||
* Setter for #GcalWeatherInfo:temperature.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_info_set_temperature (GcalWeatherInfo *self,
|
||||
const gchar *temperature)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_INFO (self));
|
||||
g_return_if_fail (temperature != NULL);
|
||||
|
||||
if (g_strcmp0 (self->temperature, temperature) != 0)
|
||||
{
|
||||
g_free (self->temperature);
|
||||
self->temperature = g_strdup (temperature);
|
||||
g_object_notify ((GObject*) self, "temperature");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_info_set_icon_name:
|
||||
* @self: A #GcalWeatherInfo instance.
|
||||
* @icon_name: The name of the icon to display.
|
||||
*
|
||||
* Setter for #GcalWeatherInfo:icon_name.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_info_set_icon_name (GcalWeatherInfo *self,
|
||||
const gchar *icon_name)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_INFO (self));
|
||||
g_return_if_fail (icon_name != NULL);
|
||||
|
||||
if (g_strcmp0 (self->icon_name, icon_name) != 0)
|
||||
{
|
||||
g_free (self->icon_name);
|
||||
self->icon_name = g_strdup (icon_name);
|
||||
g_object_notify ((GObject*) self, "icon-name");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* < public > */
|
||||
|
||||
/**
|
||||
* gcal_weather_info_new:
|
||||
* @date: (not nullable): The date weather infos belong to.
|
||||
* @icon_name: (not nullable): The icon name indicating the weather.
|
||||
* @temperature: (nullable): The temperature to display.
|
||||
*
|
||||
* Creates a new #GcalWeatherInfo.
|
||||
*
|
||||
* @Returns: (transfer full): A newly allocated #GcalWeatherInfo.
|
||||
*/
|
||||
GcalWeatherInfo*
|
||||
gcal_weather_info_new (GDate *date,
|
||||
const gchar *icon_name,
|
||||
const gchar *temperature)
|
||||
{
|
||||
GcalWeatherInfo *info; /* owned */
|
||||
|
||||
g_return_val_if_fail (date != NULL, NULL);
|
||||
g_return_val_if_fail (icon_name != NULL, NULL);
|
||||
g_return_val_if_fail (temperature != NULL, NULL);
|
||||
|
||||
info = (GcalWeatherInfo*) g_object_new (GCAL_TYPE_WEATHER_INFO,
|
||||
"date", date,
|
||||
"icon-name", icon_name,
|
||||
"temperature", temperature,
|
||||
NULL);
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_info_get_date:
|
||||
* @self: A #GcalWeatherInfo instance.
|
||||
* @date: (out) (not nullable): The associated day.
|
||||
*
|
||||
* Getter for #GcalWeatherInfo:date.
|
||||
*/
|
||||
void
|
||||
gcal_weather_info_get_date (GcalWeatherInfo *self,
|
||||
GDate *date)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_INFO (self));
|
||||
|
||||
*date = self->date;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_info_get_icon_name:
|
||||
* @self: A #GcalWeatherInfo instance.
|
||||
*
|
||||
* Getter for #GcalWeatherInfo:icon_name.
|
||||
*/
|
||||
const gchar*
|
||||
gcal_weather_info_get_icon_name (GcalWeatherInfo *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_WEATHER_INFO (self), NULL);
|
||||
|
||||
return self->icon_name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_info_get_temperature:
|
||||
* @self: A #GcalWeatherInfo instance.
|
||||
*
|
||||
* Getter for #GcalWeatherInfo:temperature.
|
||||
*/
|
||||
const gchar*
|
||||
gcal_weather_info_get_temperature (GcalWeatherInfo *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_WEATHER_INFO (self), NULL);
|
||||
|
||||
return self->temperature;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* gcal-weather-info.h
|
||||
*
|
||||
* Copyright (C) 2017 Florian Brosch <flo.brosch@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GCAL_WEATHER_INFO_H__
|
||||
#define __GCAL_WEATHER_INFO_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GCAL_TYPE_WEATHER_INFO (gcal_weather_info_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GcalWeatherInfo, gcal_weather_info, GCAL, WEATHER_INFO, GObject)
|
||||
|
||||
|
||||
GcalWeatherInfo* gcal_weather_info_new (GDate *date,
|
||||
const gchar *icon_name,
|
||||
const gchar *temperature);
|
||||
|
||||
void gcal_weather_info_get_date (GcalWeatherInfo *self,
|
||||
GDate *date);
|
||||
|
||||
void gcal_weather_info_get_date (GcalWeatherInfo *self,
|
||||
GDate *date);
|
||||
|
||||
const gchar* gcal_weather_info_get_icon_name (GcalWeatherInfo *self);
|
||||
|
||||
const gchar* gcal_weather_info_get_temperature (GcalWeatherInfo *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GCAL_WEATHER_INFO_H__ */
|
||||
|
||||
|
||||
@@ -0,0 +1,779 @@
|
||||
/* gcal-weather-service.c
|
||||
*
|
||||
* Copyright (C) 2017 - Florian Brosch <flo.brosch@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DESKTOP_FILE_NAME "org.gnome.Calendar"
|
||||
|
||||
#include <geocode-glib/geocode-glib.h>
|
||||
#include <libgweather/gweather.h>
|
||||
#include <geoclue.h>
|
||||
|
||||
#include "gcal-weather-service.h"
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* GcalWeatherService:
|
||||
*
|
||||
* @location_service: Used to monitor location changes.
|
||||
* Initialized by gcal_weather_service_run(),
|
||||
* freed by gcal_weather_service_stop().
|
||||
* @location_cancellable: Used to deal with async location service construction.
|
||||
* @weather_info: The weather info to query.
|
||||
*
|
||||
* This service listens to location and weather changes and reports them.
|
||||
*
|
||||
* * Create a new instance with gcal_weather_service_new().
|
||||
* * Connect to ::changed to catch weather information.
|
||||
* * Use gcal_weather_service_start() to start the service.
|
||||
* * Use gcal_weather_service_stop() when you are done.
|
||||
*
|
||||
* Make sure to stop this service before destroying it.
|
||||
*/
|
||||
struct _GcalWeatherService
|
||||
{
|
||||
GObjectClass parent;
|
||||
|
||||
/* <public> */
|
||||
|
||||
/* <private> */
|
||||
/* timer: */
|
||||
guint check_interval;
|
||||
guint timeout_id;
|
||||
|
||||
/* locations: */
|
||||
GClueSimple *location_service; /* owned, nullable */
|
||||
GCancellable *location_cancellable; /* owned, non-null */
|
||||
gboolean location_service_running;
|
||||
|
||||
/* weather: */
|
||||
GWeatherInfo *weather_info; /* owned, nullable */
|
||||
guint max_days;
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MAX_DAYS,
|
||||
PROP_CHECK_INTERVAL,
|
||||
PROP_NUM,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIG_WEATHER_CHANGED,
|
||||
SIG_NUM,
|
||||
};
|
||||
|
||||
static guint gcal_weather_service_signals[SIG_NUM] = { 0 };
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GcalWeatherService, gcal_weather_service, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
/* Internal location API and callbacks: */
|
||||
static void gcal_weather_service_update_location (GcalWeatherService *self,
|
||||
GClueLocation *location);
|
||||
|
||||
static char* gcal_weather_service_get_location_name (GClueLocation *location);
|
||||
|
||||
static void on_gclue_simple_creation (GClueSimple *source,
|
||||
GAsyncResult *result,
|
||||
GcalWeatherService *data);
|
||||
|
||||
static void on_gclue_location_changed (GClueLocation *location,
|
||||
GcalWeatherService *self);
|
||||
|
||||
static void on_gclue_client_activity_changed (GClueClient *client,
|
||||
GcalWeatherService *self);
|
||||
|
||||
static void on_gclue_client_stop (GClueClient *client,
|
||||
GAsyncResult *res,
|
||||
GClueSimple *simple);
|
||||
|
||||
/* Internal Wweather API */
|
||||
static void gcal_weather_service_set_max_days (GcalWeatherService *self,
|
||||
guint days);
|
||||
|
||||
static void gcal_weather_service_update_weather (GWeatherInfo *info,
|
||||
GcalWeatherService *self);
|
||||
|
||||
/* Internal weather update timer API and callbacks */
|
||||
static void gcal_weather_service_set_check_interval (GcalWeatherService *self,
|
||||
guint check_interval);
|
||||
|
||||
static void gcal_weather_service_timer_stop (GcalWeatherService *self);
|
||||
|
||||
static void gcal_weather_service_timer_start (GcalWeatherService *self);
|
||||
|
||||
static gboolean on_timer_timeout (GcalWeatherService *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
/********************
|
||||
* < gobject setup >
|
||||
*******************/
|
||||
|
||||
static void
|
||||
gcal_weather_service_finalize (GObject *object)
|
||||
{
|
||||
GcalWeatherService *self; /* unowned */
|
||||
|
||||
self = (GcalWeatherService *) object;
|
||||
|
||||
if (self->location_service != NULL)
|
||||
g_clear_object (&self->location_service);
|
||||
|
||||
g_cancellable_cancel (self->location_cancellable);
|
||||
g_clear_object (&self->location_cancellable);
|
||||
|
||||
if (self->weather_info != NULL)
|
||||
g_clear_object (&self->weather_info);
|
||||
|
||||
G_OBJECT_CLASS (gcal_weather_service_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_service_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalWeatherService* self; /* unowned */
|
||||
|
||||
self = GCAL_WEATHER_SERVICE (object);
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MAX_DAYS:
|
||||
g_value_set_uint (value, gcal_weather_service_get_max_days (self));
|
||||
break;
|
||||
case PROP_CHECK_INTERVAL:
|
||||
g_value_set_uint (value, gcal_weather_service_get_check_interval (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_service_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalWeatherService* self; /* unowned */
|
||||
|
||||
self = GCAL_WEATHER_SERVICE (object);
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MAX_DAYS:
|
||||
gcal_weather_service_set_max_days (self, g_value_get_uint (value));
|
||||
break;
|
||||
case PROP_CHECK_INTERVAL:
|
||||
gcal_weather_service_set_check_interval (self, g_value_get_uint (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
|
||||
{
|
||||
GObjectClass *object_class; /* unowned */
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = gcal_weather_service_finalize;
|
||||
object_class->get_property = gcal_weather_service_get_property;
|
||||
object_class->set_property = gcal_weather_service_set_property;
|
||||
|
||||
const gint prop_flags = G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||
G_PARAM_STATIC_BLURB | G_PARAM_READABLE |
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
|
||||
|
||||
/**
|
||||
* GcalWeatherServiceClass:max-days:
|
||||
*
|
||||
* Maximal number of days to fetch forecasts for.
|
||||
*/
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass),
|
||||
PROP_MAX_DAYS,
|
||||
g_param_spec_uint ("max-days", "max-days", "max-days",
|
||||
0, G_MAXUINT, 0, prop_flags));
|
||||
/**
|
||||
* GcalWeatherServiceClass:check-interval:
|
||||
*
|
||||
* Amount of seconds to wait before re-fetching weather infos.
|
||||
* Use %0 to disable timers.
|
||||
*/
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass),
|
||||
PROP_CHECK_INTERVAL,
|
||||
g_param_spec_uint ("check-interval", "check-interval", "check-interval",
|
||||
0, G_MAXUINT, 0, prop_flags));
|
||||
|
||||
/**
|
||||
* GcalWeatherService::weather-changed:
|
||||
* @sender: The #GcalWeatherService
|
||||
* @gwi: A SList containing updated #GcalWeatherInfos.
|
||||
* @self: data pointer.
|
||||
*
|
||||
* Triggered on weather changes.
|
||||
*/
|
||||
gcal_weather_service_signals[SIG_WEATHER_CHANGED]
|
||||
= g_signal_new ("weather-changed",
|
||||
GCAL_TYPE_WEATHER_SERVICE,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__POINTER,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
G_TYPE_POINTER);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gcal_weather_service_init (GcalWeatherService *self)
|
||||
{
|
||||
self->check_interval = 0;
|
||||
self->timeout_id = 0;
|
||||
self->location_cancellable = g_cancellable_new ();
|
||||
self->location_service_running = FALSE;
|
||||
self->location_service = NULL;
|
||||
self->weather_info = NULL;
|
||||
self->max_days = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************
|
||||
* < private >
|
||||
**************/
|
||||
|
||||
/**
|
||||
* gcal_weather_service_update_location:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
* @location: (nullable) (element-type Gcal.WeatherInfo): The location we want weather information for.
|
||||
*
|
||||
* Registers the location to retrieve weather information from.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_update_location (GcalWeatherService *self,
|
||||
GClueLocation *location)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
g_return_if_fail (location == NULL || GCLUE_IS_LOCATION (location));
|
||||
|
||||
if (self->weather_info != NULL)
|
||||
{
|
||||
gcal_weather_service_timer_stop (self);
|
||||
g_clear_object (&self->weather_info);
|
||||
}
|
||||
|
||||
if (location == NULL)
|
||||
{
|
||||
g_info ("Could not retrieve current location.");
|
||||
gcal_weather_service_update_weather (NULL, self);
|
||||
}
|
||||
else
|
||||
{
|
||||
GWeatherLocation *wlocation; /* owned */
|
||||
g_autofree gchar *loc_name = NULL;
|
||||
gdouble latitude;
|
||||
gdouble longitude;
|
||||
|
||||
loc_name = gcal_weather_service_get_location_name (location);
|
||||
latitude = gclue_location_get_latitude (location);
|
||||
longitude = gclue_location_get_longitude (location);
|
||||
wlocation = gweather_location_new_detached (loc_name, NULL, latitude, longitude);
|
||||
|
||||
self->weather_info = gweather_info_new (wlocation, GWEATHER_FORECAST_ZONE);
|
||||
|
||||
gweather_info_set_enabled_providers (self->weather_info, GWEATHER_PROVIDER_ALL);
|
||||
g_signal_connect (self->weather_info, "updated", (GCallback) gcal_weather_service_update_weather, self);
|
||||
gweather_info_update (self->weather_info);
|
||||
|
||||
gcal_weather_service_timer_start (self);
|
||||
gweather_location_unref (wlocation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_get_location_name:
|
||||
* @location: A #GClueLocation to get the city name from.
|
||||
*
|
||||
* Queries the city name for the given location or %NULL.
|
||||
*
|
||||
* @Returns: (transfer full): City name or %NULL.
|
||||
*/
|
||||
static char*
|
||||
gcal_weather_service_get_location_name (GClueLocation *location)
|
||||
{
|
||||
g_autoptr (GeocodeLocation) glocation = NULL;
|
||||
g_autoptr (GeocodeReverse) greverse = NULL;
|
||||
g_autoptr (GeocodePlace) gplace = NULL;
|
||||
|
||||
g_return_val_if_fail (GCLUE_IS_LOCATION (location), NULL);
|
||||
|
||||
glocation = geocode_location_new (gclue_location_get_latitude (location),
|
||||
gclue_location_get_longitude (location),
|
||||
gclue_location_get_accuracy (location));
|
||||
|
||||
greverse = geocode_reverse_new_for_location (glocation);
|
||||
|
||||
gplace = geocode_reverse_resolve (greverse, NULL);
|
||||
if (gplace == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_strdup (geocode_place_get_town (gplace));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* on_gclue_simple_creation:
|
||||
* @source:
|
||||
* @result: Result of gclue_simple_new().
|
||||
* @self: (transfer full): A GcalWeatherService reference.
|
||||
*
|
||||
* Callback used in gcal_weather_service_run().
|
||||
*/
|
||||
static void
|
||||
on_gclue_simple_creation (GClueSimple *_source,
|
||||
GAsyncResult *result,
|
||||
GcalWeatherService *self)
|
||||
{
|
||||
GClueSimple *location_service; /* owned */
|
||||
GClueLocation *location; /* unowned */
|
||||
GClueClient *client; /* unowned */
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
g_return_if_fail (G_IS_ASYNC_RESULT (result));
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
g_return_if_fail (self->location_service_running);
|
||||
|
||||
/* make sure we do not touch self->location_service
|
||||
* if the current operation was cancelled.
|
||||
*/
|
||||
location_service = gclue_simple_new_finish (result, &error);
|
||||
if (error != NULL)
|
||||
{
|
||||
g_assert (location_service == NULL);
|
||||
|
||||
if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
|
||||
/* Cancelled during creation. Silently fail. */;
|
||||
else
|
||||
g_warning ("Could not create GCLueSimple: %s", error->message);
|
||||
|
||||
g_object_unref (self);
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert (self->location_service == NULL);
|
||||
g_assert (location_service != NULL);
|
||||
|
||||
self->location_service = g_steal_pointer (&location_service);
|
||||
|
||||
location = gclue_simple_get_location (self->location_service);
|
||||
client = gclue_simple_get_client (self->location_service);
|
||||
|
||||
if (location != NULL)
|
||||
{
|
||||
gcal_weather_service_update_location (self, location);
|
||||
|
||||
|
||||
g_signal_connect_object (location,
|
||||
"notify::location",
|
||||
G_CALLBACK (on_gclue_location_changed),
|
||||
self,
|
||||
0);
|
||||
}
|
||||
|
||||
g_signal_connect_object (client,
|
||||
"notify::active",
|
||||
G_CALLBACK (on_gclue_client_activity_changed),
|
||||
self,
|
||||
0);
|
||||
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* on_gclue_location_changed:
|
||||
* @location: #GClueLocation owned by @self
|
||||
* @self: The #GcalWeatherService
|
||||
*
|
||||
* Handles location changes.
|
||||
*/
|
||||
static void
|
||||
on_gclue_location_changed (GClueLocation *location,
|
||||
GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCLUE_IS_LOCATION (location));
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
g_return_if_fail (self->location_service_running);
|
||||
|
||||
gcal_weather_service_update_location (self, location);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* on_gclue_client_activity_changed:
|
||||
* @client: The #GClueclient ownd by @self
|
||||
* @self: The #GcalWeatherService
|
||||
*
|
||||
* Handles location client activity changes.
|
||||
*/
|
||||
static void
|
||||
on_gclue_client_activity_changed (GClueClient *client,
|
||||
GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCLUE_IS_CLIENT (client));
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
g_return_if_fail (self->location_service_running);
|
||||
|
||||
/* Notify listeners about unknown locations: */
|
||||
gcal_weather_service_update_location (self, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* on_gclue_client_stop:
|
||||
* @source_object: A #GClueClient.
|
||||
* @res: Result of gclue_client_call_stop().
|
||||
* @simple: (transfer full): A #GClueSimple.
|
||||
*
|
||||
* Helper-callback used in gcal_weather_service_stop().
|
||||
*/
|
||||
static void
|
||||
on_gclue_client_stop (GClueClient *client,
|
||||
GAsyncResult *res,
|
||||
GClueSimple *simple)
|
||||
{
|
||||
g_autoptr(GError) error = NULL; /* owned */
|
||||
gboolean stopped;
|
||||
|
||||
g_return_if_fail (GCLUE_IS_CLIENT (client));
|
||||
g_return_if_fail (G_IS_ASYNC_RESULT (res));
|
||||
g_return_if_fail (GCLUE_IS_SIMPLE (simple));
|
||||
|
||||
stopped = gclue_client_call_stop_finish (client,
|
||||
res,
|
||||
&error);
|
||||
if (error != NULL)
|
||||
g_warning ("Could not stop location service: %s", error->message);
|
||||
else if (!stopped)
|
||||
g_warning ("Could not stop location service");
|
||||
|
||||
g_object_unref (simple);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_set_max_days:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
* @days: Number of days.
|
||||
*
|
||||
* Setter for #GcalWeatherInfos:max-days.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_set_max_days (GcalWeatherService *self,
|
||||
guint days)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
self->max_days = days;
|
||||
|
||||
g_object_notify ((GObject*) self, "max-days");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_update_weather:
|
||||
* @info: (nullable): Newly received weather information or %NULL.
|
||||
* @self: A #GcalWeatherService instance.
|
||||
*
|
||||
* Retrieves weather information for @location and triggers
|
||||
* #GcalWeatherService::weather-changed.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_update_weather (GWeatherInfo *info,
|
||||
GcalWeatherService *self)
|
||||
{
|
||||
GSList* infos = NULL;
|
||||
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
g_info ("Could not retrieve valid weather");
|
||||
}
|
||||
else if (gweather_info_is_valid (info))
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree gchar* location_name = gweather_info_get_location_name (info);
|
||||
g_info ("Could not retrieve valid weather for location '%s'", location_name);
|
||||
}
|
||||
|
||||
g_signal_emit (self, gcal_weather_service_signals[SIG_WEATHER_CHANGED], 0, infos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_set_max_days:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
* @days: Number of days.
|
||||
*
|
||||
* Setter for GcalWeatherInfos:max-days.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_set_check_interval (GcalWeatherService *self,
|
||||
guint interval)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
self->check_interval = interval;
|
||||
|
||||
g_object_notify ((GObject*) self, "check-interval");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_timer_stop:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Stops the internal timer. Does nothing if
|
||||
* timer is not running.
|
||||
*
|
||||
* This timer is used to update weather information
|
||||
* every :check-interval seconds
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_timer_stop (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (self->timeout_id > 0)
|
||||
{
|
||||
g_source_remove (self->timeout_id);
|
||||
self->timeout_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_timer_start
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Starts the internal timer. Does nothing if
|
||||
* timer is already running.
|
||||
*
|
||||
* Timer is used to update weather information
|
||||
* every :check-interval seconds.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_timer_start (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (self->check_interval == 0)
|
||||
/* timer is disabled */
|
||||
return;
|
||||
|
||||
if (self->timeout_id > 0)
|
||||
/* timer is already running */
|
||||
return;
|
||||
|
||||
self->timeout_id = g_timeout_add_seconds (self->check_interval,
|
||||
(GSourceFunc) on_timer_timeout,
|
||||
self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* on_timer_timeout
|
||||
* @self: A #GcalWeatherService.
|
||||
*
|
||||
* Handles scheduled weather report updates.
|
||||
*
|
||||
* Returns: %G_SOURCE_CONTINUE
|
||||
*/
|
||||
static gboolean
|
||||
on_timer_timeout (GcalWeatherService *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), G_SOURCE_REMOVE);
|
||||
|
||||
if (self->weather_info != NULL)
|
||||
gweather_info_update (self->weather_info);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*************
|
||||
* < public >
|
||||
*************/
|
||||
|
||||
/**
|
||||
* gcal_weather_service_new:
|
||||
* @max_days: mumber of days to fetch forecasts for.
|
||||
* @check_interval: seconds between checks for new weather information or %0 to disable checks.
|
||||
*
|
||||
* Creates a new #GcalWeatherService. This service listens
|
||||
* to location and weather changes and reports them.
|
||||
*
|
||||
* Returns: (transfer full): A newly created #GcalWeatherService.
|
||||
*/
|
||||
GcalWeatherService *
|
||||
gcal_weather_service_new (guint max_days,
|
||||
guint check_interval)
|
||||
{
|
||||
return g_object_new (GCAL_TYPE_WEATHER_SERVICE,
|
||||
"max-days",
|
||||
max_days,
|
||||
"check-interval",
|
||||
check_interval,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_service_run:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Starts to monitor location and weather changes.
|
||||
* Use ::weather-changed to catch responses.
|
||||
*/
|
||||
void
|
||||
gcal_weather_service_run (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (self->location_service_running)
|
||||
return ;
|
||||
|
||||
self->location_service_running = TRUE;
|
||||
|
||||
g_assert (self->location_service == NULL);
|
||||
|
||||
if (self->max_days > 0)
|
||||
{
|
||||
g_cancellable_cancel (self->location_cancellable);
|
||||
g_cancellable_reset (self->location_cancellable);
|
||||
gclue_simple_new (DESKTOP_FILE_NAME,
|
||||
GCLUE_ACCURACY_LEVEL_EXACT,
|
||||
self->location_cancellable,
|
||||
(GAsyncReadyCallback) on_gclue_simple_creation,
|
||||
g_object_ref (self));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_service_stop:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Stops the service. Returns gracefully if service is
|
||||
* not running.
|
||||
*/
|
||||
void
|
||||
gcal_weather_service_stop (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (!self->location_service_running)
|
||||
return ;
|
||||
|
||||
self->location_service_running = FALSE;
|
||||
|
||||
|
||||
gcal_weather_service_timer_stop (self);
|
||||
|
||||
/* Notify all listeners about unknown location: */
|
||||
gcal_weather_service_update_location (self, NULL);
|
||||
|
||||
if (self->location_service == NULL)
|
||||
{
|
||||
/* location service is under construction. Cancel creation. */
|
||||
g_cancellable_cancel (self->location_cancellable);
|
||||
}
|
||||
else
|
||||
{
|
||||
GClueClient *client; /* owned */
|
||||
|
||||
client = gclue_simple_get_client (self->location_service);
|
||||
self->location_service = NULL;
|
||||
|
||||
gclue_client_call_stop (client,
|
||||
NULL,
|
||||
(GAsyncReadyCallback) on_gclue_client_stop,
|
||||
client); /* transfres ownership */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_service_get_max_days:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Getter for #GcalWeatherService:max-days.
|
||||
*/
|
||||
guint
|
||||
gcal_weather_service_get_max_days (GcalWeatherService *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), 0);
|
||||
|
||||
return self->max_days;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_weather_service_get_max_days:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Getter for #GcalWeatherService:max-days.
|
||||
*/
|
||||
guint
|
||||
gcal_weather_service_get_check_interval (GcalWeatherService *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), 0);
|
||||
|
||||
return self->check_interval;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* gcal-weather-service.h
|
||||
*
|
||||
* Copyright (C) 2017 - Florian Brosch <flo.brosch@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GCAL_WEATHER_SERVICE_H
|
||||
#define GCAL_WEATHER_SERVICE_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GCAL_TYPE_WEATHER_SERVICE (gcal_weather_service_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GcalWeatherService, gcal_weather_service, GCAL, WEATHER_SERVICE, GObject)
|
||||
|
||||
|
||||
GcalWeatherService* gcal_weather_service_new (guint max_days,
|
||||
guint check_interval);
|
||||
|
||||
void gcal_weather_service_run (GcalWeatherService *self);
|
||||
|
||||
void gcal_weather_service_stop (GcalWeatherService *self);
|
||||
|
||||
guint gcal_weather_service_get_max_days (GcalWeatherService *self);
|
||||
|
||||
guint gcal_weather_service_get_check_interval (GcalWeatherService *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GCAL_WEATHER_SERVICE_H */
|
||||
|
||||
@@ -15,6 +15,9 @@ gcal_deps = [
|
||||
gtk_dep,
|
||||
gio_dep,
|
||||
goa_dep,
|
||||
gweather_dep,
|
||||
geoclue_dep,
|
||||
geocode_dep,
|
||||
m_dep,
|
||||
]
|
||||
|
||||
@@ -47,6 +50,8 @@ sources = files(
|
||||
'gcal-time-selector.c',
|
||||
'gcal-utils.c',
|
||||
'gcal-window.c',
|
||||
'gcal-weather-service.c',
|
||||
'gcal-weather-info.c',
|
||||
)
|
||||
|
||||
gnome.mkenums(
|
||||
|
||||
Reference in New Issue
Block a user