diff options
Diffstat (limited to 'plugins/datetime')
-rw-r--r-- | plugins/datetime/backward | 118 | ||||
-rw-r--r-- | plugins/datetime/gsd-datetime-manager.c | 226 | ||||
-rw-r--r-- | plugins/datetime/gsd-datetime-manager.h | 37 | ||||
-rw-r--r-- | plugins/datetime/gsd-timezone-monitor.c | 472 | ||||
-rw-r--r-- | plugins/datetime/gsd-timezone-monitor.h | 55 | ||||
-rw-r--r-- | plugins/datetime/main.c | 7 | ||||
-rw-r--r-- | plugins/datetime/meson.build | 40 | ||||
-rw-r--r-- | plugins/datetime/timedated1-interface.xml | 28 | ||||
-rw-r--r-- | plugins/datetime/tz.c | 482 | ||||
-rw-r--r-- | plugins/datetime/tz.h | 89 | ||||
-rw-r--r-- | plugins/datetime/weather-tz.c | 118 | ||||
-rw-r--r-- | plugins/datetime/weather-tz.h | 27 |
12 files changed, 1699 insertions, 0 deletions
diff --git a/plugins/datetime/backward b/plugins/datetime/backward new file mode 100644 index 0000000..f1f95a8 --- /dev/null +++ b/plugins/datetime/backward @@ -0,0 +1,118 @@ +# <pre> +# @(#)backward 8.9 +# This file is in the public domain, so clarified as of +# 2009-05-17 by Arthur David Olson. + +# This file provides links between current names for time zones +# and their old names. Many names changed in late 1993. + +Link Africa/Asmara Africa/Asmera +Link Africa/Bamako Africa/Timbuktu +Link America/Argentina/Catamarca America/Argentina/ComodRivadavia +Link America/Adak America/Atka +Link America/Argentina/Buenos_Aires America/Buenos_Aires +Link America/Argentina/Catamarca America/Catamarca +Link America/Atikokan America/Coral_Harbour +Link America/Argentina/Cordoba America/Cordoba +Link America/Tijuana America/Ensenada +Link America/Indiana/Indianapolis America/Fort_Wayne +Link America/Indiana/Indianapolis America/Indianapolis +Link America/Argentina/Jujuy America/Jujuy +Link America/Indiana/Knox America/Knox_IN +Link America/Kentucky/Louisville America/Louisville +Link America/Argentina/Mendoza America/Mendoza +Link America/Rio_Branco America/Porto_Acre +Link America/Argentina/Cordoba America/Rosario +Link America/St_Thomas America/Virgin +Link Asia/Ashgabat Asia/Ashkhabad +Link Asia/Chongqing Asia/Chungking +Link Asia/Dhaka Asia/Dacca +Link Asia/Kathmandu Asia/Katmandu +Link Asia/Kolkata Asia/Calcutta +Link Asia/Macau Asia/Macao +Link Asia/Jerusalem Asia/Tel_Aviv +Link Asia/Ho_Chi_Minh Asia/Saigon +Link Asia/Thimphu Asia/Thimbu +Link Asia/Makassar Asia/Ujung_Pandang +Link Asia/Ulaanbaatar Asia/Ulan_Bator +Link Atlantic/Faroe Atlantic/Faeroe +Link Europe/Oslo Atlantic/Jan_Mayen +Link Australia/Sydney Australia/ACT +Link Australia/Sydney Australia/Canberra +Link Australia/Lord_Howe Australia/LHI +Link Australia/Sydney Australia/NSW +Link Australia/Darwin Australia/North +Link Australia/Brisbane Australia/Queensland +Link Australia/Adelaide Australia/South +Link Australia/Hobart Australia/Tasmania +Link Australia/Melbourne Australia/Victoria +Link Australia/Perth Australia/West +Link Australia/Broken_Hill Australia/Yancowinna +Link America/Rio_Branco Brazil/Acre +Link America/Noronha Brazil/DeNoronha +Link America/Sao_Paulo Brazil/East +Link America/Manaus Brazil/West +Link America/Halifax Canada/Atlantic +Link America/Winnipeg Canada/Central +Link America/Regina Canada/East-Saskatchewan +Link America/Toronto Canada/Eastern +Link America/Edmonton Canada/Mountain +Link America/St_Johns Canada/Newfoundland +Link America/Vancouver Canada/Pacific +Link America/Regina Canada/Saskatchewan +Link America/Whitehorse Canada/Yukon +Link America/Santiago Chile/Continental +Link Pacific/Easter Chile/EasterIsland +Link America/Havana Cuba +Link Africa/Cairo Egypt +Link Europe/Dublin Eire +Link Europe/London Europe/Belfast +Link Europe/Chisinau Europe/Tiraspol +Link Europe/London GB +Link Europe/London GB-Eire +Link Etc/GMT GMT+0 +Link Etc/GMT GMT-0 +Link Etc/GMT GMT0 +Link Etc/GMT Greenwich +Link Asia/Hong_Kong Hongkong +Link Atlantic/Reykjavik Iceland +Link Asia/Tehran Iran +Link Asia/Jerusalem Israel +Link America/Jamaica Jamaica +Link Asia/Tokyo Japan +Link Pacific/Kwajalein Kwajalein +Link Africa/Tripoli Libya +Link America/Tijuana Mexico/BajaNorte +Link America/Mazatlan Mexico/BajaSur +Link America/Mexico_City Mexico/General +Link Pacific/Auckland NZ +Link Pacific/Chatham NZ-CHAT +Link America/Denver Navajo +Link Asia/Shanghai PRC +Link Pacific/Pago_Pago Pacific/Samoa +Link Pacific/Chuuk Pacific/Yap +Link Pacific/Chuuk Pacific/Truk +Link Pacific/Pohnpei Pacific/Ponape +Link Europe/Warsaw Poland +Link Europe/Lisbon Portugal +Link Asia/Taipei ROC +Link Asia/Seoul ROK +Link Asia/Singapore Singapore +Link Europe/Istanbul Turkey +Link Etc/UCT UCT +Link America/Anchorage US/Alaska +Link America/Adak US/Aleutian +Link America/Phoenix US/Arizona +Link America/Chicago US/Central +Link America/Indiana/Indianapolis US/East-Indiana +Link America/New_York US/Eastern +Link Pacific/Honolulu US/Hawaii +Link America/Indiana/Knox US/Indiana-Starke +Link America/Detroit US/Michigan +Link America/Denver US/Mountain +Link America/Los_Angeles US/Pacific +Link Pacific/Pago_Pago US/Samoa +Link Etc/UTC UTC +Link Etc/UTC Universal +Link Europe/Moscow W-SU +Link Etc/UTC Zulu diff --git a/plugins/datetime/gsd-datetime-manager.c b/plugins/datetime/gsd-datetime-manager.c new file mode 100644 index 0000000..dcd9f8c --- /dev/null +++ b/plugins/datetime/gsd-datetime-manager.c @@ -0,0 +1,226 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Kalev Lember <kalevlember@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 2 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 "config.h" + +#include <gio/gio.h> +#include <glib/gi18n.h> +#include <libnotify/notify.h> + +#include "gsd-datetime-manager.h" +#include "gsd-timezone-monitor.h" +#include "gnome-settings-profile.h" + +#define DATETIME_SCHEMA "org.gnome.desktop.datetime" +#define AUTO_TIMEZONE_KEY "automatic-timezone" + +struct _GsdDatetimeManager +{ + GObject parent; + + GSettings *settings; + GsdTimezoneMonitor *timezone_monitor; + NotifyNotification *notification; +}; + +static void gsd_datetime_manager_class_init (GsdDatetimeManagerClass *klass); +static void gsd_datetime_manager_init (GsdDatetimeManager *manager); +static void gsd_datetime_manager_finalize (GObject *object); + +G_DEFINE_TYPE (GsdDatetimeManager, gsd_datetime_manager, G_TYPE_OBJECT) + +static gpointer manager_object = NULL; + +static void +notification_closed_cb (NotifyNotification *n, + GsdDatetimeManager *self) +{ + g_clear_object (&self->notification); +} + +static void +open_settings_cb (NotifyNotification *n, + const char *action, + const char *path) +{ + const gchar *argv[] = { "gnome-control-center", "datetime", NULL }; + + g_debug ("Running gnome-control-center datetime"); + g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL); + + notify_notification_close (n, NULL); +} + +static void +timezone_changed_cb (GsdTimezoneMonitor *timezone_monitor, + const gchar *timezone_id, + GsdDatetimeManager *self) +{ + GDateTime *datetime; + GTimeZone *tz; + gchar *notification_summary; + gchar *timezone_name; + gchar *utc_offset; + + tz = g_time_zone_new (timezone_id); + datetime = g_date_time_new_now (tz); + g_time_zone_unref (tz); + + /* Translators: UTC here means the Coordinated Universal Time. + * %:::z will be replaced by the offset from UTC e.g. UTC+02 */ + utc_offset = g_date_time_format (datetime, _("UTC%:::z")); + timezone_name = g_strdup (g_date_time_get_timezone_abbreviation (datetime)); + g_date_time_unref (datetime); + + notification_summary = g_strdup_printf (_("Time Zone Updated to %s (%s)"), + timezone_name, + utc_offset); + g_free (timezone_name); + g_free (utc_offset); + + if (self->notification == NULL) { + self->notification = notify_notification_new (notification_summary, NULL, + "preferences-system-time-symbolic"); + g_signal_connect (self->notification, + "closed", + G_CALLBACK (notification_closed_cb), + self); + + notify_notification_add_action (self->notification, + "settings", + _("Settings"), + (NotifyActionCallback) open_settings_cb, + NULL, NULL); + } else { + notify_notification_update (self->notification, + notification_summary, NULL, + "preferences-system-time-symbolic"); + } + g_free (notification_summary); + + notify_notification_set_app_name (self->notification, _("Date & Time Settings")); + notify_notification_set_hint_string (self->notification, "desktop-entry", "gnome-datetime-panel"); + notify_notification_set_urgency (self->notification, NOTIFY_URGENCY_NORMAL); + notify_notification_set_timeout (self->notification, NOTIFY_EXPIRES_NEVER); + + if (!notify_notification_show (self->notification, NULL)) { + g_warning ("Failed to send timezone notification"); + } +} + +static void +auto_timezone_settings_changed_cb (GSettings *settings, + const char *key, + GsdDatetimeManager *self) +{ + gboolean enabled; + + enabled = g_settings_get_boolean (settings, key); + if (enabled && self->timezone_monitor == NULL) { + g_debug ("Automatic timezone enabled"); + self->timezone_monitor = gsd_timezone_monitor_new (); + + g_signal_connect (self->timezone_monitor, "timezone-changed", + G_CALLBACK (timezone_changed_cb), self); + } else { + g_debug ("Automatic timezone disabled"); + g_clear_object (&self->timezone_monitor); + } +} + +gboolean +gsd_datetime_manager_start (GsdDatetimeManager *self, + GError **error) +{ + g_debug ("Starting datetime manager"); + gnome_settings_profile_start (NULL); + + self->settings = g_settings_new (DATETIME_SCHEMA); + + g_signal_connect (self->settings, "changed::" AUTO_TIMEZONE_KEY, + G_CALLBACK (auto_timezone_settings_changed_cb), self); + auto_timezone_settings_changed_cb (self->settings, AUTO_TIMEZONE_KEY, self); + + gnome_settings_profile_end (NULL); + + return TRUE; +} + +void +gsd_datetime_manager_stop (GsdDatetimeManager *self) +{ + g_debug ("Stopping datetime manager"); + + g_clear_object (&self->settings); + g_clear_object (&self->timezone_monitor); + + if (self->notification != NULL) { + g_signal_handlers_disconnect_by_func (self->notification, + G_CALLBACK (notification_closed_cb), + self); + g_clear_object (&self->notification); + } +} + +static void +gsd_datetime_manager_class_init (GsdDatetimeManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsd_datetime_manager_finalize; + + notify_init ("gnome-settings-daemon"); +} + +static void +gsd_datetime_manager_init (GsdDatetimeManager *manager) +{ +} + +static void +gsd_datetime_manager_finalize (GObject *object) +{ + GsdDatetimeManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSD_IS_DATETIME_MANAGER (object)); + + manager = GSD_DATETIME_MANAGER (object); + + g_return_if_fail (manager != NULL); + + gsd_datetime_manager_stop (manager); + + G_OBJECT_CLASS (gsd_datetime_manager_parent_class)->finalize (object); +} + +GsdDatetimeManager * +gsd_datetime_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (GSD_TYPE_DATETIME_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return GSD_DATETIME_MANAGER (manager_object); +} diff --git a/plugins/datetime/gsd-datetime-manager.h b/plugins/datetime/gsd-datetime-manager.h new file mode 100644 index 0000000..5478145 --- /dev/null +++ b/plugins/datetime/gsd-datetime-manager.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Kalev Lember <kalevlember@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 2 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 __GSD_DATETIME_MANAGER_H +#define __GSD_DATETIME_MANAGER_H + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GSD_TYPE_DATETIME_MANAGER (gsd_datetime_manager_get_type ()) +G_DECLARE_FINAL_TYPE (GsdDatetimeManager, gsd_datetime_manager, GSD, DATETIME_MANAGER, GObject) + +GsdDatetimeManager *gsd_datetime_manager_new (void); +gboolean gsd_datetime_manager_start (GsdDatetimeManager *manager, GError **error); +void gsd_datetime_manager_stop (GsdDatetimeManager *manager); + +G_END_DECLS + +#endif /* __GSD_DATETIME_MANAGER_H */ diff --git a/plugins/datetime/gsd-timezone-monitor.c b/plugins/datetime/gsd-timezone-monitor.c new file mode 100644 index 0000000..a6e8a48 --- /dev/null +++ b/plugins/datetime/gsd-timezone-monitor.c @@ -0,0 +1,472 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Kalev Lember <kalevlember@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 2 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 "config.h" + +#include "gsd-timezone-monitor.h" + +#include "timedated.h" +#include "tz.h" +#include "weather-tz.h" + +#include <geoclue.h> +#include <geocode-glib/geocode-glib.h> +#include <polkit/polkit.h> + +#define DESKTOP_ID "gnome-datetime-panel" +#define SET_TIMEZONE_PERMISSION "org.freedesktop.timedate1.set-timezone" + +enum { + TIMEZONE_CHANGED, + LAST_SIGNAL +}; + +static int signals[LAST_SIGNAL] = { 0 }; + +typedef struct +{ + GCancellable *cancellable; + GPermission *permission; + Timedate1 *dtm; + + GClueClient *geoclue_client; + GClueSimple *geoclue_simple; + GCancellable *geoclue_cancellable; + + gchar *current_timezone; + + GSettings *location_settings; +} GsdTimezoneMonitorPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GsdTimezoneMonitor, gsd_timezone_monitor, G_TYPE_OBJECT) + +static void +set_timezone_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GsdTimezoneMonitorPrivate *priv; + GError *error = NULL; + + if (!timedate1_call_set_timezone_finish (TIMEDATE1 (source), + res, + &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Could not set system timezone: %s", error->message); + g_error_free (error); + return; + } + + priv = gsd_timezone_monitor_get_instance_private (user_data); + g_signal_emit (G_OBJECT (user_data), + signals[TIMEZONE_CHANGED], + 0, priv->current_timezone); + + g_debug ("Successfully changed timezone to '%s'", + priv->current_timezone); +} + +static void +queue_set_timezone (GsdTimezoneMonitor *self, + const gchar *new_timezone) +{ + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + + g_debug ("Changing timezone to '%s'", new_timezone); + + timedate1_call_set_timezone (priv->dtm, + new_timezone, + TRUE, + priv->cancellable, + set_timezone_cb, + self); + + g_free (priv->current_timezone); + priv->current_timezone = g_strdup (new_timezone); +} + +static gint +compare_locations (TzLocation *a, + TzLocation *b) +{ + if (a->dist > b->dist) + return 1; + + if (a->dist < b->dist) + return -1; + + return 0; +} + +static GList * +sort_by_closest_to (GList *locations, + GeocodeLocation *location) +{ + GList *l; + + for (l = locations; l; l = l->next) { + GeocodeLocation *loc; + TzLocation *tz_location = l->data; + + loc = geocode_location_new (tz_location->latitude, + tz_location->longitude, + GEOCODE_LOCATION_ACCURACY_UNKNOWN); + tz_location->dist = geocode_location_get_distance_from (loc, location); + g_object_unref (loc); + } + + return g_list_sort (locations, (GCompareFunc) compare_locations); +} + +static GList * +ptr_array_to_list (GPtrArray *array) +{ + GList *l = NULL; + gint i; + + for (i = 0; i < array->len; i++) + l = g_list_prepend (l, g_ptr_array_index (array, i)); + + return l; +} + +static GList * +find_by_country (GList *locations, + const gchar *country_code) +{ + GList *l, *found = NULL; + gchar *c1; + gchar *c2; + + c1 = g_ascii_strdown (country_code, -1); + + for (l = locations; l; l = l->next) { + TzLocation *loc = l->data; + + c2 = g_ascii_strdown (loc->country, -1); + if (g_strcmp0 (c1, c2) == 0) + found = g_list_prepend (found, loc); + g_free (c2); + } + g_free (c1); + + return found; +} + +static gchar * +find_timezone (GsdTimezoneMonitor *self, + GeocodeLocation *location, + const gchar *country_code) +{ + TzDB *tzdb; + gchar *res; + GList *filtered; + GList *weather_locations; + GList *locations; + TzLocation *closest_tz_location; + + tzdb = tz_load_db (); + + /* First load locations from Olson DB */ + locations = ptr_array_to_list (tz_get_locations (tzdb)); + g_return_val_if_fail (locations != NULL, NULL); + + /* ... and then add libgweather's locations as well */ + weather_locations = weather_tz_db_get_locations (country_code); + locations = g_list_concat (locations, + g_list_copy (weather_locations)); + + /* Filter tz locations by country */ + filtered = find_by_country (locations, country_code); + if (filtered != NULL) { + g_list_free (locations); + locations = filtered; + } else { + g_debug ("No match for country code '%s' in tzdb", country_code); + } + + /* Find the closest tz location */ + locations = sort_by_closest_to (locations, location); + closest_tz_location = (TzLocation *) locations->data; + + res = g_strdup (closest_tz_location->zone); + + g_list_free (locations); + g_list_free_full (weather_locations, (GDestroyNotify) tz_location_free); + tz_db_free (tzdb); + + return res; +} + +static void +process_location (GsdTimezoneMonitor *self, + GeocodePlace *place) +{ + GeocodeLocation *location; + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + const gchar *country_code; + g_autofree gchar *new_timezone = NULL; + + country_code = geocode_place_get_country_code (place); + location = geocode_place_get_location (place); + + new_timezone = find_timezone (self, location, country_code); + + if (g_strcmp0 (priv->current_timezone, new_timezone) != 0) { + g_debug ("Found updated timezone '%s' for country '%s'", + new_timezone, country_code); + queue_set_timezone (self, new_timezone); + } else { + g_debug ("Timezone didn't change from '%s' for country '%s'", + new_timezone, country_code); + } +} + +static void +on_reverse_geocoding_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GeocodePlace *place; + GError *error = NULL; + + place = geocode_reverse_resolve_finish (GEOCODE_REVERSE (source_object), + res, + &error); + if (error != NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_debug ("Reverse geocoding failed: %s", error->message); + g_error_free (error); + return; + } + g_debug ("Geocode lookup resolved country to '%s'", + geocode_place_get_country (place)); + + process_location (user_data, place); + g_object_unref (place); +} + +static void +start_reverse_geocoding (GsdTimezoneMonitor *self, + gdouble latitude, + gdouble longitude) +{ + GeocodeLocation *location; + GeocodeReverse *reverse; + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + + location = geocode_location_new (latitude, + longitude, + GEOCODE_LOCATION_ACCURACY_CITY); + + reverse = geocode_reverse_new_for_location (location); + geocode_reverse_resolve_async (reverse, + priv->geoclue_cancellable, + on_reverse_geocoding_ready, + self); + + g_object_unref (location); + g_object_unref (reverse); +} + +static void +on_location_notify (GClueSimple *simple, + GParamSpec *pspec, + gpointer user_data) +{ + GsdTimezoneMonitor *self = user_data; + GClueLocation *location; + gdouble latitude, longitude; + + location = gclue_simple_get_location (simple); + + latitude = gclue_location_get_latitude (location); + longitude = gclue_location_get_longitude (location); + + g_debug ("Got location %lf,%lf", latitude, longitude); + + start_reverse_geocoding (self, latitude, longitude); +} + +static void +on_geoclue_simple_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + GsdTimezoneMonitorPrivate *priv; + GClueSimple *geoclue_simple; + + geoclue_simple = gclue_simple_new_finish (res, &error); + if (geoclue_simple == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to connect to GeoClue2 service: %s", error->message); + g_error_free (error); + return; + } + + g_debug ("Geoclue now available"); + + priv = gsd_timezone_monitor_get_instance_private (user_data); + priv->geoclue_simple = geoclue_simple; + priv->geoclue_client = gclue_simple_get_client (priv->geoclue_simple); + gclue_client_set_distance_threshold (priv->geoclue_client, + GEOCODE_LOCATION_ACCURACY_CITY); + + g_signal_connect (priv->geoclue_simple, "notify::location", + G_CALLBACK (on_location_notify), user_data); + + on_location_notify (priv->geoclue_simple, NULL, user_data); +} + +static void +start_geoclue (GsdTimezoneMonitor *self) +{ + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + + g_debug ("Timezone monitor enabled, starting geoclue"); + + priv->geoclue_cancellable = g_cancellable_new (); + gclue_simple_new (DESKTOP_ID, + GCLUE_ACCURACY_LEVEL_CITY, + priv->geoclue_cancellable, + on_geoclue_simple_ready, + self); + +} + +static void +stop_geoclue (GsdTimezoneMonitor *self) +{ + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + + g_debug ("Timezone monitor disabled, stopping geoclue"); + + g_cancellable_cancel (priv->geoclue_cancellable); + g_clear_object (&priv->geoclue_cancellable); + + if (priv->geoclue_client) { + gclue_client_call_stop (priv->geoclue_client, NULL, NULL, NULL); + priv->geoclue_client = NULL; + } + + g_clear_object (&priv->geoclue_simple); +} + +GsdTimezoneMonitor * +gsd_timezone_monitor_new (void) +{ + return g_object_new (GSD_TYPE_TIMEZONE_MONITOR, NULL); +} + +static void +gsd_timezone_monitor_finalize (GObject *obj) +{ + GsdTimezoneMonitor *monitor = GSD_TIMEZONE_MONITOR (obj); + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (monitor); + + g_debug ("Stopping timezone monitor"); + + stop_geoclue (monitor); + + if (priv->cancellable) { + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + } + + g_clear_object (&priv->dtm); + g_clear_object (&priv->permission); + g_clear_pointer (&priv->current_timezone, g_free); + + g_clear_object (&priv->location_settings); + + G_OBJECT_CLASS (gsd_timezone_monitor_parent_class)->finalize (obj); +} + +static void +gsd_timezone_monitor_class_init (GsdTimezoneMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsd_timezone_monitor_finalize; + + signals[TIMEZONE_CHANGED] = + g_signal_new ("timezone-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdTimezoneMonitorClass, timezone_changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + +static void +check_location_settings (GsdTimezoneMonitor *self) +{ + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + if (g_settings_get_boolean (priv->location_settings, "enabled")) + start_geoclue (self); + else + stop_geoclue (self); +} + +static void +gsd_timezone_monitor_init (GsdTimezoneMonitor *self) +{ + GError *error = NULL; + GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self); + + g_debug ("Starting timezone monitor"); + + priv->permission = polkit_permission_new_sync (SET_TIMEZONE_PERMISSION, + NULL, NULL, + &error); + if (priv->permission == NULL) { + g_warning ("Could not get '%s' permission: %s", + SET_TIMEZONE_PERMISSION, + error->message); + g_error_free (error); + return; + } + + if (!g_permission_get_allowed (priv->permission)) { + g_debug ("No permission to set timezone"); + return; + } + + priv->cancellable = g_cancellable_new (); + priv->dtm = timedate1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + priv->cancellable, + &error); + if (priv->dtm == NULL) { + g_warning ("Could not get proxy for DateTimeMechanism: %s", error->message); + g_error_free (error); + return; + } + + priv->current_timezone = timedate1_dup_timezone (priv->dtm); + + priv->location_settings = g_settings_new ("org.gnome.system.location"); + g_signal_connect_swapped (priv->location_settings, "changed::enabled", + G_CALLBACK (check_location_settings), self); + check_location_settings (self); +} diff --git a/plugins/datetime/gsd-timezone-monitor.h b/plugins/datetime/gsd-timezone-monitor.h new file mode 100644 index 0000000..da2bcf8 --- /dev/null +++ b/plugins/datetime/gsd-timezone-monitor.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Kalev Lember <kalevlember@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 2 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 __GSD_TIMEZONE_MONITOR_H +#define __GSD_TIMEZONE_MONITOR_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GSD_TYPE_TIMEZONE_MONITOR (gsd_timezone_monitor_get_type ()) +#define GSD_TIMEZONE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_TIMEZONE_MONITOR, GsdTimezoneMonitor)) +#define GSD_IS_TIMEZONE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_TIMEZONE_MONITOR)) +#define GSD_TIMEZONE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_TIMEZONE_MONITOR, GsdTimezoneMonitorClass)) +#define GSD_IS_TIMEZONE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_TIMEZONE_MONITOR)) +#define GSD_TIMEZONE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSD_TYPE_TIMEZONE_MONITOR, GsdTimezoneMonitorClass)) + +typedef struct _GsdTimezoneMonitor GsdTimezoneMonitor; +typedef struct _GsdTimezoneMonitorClass GsdTimezoneMonitorClass; + +struct _GsdTimezoneMonitor +{ + GObject parent_instance; +}; + +struct _GsdTimezoneMonitorClass +{ + GObjectClass parent_class; + + void (*timezone_changed) (GsdTimezoneMonitor *monitor, gchar *timezone_id); +}; + +GType gsd_timezone_monitor_get_type (void) G_GNUC_CONST; + +GsdTimezoneMonitor *gsd_timezone_monitor_new (void); + +G_END_DECLS + +#endif /* __GSD_TIMEZONE_MONITOR_H */ diff --git a/plugins/datetime/main.c b/plugins/datetime/main.c new file mode 100644 index 0000000..8950ab0 --- /dev/null +++ b/plugins/datetime/main.c @@ -0,0 +1,7 @@ +#define NEW gsd_datetime_manager_new +#define START gsd_datetime_manager_start +#define STOP gsd_datetime_manager_stop +#define MANAGER GsdDatetimeManager +#include "gsd-datetime-manager.h" + +#include "daemon-skeleton.h" diff --git a/plugins/datetime/meson.build b/plugins/datetime/meson.build new file mode 100644 index 0000000..ed2d433 --- /dev/null +++ b/plugins/datetime/meson.build @@ -0,0 +1,40 @@ +install_data( + 'backward', + install_dir: join_paths(gsd_pkgdatadir, 'datetime') +) + +sources = files( + 'gsd-datetime-manager.c', + 'gsd-timezone-monitor.c', + 'main.c', + 'tz.c', + 'weather-tz.c' +) + +sources += gnome.gdbus_codegen( + 'timedated', + 'timedated1-interface.xml', + interface_prefix: 'org.freedesktop.' +) + +deps = plugins_deps + [ + geocode_glib_dep, + gweather_dep, + libgeoclue_dep, + libnotify_dep, + m_dep, + polkit_gobject_dep +] + +cflags += ['-DGNOMECC_DATA_DIR="@0@"'.format(gsd_pkgdatadir)] + +executable( + 'gsd-' + plugin_name, + sources, + include_directories: [top_inc, common_inc], + dependencies: deps, + c_args: cflags, + install: true, + install_rpath: gsd_pkglibdir, + install_dir: gsd_libexecdir +) diff --git a/plugins/datetime/timedated1-interface.xml b/plugins/datetime/timedated1-interface.xml new file mode 100644 index 0000000..3370e0e --- /dev/null +++ b/plugins/datetime/timedated1-interface.xml @@ -0,0 +1,28 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.freedesktop.timedate1"> + <property name="Timezone" type="s" access="read"/> + <property name="LocalRTC" type="b" access="read"/> + <property name="CanNTP" type="b" access="read"/> + <property name="NTP" type="b" access="read"/> + <method name="SetTime"> + <arg name="usec_utc" type="x" direction="in"/> + <arg name="relative" type="b" direction="in"/> + <arg name="user_interaction" type="b" direction="in"/> + </method> + <method name="SetTimezone"> + <arg name="timezone" type="s" direction="in"/> + <arg name="user_interaction" type="b" direction="in"/> + </method> + <method name="SetLocalRTC"> + <arg name="local_rtc" type="b" direction="in"/> + <arg name="fix_system" type="b" direction="in"/> + <arg name="user_interaction" type="b" direction="in"/> + </method> + <method name="SetNTP"> + <arg name="use_ntp" type="b" direction="in"/> + <arg name="user_interaction" type="b" direction="in"/> + </method> + </interface> +</node> diff --git a/plugins/datetime/tz.c b/plugins/datetime/tz.c new file mode 100644 index 0000000..034d63d --- /dev/null +++ b/plugins/datetime/tz.c @@ -0,0 +1,482 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Generic timezone utilities. + * + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Authors: Hans Petter Jansson <hpj@ximian.com> + * + * Largely based on Michael Fulbright's work on Anaconda. + * + * 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 2 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 <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <math.h> +#include <string.h> +#include <ctype.h> +#include "tz.h" + + +/* Forward declarations for private functions */ + +static float convert_pos (gchar *pos, int digits); +static int compare_country_names (const void *a, const void *b); +static void sort_locations_by_country (GPtrArray *locations); +static gchar * tz_data_file_get (void); +static void load_backward_tz (TzDB *tz_db); + +/* ---------------- * + * Public interface * + * ---------------- */ +TzDB * +tz_load_db (void) +{ + gchar *tz_data_file; + TzDB *tz_db; + FILE *tzfile; + char buf[4096]; + + tz_data_file = tz_data_file_get (); + if (!tz_data_file) { + g_warning ("Could not get the TimeZone data file name"); + return NULL; + } + tzfile = fopen (tz_data_file, "r"); + if (!tzfile) { + g_warning ("Could not open *%s*\n", tz_data_file); + g_free (tz_data_file); + return NULL; + } + + tz_db = g_new0 (TzDB, 1); + tz_db->locations = g_ptr_array_new (); + + while (fgets (buf, sizeof(buf), tzfile)) + { + gchar **tmpstrarr; + gchar *latstr, *lngstr, *p; + TzLocation *loc; + + if (*buf == '#') continue; + + g_strchomp(buf); + tmpstrarr = g_strsplit(buf,"\t", 6); + + latstr = g_strdup (tmpstrarr[1]); + p = latstr + 1; + while (*p != '-' && *p != '+') p++; + lngstr = g_strdup (p); + *p = '\0'; + + loc = g_new0 (TzLocation, 1); + loc->country = g_strdup (tmpstrarr[0]); + loc->zone = g_strdup (tmpstrarr[2]); + loc->latitude = convert_pos (latstr, 2); + loc->longitude = convert_pos (lngstr, 3); + +#ifdef __sun + if (tmpstrarr[3] && *tmpstrarr[3] == '-' && tmpstrarr[4]) + loc->comment = g_strdup (tmpstrarr[4]); + + if (tmpstrarr[3] && *tmpstrarr[3] != '-' && !islower(loc->zone)) { + TzLocation *locgrp; + + /* duplicate entry */ + locgrp = g_new0 (TzLocation, 1); + locgrp->country = g_strdup (tmpstrarr[0]); + locgrp->zone = g_strdup (tmpstrarr[3]); + locgrp->latitude = convert_pos (latstr, 2); + locgrp->longitude = convert_pos (lngstr, 3); + locgrp->comment = (tmpstrarr[4]) ? g_strdup (tmpstrarr[4]) : NULL; + + g_ptr_array_add (tz_db->locations, (gpointer) locgrp); + } +#else + loc->comment = (tmpstrarr[3]) ? g_strdup(tmpstrarr[3]) : NULL; +#endif + + g_ptr_array_add (tz_db->locations, (gpointer) loc); + + g_free (latstr); + g_free (lngstr); + g_strfreev (tmpstrarr); + } + + fclose (tzfile); + + /* now sort by country */ + sort_locations_by_country (tz_db->locations); + + g_free (tz_data_file); + + /* Load up the hashtable of backward links */ + load_backward_tz (tz_db); + + return tz_db; +} + +void +tz_location_free (TzLocation *loc) +{ + g_free (loc->country); + g_free (loc->zone); + g_free (loc->comment); + + g_free (loc); +} + +void +tz_db_free (TzDB *db) +{ + g_ptr_array_foreach (db->locations, (GFunc) tz_location_free, NULL); + g_ptr_array_free (db->locations, TRUE); + g_hash_table_destroy (db->backward); + g_free (db); +} + +GPtrArray * +tz_get_locations (TzDB *db) +{ + return db->locations; +} + + +gchar * +tz_location_get_country (TzLocation *loc) +{ + return loc->country; +} + + +gchar * +tz_location_get_zone (TzLocation *loc) +{ + return loc->zone; +} + + +gchar * +tz_location_get_comment (TzLocation *loc) +{ + return loc->comment; +} + + +void +tz_location_get_position (TzLocation *loc, double *longitude, double *latitude) +{ + *longitude = loc->longitude; + *latitude = loc->latitude; +} + +glong +tz_location_get_utc_offset (TzLocation *loc) +{ + TzInfo *tz_info; + glong offset; + + tz_info = tz_info_from_location (loc); + offset = tz_info->utc_offset; + tz_info_free (tz_info); + return offset; +} + +TzInfo * +tz_info_from_location (TzLocation *loc) +{ + TzInfo *tzinfo; + time_t curtime; + struct tm *curzone; + gchar *tz_env_value; + + g_return_val_if_fail (loc != NULL, NULL); + g_return_val_if_fail (loc->zone != NULL, NULL); + + tz_env_value = g_strdup (getenv ("TZ")); + setenv ("TZ", loc->zone, 1); + +#if 0 + tzset (); +#endif + tzinfo = g_new0 (TzInfo, 1); + + curtime = time (NULL); + curzone = localtime (&curtime); + +#ifndef __sun + /* Currently this solution doesnt seem to work - I get that */ + /* America/Phoenix uses daylight savings, which is wrong */ + tzinfo->tzname_normal = g_strdup (curzone->tm_zone); + if (curzone->tm_isdst) + tzinfo->tzname_daylight = + g_strdup (&curzone->tm_zone[curzone->tm_isdst]); + else + tzinfo->tzname_daylight = NULL; + + tzinfo->utc_offset = curzone->tm_gmtoff; +#else + tzinfo->tzname_normal = NULL; + tzinfo->tzname_daylight = NULL; + tzinfo->utc_offset = 0; +#endif + + tzinfo->daylight = curzone->tm_isdst; + + if (tz_env_value) + setenv ("TZ", tz_env_value, 1); + else + unsetenv ("TZ"); + + g_free (tz_env_value); + + return tzinfo; +} + + +void +tz_info_free (TzInfo *tzinfo) +{ + g_return_if_fail (tzinfo != NULL); + + if (tzinfo->tzname_normal) g_free (tzinfo->tzname_normal); + if (tzinfo->tzname_daylight) g_free (tzinfo->tzname_daylight); + g_free (tzinfo); +} + +struct { + const char *orig; + const char *dest; +} aliases[] = { + { "Asia/Istanbul", "Europe/Istanbul" }, /* Istanbul is in both Europe and Asia */ + { "Europe/Nicosia", "Asia/Nicosia" }, /* Ditto */ + { "EET", "Europe/Istanbul" }, /* Same tz as the 2 above */ + { "HST", "Pacific/Honolulu" }, + { "WET", "Europe/Brussels" }, /* Other name for the mainland Europe tz */ + { "CET", "Europe/Brussels" }, /* ditto */ + { "MET", "Europe/Brussels" }, + { "Etc/Zulu", "Etc/GMT" }, + { "Etc/UTC", "Etc/GMT" }, + { "GMT", "Etc/GMT" }, + { "Greenwich", "Etc/GMT" }, + { "Etc/UCT", "Etc/GMT" }, + { "Etc/GMT0", "Etc/GMT" }, + { "Etc/GMT+0", "Etc/GMT" }, + { "Etc/GMT-0", "Etc/GMT" }, + { "Etc/Universal", "Etc/GMT" }, + { "PST8PDT", "America/Los_Angeles" }, /* Other name for the Atlantic tz */ + { "EST", "America/New_York" }, /* Other name for the Eastern tz */ + { "EST5EDT", "America/New_York" }, /* ditto */ + { "CST6CDT", "America/Chicago" }, /* Other name for the Central tz */ + { "MST", "America/Denver" }, /* Other name for the mountain tz */ + { "MST7MDT", "America/Denver" }, /* ditto */ +}; + +static gboolean +compare_timezones (const char *a, + const char *b) +{ + if (g_str_equal (a, b)) + return TRUE; + if (strchr (b, '/') == NULL) { + char *prefixed; + + prefixed = g_strdup_printf ("/%s", b); + if (g_str_has_suffix (a, prefixed)) { + g_free (prefixed); + return TRUE; + } + g_free (prefixed); + } + + return FALSE; +} + +char * +tz_info_get_clean_name (TzDB *tz_db, + const char *tz) +{ + char *ret; + const char *timezone; + guint i; + gboolean replaced; + + /* Remove useless prefixes */ + if (g_str_has_prefix (tz, "right/")) + tz = tz + strlen ("right/"); + else if (g_str_has_prefix (tz, "posix/")) + tz = tz + strlen ("posix/"); + + /* Here start the crazies */ + replaced = FALSE; + + for (i = 0; i < G_N_ELEMENTS (aliases); i++) { + if (compare_timezones (tz, aliases[i].orig)) { + replaced = TRUE; + timezone = aliases[i].dest; + break; + } + } + + /* Try again! */ + if (!replaced) { + /* Ignore crazy solar times from the '80s */ + if (g_str_has_prefix (tz, "Asia/Riyadh") || + g_str_has_prefix (tz, "Mideast/Riyadh")) { + timezone = "Asia/Riyadh"; + replaced = TRUE; + } + } + + if (!replaced) + timezone = tz; + + ret = g_hash_table_lookup (tz_db->backward, timezone); + if (ret == NULL) + return g_strdup (timezone); + return g_strdup (ret); +} + +/* ----------------- * + * Private functions * + * ----------------- */ + +static gchar * +tz_data_file_get (void) +{ + gchar *file; + + file = g_strdup (TZ_DATA_FILE); + + return file; +} + +static float +convert_pos (gchar *pos, int digits) +{ + gchar whole[10]; + gchar *fraction; + gint i; + float t1, t2; + + if (!pos || strlen(pos) < 4 || digits > 9) return 0.0; + + for (i = 0; i < digits + 1; i++) whole[i] = pos[i]; + whole[i] = '\0'; + fraction = pos + digits + 1; + + t1 = g_strtod (whole, NULL); + t2 = g_strtod (fraction, NULL); + + if (t1 >= 0.0) return t1 + t2/pow (10.0, strlen(fraction)); + else return t1 - t2/pow (10.0, strlen(fraction)); +} + + +#if 0 + +/* Currently not working */ +static void +free_tzdata (TzLocation *tz) +{ + + if (tz->country) + g_free(tz->country); + if (tz->zone) + g_free(tz->zone); + if (tz->comment) + g_free(tz->comment); + + g_free(tz); +} +#endif + + +static int +compare_country_names (const void *a, const void *b) +{ + const TzLocation *tza = * (TzLocation **) a; + const TzLocation *tzb = * (TzLocation **) b; + + return strcmp (tza->zone, tzb->zone); +} + + +static void +sort_locations_by_country (GPtrArray *locations) +{ + qsort (locations->pdata, locations->len, sizeof (gpointer), + compare_country_names); +} + +static void +load_backward_tz (TzDB *tz_db) +{ + GError *error = NULL; + char **lines, *contents; + guint i; + + tz_db->backward = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + if (g_file_get_contents (GNOMECC_DATA_DIR "/datetime/backward", &contents, NULL, &error) == FALSE) + { + g_warning ("Failed to load 'backward' file: %s", error->message); + return; + } + lines = g_strsplit (contents, "\n", -1); + g_free (contents); + for (i = 0; lines[i] != NULL; i++) + { + char **items; + guint j; + char *real, *alias; + + if (g_ascii_strncasecmp (lines[i], "Link\t", 5) != 0) + continue; + + items = g_strsplit (lines[i], "\t", -1); + real = NULL; + alias = NULL; + /* Skip the "Link<tab>" part */ + for (j = 1; items[j] != NULL; j++) + { + if (items[j][0] == '\0') + continue; + if (real == NULL) + { + real = items[j]; + continue; + } + alias = items[j]; + break; + } + + if (real == NULL || alias == NULL) + g_warning ("Could not parse line: %s", lines[i]); + + /* We don't need more than one name for it */ + if (g_str_equal (real, "Etc/UTC") || + g_str_equal (real, "Etc/UCT")) + real = "Etc/GMT"; + + g_hash_table_insert (tz_db->backward, g_strdup (alias), g_strdup (real)); + g_strfreev (items); + } + g_strfreev (lines); +} + diff --git a/plugins/datetime/tz.h b/plugins/datetime/tz.h new file mode 100644 index 0000000..ab5535c --- /dev/null +++ b/plugins/datetime/tz.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Generic timezone utilities. + * + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Authors: Hans Petter Jansson <hpj@ximian.com> + * + * Largely based on Michael Fulbright's work on Anaconda. + * + * 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 2 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 _E_TZ_H +#define _E_TZ_H + +#include <glib.h> + +#ifndef __sun +# define TZ_DATA_FILE "/usr/share/zoneinfo/zone.tab" +#else +# define TZ_DATA_FILE "/usr/share/lib/zoneinfo/tab/zone_sun.tab" +#endif + +typedef struct _TzDB TzDB; +typedef struct _TzLocation TzLocation; +typedef struct _TzInfo TzInfo; + + +struct _TzDB +{ + GPtrArray *locations; + GHashTable *backward; +}; + +struct _TzLocation +{ + gchar *country; + gdouble latitude; + gdouble longitude; + gchar *zone; + gchar *comment; + + gdouble dist; /* distance to clicked point for comparison */ +}; + +/* see the glibc info page information on time zone information */ +/* tzname_normal is the default name for the timezone */ +/* tzname_daylight is the name of the zone when in daylight savings */ +/* utc_offset is offset in seconds from utc */ +/* daylight if non-zero then location obeys daylight savings */ + +struct _TzInfo +{ + gchar *tzname_normal; + gchar *tzname_daylight; + glong utc_offset; + gint daylight; +}; + + +TzDB *tz_load_db (void); +void tz_db_free (TzDB *db); +char * tz_info_get_clean_name (TzDB *tz_db, + const char *tz); +GPtrArray *tz_get_locations (TzDB *db); +void tz_location_get_position (TzLocation *loc, + double *longitude, double *latitude); +void tz_location_free (TzLocation *loc); +char *tz_location_get_country (TzLocation *loc); +gchar *tz_location_get_zone (TzLocation *loc); +gchar *tz_location_get_comment (TzLocation *loc); +glong tz_location_get_utc_offset (TzLocation *loc); +gint tz_location_set_locally (TzLocation *loc); +TzInfo *tz_info_from_location (TzLocation *loc); +void tz_info_free (TzInfo *tz_info); + +#endif diff --git a/plugins/datetime/weather-tz.c b/plugins/datetime/weather-tz.c new file mode 100644 index 0000000..f2d38d9 --- /dev/null +++ b/plugins/datetime/weather-tz.c @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Kalev Lember <kalevlember@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 2 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 "config.h" + +#include "weather-tz.h" +#include "tz.h" + +#include <libgweather/gweather.h> + +static GList * +location_get_cities (GWeatherLocation *parent_location) +{ + GList *cities = NULL; + GWeatherLocation *child = NULL; + + while ((child = gweather_location_next_child (parent_location, child))) { + if (gweather_location_get_level (child) == GWEATHER_LOCATION_CITY) { + cities = g_list_prepend (cities, g_object_ref (child)); + } else { + cities = g_list_concat (cities, + location_get_cities (child)); + } + } + + return cities; +} + +static gboolean +weather_location_has_timezone (GWeatherLocation *loc) +{ + return gweather_location_get_timezone (loc) != NULL; +} + +/** + * load_timezones: + * @cities: a list of #GWeatherLocation + * + * Returns: a list of #TzLocation + */ +static GList * +load_timezones (GList *cities) +{ + GList *l; + GList *tz_locations = NULL; + + for (l = cities; l; l = l->next) { + TzLocation *loc; + const gchar *country; + const gchar *timezone_id; + GTimeZone *tz; + gdouble latitude; + gdouble longitude; + + if (!gweather_location_has_coords (l->data) || + !weather_location_has_timezone (l->data)) { + g_debug ("Incomplete GWeather location entry: (%s) %s", + gweather_location_get_country (l->data), + gweather_location_get_city_name (l->data)); + continue; + } + + country = gweather_location_get_country (l->data); + tz = gweather_location_get_timezone (l->data); + timezone_id = g_time_zone_get_identifier (tz); + gweather_location_get_coords (l->data, + &latitude, + &longitude); + + loc = g_new0 (TzLocation, 1); + loc->country = g_strdup (country); + loc->latitude = latitude; + loc->longitude = longitude; + loc->zone = g_strdup (timezone_id); + loc->comment = NULL; + + tz_locations = g_list_prepend (tz_locations, loc); + } + + return tz_locations; +} + +GList * +weather_tz_db_get_locations (const gchar *country_code) +{ + g_autoptr(GWeatherLocation) world = NULL; + g_autoptr(GWeatherLocation) country = NULL; + g_autolist(GWeatherLocation) cities = NULL; + GList *tz_locations; + + world = gweather_location_get_world (); + + country = gweather_location_find_by_country_code (world, country_code); + + if (!country) + return NULL; + + cities = location_get_cities (country); + tz_locations = load_timezones (cities); + + return tz_locations; +} diff --git a/plugins/datetime/weather-tz.h b/plugins/datetime/weather-tz.h new file mode 100644 index 0000000..15b1571 --- /dev/null +++ b/plugins/datetime/weather-tz.h @@ -0,0 +1,27 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 Kalev Lember <kalevlember@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 2 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 __WEATHER_TZ_H +#define __WEATHER_TZ_H + +#include <glib.h> + +GList *weather_tz_db_get_locations (const char *country); + +#endif /* __WEATHER_TZ_H */ |