summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--configure.ac9
-rw-r--r--data/Makefile.am1
-rw-r--r--data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in.in14
-rw-r--r--data/org.gnome.settings-daemon.plugins.gschema.xml.in.in1
-rw-r--r--plugins/Makefile.am1
-rw-r--r--plugins/datetime/Makefile.am104
-rw-r--r--plugins/datetime/backward118
-rw-r--r--plugins/datetime/datetime.gnome-settings-plugin.in10
-rw-r--r--plugins/datetime/geoclue-interface.xml25
-rw-r--r--plugins/datetime/gsd-datetime-manager.c152
-rw-r--r--plugins/datetime/gsd-datetime-manager.h57
-rw-r--r--plugins/datetime/gsd-datetime-plugin.c29
-rw-r--r--plugins/datetime/gsd-timezone-monitor.c355
-rw-r--r--plugins/datetime/gsd-timezone-monitor.h54
-rw-r--r--plugins/datetime/test-datetime.c7
-rw-r--r--plugins/datetime/timedated1-interface.xml28
-rw-r--r--plugins/datetime/tz.c482
-rw-r--r--plugins/datetime/tz.h89
-rw-r--r--po/POTFILES.in1
-rw-r--r--po/POTFILES.skip1
21 files changed, 1543 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 13ba5691..c0d804eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,11 @@ plugins/background/test-background
plugins/common/gsd-test-input-helper
plugins/common/test-egg-key-parsing
plugins/cursor/gsd-test-cursor
+plugins/datetime/gsd-test-datetime
+plugins/datetime/geoclue.c
+plugins/datetime/geoclue.h
+plugins/datetime/timedated.c
+plugins/datetime/timedated.h
plugins/housekeeping/gsd-disk-space-test
plugins/housekeeping/gsd-empty-trash-test
plugins/housekeeping/gsd-test-housekeeping
diff --git a/configure.ac b/configure.ac
index 41b748b4..47710ef9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -245,6 +245,13 @@ dnl ---------------------------------------------------------------------------
PKG_CHECK_MODULES(COLOR, [colord >= 1.0.2 gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION libcanberra-gtk3])
dnl ---------------------------------------------------------------------------
+dnl - datetime
+dnl ---------------------------------------------------------------------------
+
+AC_CHECK_LIBM
+AC_SUBST(LIBM)
+
+dnl ---------------------------------------------------------------------------
dnl - wacom (disabled for s390/s390x and non Linux platforms)
dnl ---------------------------------------------------------------------------
@@ -508,6 +515,7 @@ plugins/clipboard/Makefile
plugins/color/Makefile
plugins/common/Makefile
plugins/cursor/Makefile
+plugins/datetime/Makefile
plugins/dummy/Makefile
plugins/power/Makefile
plugins/housekeeping/Makefile
@@ -534,6 +542,7 @@ data/org.gnome.settings-daemon.plugins.xsettings.gschema.xml.in
data/org.gnome.settings-daemon.plugins.keyboard.gschema.xml.in
data/org.gnome.settings-daemon.plugins.power.gschema.xml.in
data/org.gnome.settings-daemon.plugins.color.gschema.xml.in
+data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in
data/org.gnome.settings-daemon.plugins.media-keys.gschema.xml.in
data/org.gnome.settings-daemon.peripherals.gschema.xml.in
data/org.gnome.settings-daemon.plugins.housekeeping.gschema.xml.in
diff --git a/data/Makefile.am b/data/Makefile.am
index ecfe00a9..c1522db0 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -12,6 +12,7 @@ gsettings_SCHEMAS = \
org.gnome.settings-daemon.plugins.keyboard.gschema.xml \
org.gnome.settings-daemon.plugins.power.gschema.xml \
org.gnome.settings-daemon.plugins.color.gschema.xml \
+ org.gnome.settings-daemon.plugins.datetime.gschema.xml \
org.gnome.settings-daemon.plugins.media-keys.gschema.xml \
org.gnome.settings-daemon.plugins.xsettings.gschema.xml \
org.gnome.settings-daemon.plugins.housekeeping.gschema.xml \
diff --git a/data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in.in b/data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in.in
new file mode 100644
index 00000000..eacc4a0d
--- /dev/null
+++ b/data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in.in
@@ -0,0 +1,14 @@
+<schemalist>
+ <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.plugins.datetime" path="/org/gnome/settings-daemon/plugins/datetime/">
+ <key name="active" type="b">
+ <default>true</default>
+ <_summary>Activation of this plugin</_summary>
+ <_description>Whether this plugin would be activated by gnome-settings-daemon or not</_description>
+ </key>
+ <key name="priority" type="i">
+ <default>0</default>
+ <_summary>Priority to use for this plugin</_summary>
+ <_description>Priority to use for this plugin in gnome-settings-daemon startup queue</_description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in b/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
index bea64f3e..57a058db 100644
--- a/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
+++ b/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
@@ -14,6 +14,7 @@
<child name="clipboard" schema="org.gnome.settings-daemon.plugins.clipboard"/>
<child name="color" schema="org.gnome.settings-daemon.plugins.color"/>
<child name="cursor" schema="org.gnome.settings-daemon.plugins.cursor"/>
+ <child name="datetime" schema="org.gnome.settings-daemon.plugins.datetime"/>
<child name="gsdwacom" schema="org.gnome.settings-daemon.plugins.gsdwacom"/>
<child name="housekeeping" schema="org.gnome.settings-daemon.plugins.housekeeping"/>
<child name="keyboard" schema="org.gnome.settings-daemon.plugins.keyboard"/>
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 787fd141..2f4840de 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -6,6 +6,7 @@ enabled_plugins = \
clipboard \
color \
cursor \
+ datetime \
dummy \
power \
housekeeping \
diff --git a/plugins/datetime/Makefile.am b/plugins/datetime/Makefile.am
new file mode 100644
index 00000000..f8cdbe2d
--- /dev/null
+++ b/plugins/datetime/Makefile.am
@@ -0,0 +1,104 @@
+plugin_name = datetime
+
+geoclue.c: geoclue.h
+geoclue.h: Makefile.am geoclue-interface.xml
+ gdbus-codegen \
+ --interface-prefix org.freedesktop.GeoClue2 \
+ --generate-c-code geoclue \
+ --c-namespace Geoclue \
+ $(srcdir)/geoclue-interface.xml
+
+timedated.c: timedated.h
+timedated.h: Makefile.am timedated1-interface.xml
+ gdbus-codegen \
+ --interface-prefix org.freedesktop. \
+ --generate-c-code timedated \
+ $(srcdir)/timedated1-interface.xml
+
+BUILT_SOURCES = \
+ geoclue.c \
+ geoclue.h \
+ timedated.c \
+ timedated.h
+
+tzdatadir = $(pkgdatadir)/datetime
+dist_tzdata_DATA = backward
+
+libexec_PROGRAMS = gsd-test-datetime
+
+gsd_test_datetime_SOURCES = \
+ $(BUILT_SOURCES) \
+ gsd-datetime-manager.c \
+ gsd-datetime-manager.h \
+ gsd-timezone-monitor.c \
+ gsd-timezone-monitor.h \
+ test-datetime.c \
+ tz.c \
+ tz.h
+
+gsd_test_datetime_CFLAGS = \
+ -I$(top_srcdir)/gnome-settings-daemon \
+ -I$(top_builddir)/gnome-settings-daemon \
+ -I$(top_srcdir)/plugins/common \
+ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \
+ -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
+ $(DATETIME_CFLAGS) \
+ $(PLUGIN_CFLAGS) \
+ $(SETTINGS_PLUGIN_CFLAGS) \
+ $(AM_CFLAGS)
+
+gsd_test_datetime_LDADD = \
+ $(top_builddir)/gnome-settings-daemon/libgsd.la \
+ $(top_builddir)/plugins/common/libcommon.la \
+ $(LIBM) \
+ $(SETTINGS_PLUGIN_LIBS)
+
+plugin_LTLIBRARIES = \
+ libdatetime.la
+
+libdatetime_la_SOURCES = \
+ $(BUILT_SOURCES) \
+ gsd-datetime-plugin.c \
+ gsd-datetime-manager.h \
+ gsd-datetime-manager.c \
+ gsd-timezone-monitor.h \
+ gsd-timezone-monitor.c \
+ tz.h \
+ tz.c
+
+libdatetime_la_CPPFLAGS = \
+ -I$(top_srcdir)/gnome-settings-daemon \
+ -I$(top_builddir)/gnome-settings-daemon \
+ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \
+ -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
+ $(AM_CPPFLAGS)
+
+libdatetime_la_CFLAGS = \
+ $(PLUGIN_CFLAGS) \
+ $(SETTINGS_PLUGIN_CFLAGS) \
+ $(AM_CFLAGS)
+
+libdatetime_la_LDFLAGS = \
+ $(GSD_PLUGIN_LDFLAGS)
+
+libdatetime_la_LIBADD = \
+ $(LIBM) \
+ $(SETTINGS_PLUGIN_LIBS)
+
+plugin_in_files = \
+ datetime.gnome-settings-plugin.in
+
+plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin)
+
+EXTRA_DIST = \
+ geoclue-interface.xml \
+ timedated1-interface.xml \
+ $(plugin_in_files)
+
+CLEANFILES = \
+ $(plugin_DATA)
+
+DISTCLEANFILES = \
+ $(plugin_DATA)
+
+@GSD_INTLTOOL_PLUGIN_RULE@
diff --git a/plugins/datetime/backward b/plugins/datetime/backward
new file mode 100644
index 00000000..f1f95a89
--- /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/datetime.gnome-settings-plugin.in b/plugins/datetime/datetime.gnome-settings-plugin.in
new file mode 100644
index 00000000..f7b8b5a1
--- /dev/null
+++ b/plugins/datetime/datetime.gnome-settings-plugin.in
@@ -0,0 +1,10 @@
+[GNOME Settings Plugin]
+Module=datetime
+IAge=0
+# Default Priority
+# Priority=100
+_Name=Date and Time
+_Description=Automatically update timezone and display notifications
+Authors=Kalev Lember
+Copyright=Copyright © 2013
+Website=
diff --git a/plugins/datetime/geoclue-interface.xml b/plugins/datetime/geoclue-interface.xml
new file mode 100644
index 00000000..3bf956b3
--- /dev/null
+++ b/plugins/datetime/geoclue-interface.xml
@@ -0,0 +1,25 @@
+<!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.GeoClue2.Manager">
+ <method name="GetClient">
+ <arg name="client" type="o" direction="out"/>
+ </method>
+ </interface>
+ <interface name="org.freedesktop.GeoClue2.Client">
+ <property name="Location" type="o" access="read"/>
+ <property name="DistanceThreshold" type="u" access="readwrite"/>
+ <method name="Start"/>
+ <method name="Stop"/>
+ <signal name="LocationUpdated">
+ <arg name="old" type="o"/>
+ <arg name="new" type="o"/>
+ </signal>
+ </interface>
+ <interface name="org.freedesktop.GeoClue2.Location">
+ <property name="latitude" type="d" access="read"/>
+ <property name="longitude" type="d" access="read"/>
+ <property name="accuracy" type="d" access="read"/>
+ <property name="description" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/plugins/datetime/gsd-datetime-manager.c b/plugins/datetime/gsd-datetime-manager.c
new file mode 100644
index 00000000..e08203b1
--- /dev/null
+++ b/plugins/datetime/gsd-datetime-manager.c
@@ -0,0 +1,152 @@
+/* -*- 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gsd-datetime-manager.h"
+#include "gsd-timezone-monitor.h"
+#include "gnome-settings-profile.h"
+
+#define GSD_DATETIME_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_DATETIME_MANAGER, GsdDatetimeManagerPrivate))
+
+#define DATETIME_SCHEMA "org.gnome.desktop.datetime"
+#define AUTO_TIMEZONE_KEY "automatic-timezone"
+
+struct GsdDatetimeManagerPrivate
+{
+ GSettings *settings;
+ GsdTimezoneMonitor *timezone_monitor;
+};
+
+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
+auto_timezone_settings_changed_cb (GSettings *settings,
+ const char *key,
+ GsdDatetimeManager *self)
+{
+ gboolean enabled;
+
+ enabled = g_settings_get_boolean (settings, key);
+ if (enabled && self->priv->timezone_monitor == NULL) {
+ g_debug ("Automatic timezone enabled");
+ self->priv->timezone_monitor = gsd_timezone_monitor_new ();
+ } else {
+ g_debug ("Automatic timezone disabled");
+ g_clear_object (&self->priv->timezone_monitor);
+ }
+}
+
+gboolean
+gsd_datetime_manager_start (GsdDatetimeManager *self,
+ GError **error)
+{
+ g_debug ("Starting datetime manager");
+ gnome_settings_profile_start (NULL);
+
+ self->priv->settings = g_settings_new (DATETIME_SCHEMA);
+
+ auto_timezone_settings_changed_cb (self->priv->settings, AUTO_TIMEZONE_KEY, self);
+ g_signal_connect (self->priv->settings, "changed::" AUTO_TIMEZONE_KEY,
+ G_CALLBACK (auto_timezone_settings_changed_cb), self);
+
+ gnome_settings_profile_end (NULL);
+
+ return TRUE;
+}
+
+void
+gsd_datetime_manager_stop (GsdDatetimeManager *self)
+{
+ g_debug ("Stopping datetime manager");
+
+ g_clear_object (&self->priv->settings);
+ g_clear_object (&self->priv->timezone_monitor);
+}
+
+static GObject *
+gsd_datetime_manager_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GsdDatetimeManager *m;
+
+ m = GSD_DATETIME_MANAGER (G_OBJECT_CLASS (gsd_datetime_manager_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (m);
+}
+
+static void
+gsd_datetime_manager_class_init (GsdDatetimeManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = gsd_datetime_manager_constructor;
+ object_class->finalize = gsd_datetime_manager_finalize;
+
+ g_type_class_add_private (klass, sizeof (GsdDatetimeManagerPrivate));
+}
+
+static void
+gsd_datetime_manager_init (GsdDatetimeManager *manager)
+{
+ manager->priv = GSD_DATETIME_MANAGER_GET_PRIVATE (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->priv != 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 00000000..2125d3c0
--- /dev/null
+++ b/plugins/datetime/gsd-datetime-manager.h
@@ -0,0 +1,57 @@
+/* -*- 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#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 ())
+#define GSD_DATETIME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_DATETIME_MANAGER, GsdDatetimeManager))
+#define GSD_DATETIME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSD_TYPE_DATETIME_MANAGER, GsdDatetimeManagerClass))
+#define GSD_IS_DATETIME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_DATETIME_MANAGER))
+#define GSD_IS_DATETIME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_DATETIME_MANAGER))
+#define GSD_DATETIME_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_DATETIME_MANAGER, GsdDatetimeManagerClass))
+
+typedef struct GsdDatetimeManagerPrivate GsdDatetimeManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GsdDatetimeManagerPrivate *priv;
+} GsdDatetimeManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GsdDatetimeManagerClass;
+
+GType gsd_datetime_manager_get_type (void) G_GNUC_CONST;
+
+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-datetime-plugin.c b/plugins/datetime/gsd-datetime-plugin.c
new file mode 100644
index 00000000..af5564c8
--- /dev/null
+++ b/plugins/datetime/gsd-datetime-plugin.c
@@ -0,0 +1,29 @@
+/* -*- 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, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include "gnome-settings-plugin.h"
+#include "gsd-datetime-manager.h"
+
+GNOME_SETTINGS_PLUGIN_REGISTER (GsdDatetime, gsd_datetime)
diff --git a/plugins/datetime/gsd-timezone-monitor.c b/plugins/datetime/gsd-timezone-monitor.c
new file mode 100644
index 00000000..f283d692
--- /dev/null
+++ b/plugins/datetime/gsd-timezone-monitor.c
@@ -0,0 +1,355 @@
+/* -*- 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "gsd-timezone-monitor.h"
+
+#include "geoclue.h"
+#include "timedated.h"
+#include "tz.h"
+
+#include <math.h>
+
+typedef struct
+{
+ GCancellable *cancellable;
+ GeoclueClient *geoclue_client;
+ GeoclueManager *geoclue_manager;
+ Timedate1 *dtm;
+
+ TzDB *tzdb;
+ gchar *current_timezone;
+} 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)
+{
+ GsdTimezoneMonitor *self = user_data;
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+ GError *error = NULL;
+
+ if (!timedate1_call_set_timezone_finish (priv->dtm,
+ res,
+ &error)) {
+ g_warning ("Could not set system timezone: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+queue_set_timezone (GsdTimezoneMonitor *self,
+ const char *timezone)
+{
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+ timedate1_call_set_timezone (priv->dtm,
+ timezone,
+ TRUE,
+ priv->cancellable,
+ set_timezone_cb,
+ self);
+}
+
+
+#define EARTH_RADIUS_KM 6372.795
+
+/* Copy of geocode_location_get_distance_from() from geocode-glib */
+static double
+distance_between (gdouble loca_latitude, gdouble loca_longitude,
+ gdouble locb_latitude, gdouble locb_longitude)
+{
+ gdouble dlat, dlon, lat1, lat2;
+ gdouble a, c;
+
+ /* Algorithm from:
+ * http://www.movable-type.co.uk/scripts/latlong.html */
+
+ dlat = (locb_latitude - loca_latitude) * M_PI / 180.0;
+ dlon = (locb_longitude - loca_longitude) * M_PI / 180.0;
+ lat1 = loca_latitude * M_PI / 180.0;
+ lat2 = locb_latitude * M_PI / 180.0;
+
+ a = sin (dlat / 2) * sin (dlat / 2) +
+ sin (dlon / 2) * sin (dlon / 2) * cos (lat1) * cos (lat2);
+ c = 2 * atan2 (sqrt (a), sqrt (1-a));
+ return EARTH_RADIUS_KM * c;
+}
+
+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 void
+process_location (GsdTimezoneMonitor *self,
+ gdouble latitude,
+ gdouble longitude)
+{
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+ GPtrArray *array;
+ GList *distances = NULL;
+ TzLocation *closest_tz_location;
+ gint i;
+
+ array = tz_get_locations (priv->tzdb);
+
+ for (i = 0; i < array->len; i++) {
+ TzLocation *loc = array->pdata[i];
+
+ loc->dist = distance_between (latitude, longitude,
+ loc->latitude, loc->longitude);
+ distances = g_list_prepend (distances, loc);
+
+ }
+ distances = g_list_sort (distances, (GCompareFunc) compare_locations);
+
+ closest_tz_location = (TzLocation*) distances->data;
+ if (g_strcmp0 (priv->current_timezone, closest_tz_location->zone) != 0) {
+ g_debug ("Changing timezone to %s", closest_tz_location->zone);
+ queue_set_timezone (self, closest_tz_location->zone);
+ }
+
+ g_list_free (distances);
+}
+
+static void
+on_location_proxy_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GeoclueLocation *location;
+ gdouble latitude, longitude;
+ GError *error = NULL;
+ GsdTimezoneMonitor *self = user_data;
+
+ location = geoclue_location_proxy_new_for_bus_finish (res, &error);
+ if (error != NULL) {
+ g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ latitude = geoclue_location_get_latitude (location);
+ longitude = geoclue_location_get_longitude (location);
+
+ process_location (self, latitude, longitude);
+
+ g_object_unref (location);
+}
+
+static void
+on_location_updated (GDBusProxy *client,
+ gchar *location_path_old,
+ gchar *location_path_new,
+ gpointer user_data)
+{
+ GsdTimezoneMonitor *self = user_data;
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+ geoclue_location_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.freedesktop.GeoClue2",
+ location_path_new,
+ priv->cancellable,
+ on_location_proxy_ready,
+ self);
+}
+
+static void
+on_start_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!geoclue_client_call_start_finish (GEOCLUE_CLIENT (source_object),
+ res,
+ &error)) {
+ g_critical ("Failed to start GeoClue2 client: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+}
+
+static void
+on_client_proxy_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GsdTimezoneMonitor *self = user_data;
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+ priv->geoclue_client = geoclue_client_proxy_new_for_bus_finish (res, &error);
+ if (error != NULL) {
+ g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_signal_connect (priv->geoclue_client, "location-updated",
+ G_CALLBACK (on_location_updated), self);
+
+ geoclue_client_call_start (priv->geoclue_client,
+ priv->cancellable,
+ on_start_ready,
+ self);
+}
+
+static void
+on_get_client_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gchar *client_path;
+ GError *error = NULL;
+ GsdTimezoneMonitor *self = user_data;
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+ if (!geoclue_manager_call_get_client_finish (GEOCLUE_MANAGER (source_object),
+ &client_path,
+ res,
+ &error)) {
+ g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ geoclue_client_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.freedesktop.GeoClue2",
+ client_path,
+ priv->cancellable,
+ on_client_proxy_ready,
+ self);
+}
+
+static void
+on_manager_proxy_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+
+ GError *error = NULL;
+ GsdTimezoneMonitor *self = user_data;
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+ priv->geoclue_manager = geoclue_manager_proxy_new_for_bus_finish (res, &error);
+ if (error != NULL) {
+ g_critical ("Failed to connect to GeoClue2 service: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ geoclue_manager_call_get_client (priv->geoclue_manager,
+ priv->cancellable,
+ on_get_client_ready,
+ self);
+}
+
+static void
+register_geoclue (GsdTimezoneMonitor *self)
+{
+ GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+ geoclue_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.freedesktop.GeoClue2",
+ "/org/freedesktop/GeoClue2/Manager",
+ priv->cancellable,
+ on_manager_proxy_ready,
+ self);
+}
+
+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");
+
+ if (priv->cancellable) {
+ g_cancellable_cancel (priv->cancellable);
+ g_clear_object (&priv->cancellable);
+ }
+
+ g_clear_object (&priv->dtm);
+ g_clear_object (&priv->geoclue_client);
+ g_clear_object (&priv->geoclue_manager);
+ g_clear_pointer (&priv->current_timezone, g_free);
+ g_clear_pointer (&priv->tzdb, tz_db_free);
+
+ 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;
+}
+
+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->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->tzdb = tz_load_db ();
+
+ register_geoclue (self);
+}
diff --git a/plugins/datetime/gsd-timezone-monitor.h b/plugins/datetime/gsd-timezone-monitor.h
new file mode 100644
index 00000000..d3aa3b35
--- /dev/null
+++ b/plugins/datetime/gsd-timezone-monitor.h
@@ -0,0 +1,54 @@
+/* -*- 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#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;
+};
+
+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/test-datetime.c b/plugins/datetime/test-datetime.c
new file mode 100644
index 00000000..0a55ba22
--- /dev/null
+++ b/plugins/datetime/test-datetime.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 "test-plugin.h"
diff --git a/plugins/datetime/timedated1-interface.xml b/plugins/datetime/timedated1-interface.xml
new file mode 100644
index 00000000..3370e0e2
--- /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 00000000..c539d59d
--- /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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+#include <string.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;
+}
+
+static 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 00000000..71c1c231
--- /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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#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);
+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/po/POTFILES.in b/po/POTFILES.in
index 93d00755..9f05c02f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,6 +4,7 @@ data/gnome-settings-daemon.desktop.in.in
data/org.gnome.settings-daemon.peripherals.gschema.xml.in.in
data/org.gnome.settings-daemon.peripherals.wacom.gschema.xml.in.in
data/org.gnome.settings-daemon.plugins.color.gschema.xml.in.in
+data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in.in
data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
data/org.gnome.settings-daemon.plugins.housekeeping.gschema.xml.in.in
data/org.gnome.settings-daemon.plugins.keyboard.gschema.xml.in.in
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 69411934..132d3a54 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -10,6 +10,7 @@ data/gnome-settings-daemon.desktop.in
data/org.gnome.settings-daemon.peripherals.gschema.xml.in
data/org.gnome.settings-daemon.peripherals.wacom.gschema.xml.in
data/org.gnome.settings-daemon.plugins.color.gschema.xml.in
+data/org.gnome.settings-daemon.plugins.datetime.gschema.xml.in
data/org.gnome.settings-daemon.plugins.gschema.xml.in
data/org.gnome.settings-daemon.plugins.housekeeping.gschema.xml.in
data/org.gnome.settings-daemon.plugins.keyboard.gschema.xml.in