/* -*- mode: C; c-file-style: "linux"; indent-tabs-mode: t -*- * gnome-wall-clock.h - monitors TZ setting files and signals changes * * Copyright (C) 2010 Red Hat, Inc. * Copyright (C) 2011 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Colin Walters */ #include "config.h" #include #include #define GNOME_DESKTOP_USE_UNSTABLE_API #include "gnome-wall-clock.h" #include #include "gnome-datetime-source.h" struct _GnomeWallClockPrivate { guint clock_update_id; GTimeZone *timezone; char *clock_string; GFileMonitor *tz_monitor; GSettings *desktop_settings; gboolean time_only; gboolean ampm_available; }; enum { PROP_0, PROP_CLOCK, PROP_TIMEZONE, PROP_TIME_ONLY, }; G_DEFINE_TYPE (GnomeWallClock, gnome_wall_clock, G_TYPE_OBJECT); static gboolean update_clock (gpointer data); static void on_schema_change (GSettings *schema, const char *key, gpointer user_data); static void on_tz_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent *event, gpointer user_data); static void gnome_wall_clock_init (GnomeWallClock *self) { GFile *tz; const char *ampm; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_WALL_CLOCK, GnomeWallClockPrivate); self->priv->timezone = g_time_zone_new_local (); self->priv->clock_string = NULL; tz = g_file_new_for_path ("/etc/localtime"); self->priv->tz_monitor = g_file_monitor_file (tz, 0, NULL, NULL); g_object_unref (tz); g_signal_connect (self->priv->tz_monitor, "changed", G_CALLBACK (on_tz_changed), self); self->priv->desktop_settings = g_settings_new ("org.gnome.desktop.interface"); g_signal_connect (self->priv->desktop_settings, "changed", G_CALLBACK (on_schema_change), self); ampm = nl_langinfo (AM_STR); if (ampm != NULL && *ampm != '\0') self->priv->ampm_available = TRUE; update_clock (self); } static void gnome_wall_clock_dispose (GObject *object) { GnomeWallClock *self = GNOME_WALL_CLOCK (object); if (self->priv->clock_update_id) { g_source_remove (self->priv->clock_update_id); self->priv->clock_update_id = 0; } if (self->priv->tz_monitor != NULL) { g_object_unref (self->priv->tz_monitor); self->priv->tz_monitor = NULL; } if (self->priv->desktop_settings != NULL) { g_object_unref (self->priv->desktop_settings); self->priv->desktop_settings = NULL; } G_OBJECT_CLASS (gnome_wall_clock_parent_class)->dispose (object); } static void gnome_wall_clock_finalize (GObject *object) { GnomeWallClock *self = GNOME_WALL_CLOCK (object); g_time_zone_unref (self->priv->timezone); g_free (self->priv->clock_string); G_OBJECT_CLASS (gnome_wall_clock_parent_class)->finalize (object); } static void gnome_wall_clock_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { GnomeWallClock *self = GNOME_WALL_CLOCK (gobject); switch (prop_id) { case PROP_TIME_ONLY: g_value_set_boolean (value, self->priv->time_only); break; case PROP_TIMEZONE: g_value_set_boxed (value, self->priv->timezone); break; case PROP_CLOCK: g_value_set_string (value, self->priv->clock_string); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void gnome_wall_clock_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { GnomeWallClock *self = GNOME_WALL_CLOCK (gobject); switch (prop_id) { case PROP_TIME_ONLY: self->priv->time_only = g_value_get_boolean (value); update_clock (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void gnome_wall_clock_class_init (GnomeWallClockClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); gobject_class->get_property = gnome_wall_clock_get_property; gobject_class->set_property = gnome_wall_clock_set_property; gobject_class->dispose = gnome_wall_clock_dispose; gobject_class->finalize = gnome_wall_clock_finalize; /** * GnomeWallClock:clock: * * A formatted string representing the current clock display. */ g_object_class_install_property (gobject_class, PROP_CLOCK, g_param_spec_string ("clock", "", "", NULL, G_PARAM_READABLE)); /** * GnomeWallClock:timezone: * * The timezone used for this clock */ g_object_class_install_property (gobject_class, PROP_TIMEZONE, g_param_spec_boxed ("timezone", "", "", G_TYPE_TIME_ZONE, G_PARAM_READABLE)); /** * GnomeWallClock:time-only: * * If %TRUE, the formatted clock will never include a date or the * day of the week, irrespective of configuration. */ g_object_class_install_property (gobject_class, PROP_TIME_ONLY, g_param_spec_boolean ("time-only", "", "", FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)); g_type_class_add_private (gobject_class, sizeof (GnomeWallClockPrivate)); } /* Replace 'target' with 'replacement' in the input string. */ static char * string_replace (const char *input, const char *target, const char *replacement) { char **pieces = NULL; char *output = NULL; pieces = g_strsplit (input, target, -1); output = g_strjoinv (replacement, pieces); g_strfreev (pieces); return output; } /* This function wraps g_date_time_format, replacing colon with the ratio * character as it looks visually better in time strings. */ static char * date_time_format (GDateTime *datetime, const char *format) { char *format_with_colon; char *ret; char *tmp; gboolean is_utf8; is_utf8 = g_get_charset (NULL); /* First, replace ratio with plain colon before passing it to * g_date_time_format. */ tmp = string_replace (format, "∶", ":"); format_with_colon = g_date_time_format (datetime, tmp); g_free (tmp); /* Then, after formatting, replace the plain colon with ratio, and * prepend it with an LTR marker to force direction. */ if (is_utf8) ret = string_replace (format_with_colon, ":", "\xE2\x80\x8E∶"); else ret = g_strdup (format_with_colon); g_free (format_with_colon); return ret; } /** * gnome_wall_clock_string_for_datetime: * * Returns: (skip): a newly allocated string representing the date & time * passed, with the options applied. */ char * gnome_wall_clock_string_for_datetime (GnomeWallClock *self, GDateTime *now, GDesktopClockFormat clock_format, gboolean show_weekday, gboolean show_full_date, gboolean show_seconds) { const char *format_string; if (clock_format == G_DESKTOP_CLOCK_FORMAT_24H || self->priv->ampm_available == FALSE) { if (show_full_date) { /* Translators: This is the time format with full date used in 24-hour mode. */ format_string = show_seconds ? _("%a %b %e, %R:%S") : _("%a %b %e, %R"); } else if (show_weekday) { /* Translators: This is the time format with day used in 24-hour mode. */ format_string = show_seconds ? _("%a %R:%S") : _("%a %R"); } else { /* Translators: This is the time format without date used in 24-hour mode. */ format_string = show_seconds ? _("%R:%S") : _("%R"); } } else { if (show_full_date) { /* Translators: This is a time format with full date used for AM/PM. */ format_string = show_seconds ? _("%a %b %e, %l:%M:%S %p") : _("%a %b %e, %l:%M %p"); } else if (show_weekday) { /* Translators: This is a time format with day used for AM/PM. */ format_string = show_seconds ? _("%a %l:%M:%S %p") : _("%a %l:%M %p"); } else { /* Translators: This is a time format without date used for AM/PM. */ format_string = show_seconds ? _("%l:%M:%S %p") : _("%l:%M %p"); } } return date_time_format (now, format_string); } static gboolean update_clock (gpointer data) { GnomeWallClock *self = data; GDesktopClockFormat clock_format; gboolean show_full_date; gboolean show_weekday; gboolean show_seconds; GSource *source; GDateTime *now; GDateTime *expiry; clock_format = g_settings_get_enum (self->priv->desktop_settings, "clock-format"); show_weekday = !self->priv->time_only; show_full_date = show_weekday && g_settings_get_boolean (self->priv->desktop_settings, "clock-show-date"); show_seconds = g_settings_get_boolean (self->priv->desktop_settings, "clock-show-seconds"); now = g_date_time_new_now (self->priv->timezone); if (show_seconds) expiry = g_date_time_add_seconds (now, 1); else expiry = g_date_time_add_seconds (now, 60 - g_date_time_get_second (now)); if (self->priv->clock_update_id) g_source_remove (self->priv->clock_update_id); source = _gnome_datetime_source_new (now, expiry, TRUE); g_source_set_priority (source, G_PRIORITY_HIGH); g_source_set_callback (source, update_clock, self, NULL); self->priv->clock_update_id = g_source_attach (source, NULL); g_source_unref (source); g_free (self->priv->clock_string); self->priv->clock_string = gnome_wall_clock_string_for_datetime (self, now, clock_format, show_weekday, show_full_date, show_seconds); g_date_time_unref (now); g_date_time_unref (expiry); g_object_notify ((GObject*)self, "clock"); return FALSE; } static gboolean update_timezone (gpointer data) { GnomeWallClock *self = data; if (self->priv->timezone != NULL) g_time_zone_unref (self->priv->timezone); self->priv->timezone = g_time_zone_new_local (); g_object_notify ((GObject*)self, "timezone"); return update_clock (data); } static void on_schema_change (GSettings *schema, const char *key, gpointer user_data) { g_debug ("Updating clock because schema changed"); update_clock (user_data); } static void on_tz_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent *event, gpointer user_data) { g_debug ("Updating clock because timezone changed"); update_timezone (user_data); } /** * gnome_wall_clock_new: * * Creates a new #GnomeWallClock * * Return value: the new clock **/ GnomeWallClock * gnome_wall_clock_new (void) { return (GnomeWallClock *) g_object_new (GNOME_TYPE_WALL_CLOCK, NULL); } /** * gnome_wall_clock_get_clock: * @clock: a #GnomeWallClock * * Returns the string representing the current time of this clock * according to the user settings. * * Return value: the time of the clock as a string. * This string points to internally allocated storage and * must not be freed, modified or stored. */ const char * gnome_wall_clock_get_clock (GnomeWallClock *clock) { return clock->priv->clock_string; } /** * gnome_wall_clock_get_timezone: * @clock: a #GnomeWallClock * * Returns the current local time zone used by this clock. * * Return value: (transfer none): the #GTimeZone of the clock. */ GTimeZone * gnome_wall_clock_get_timezone (GnomeWallClock *clock) { return clock->priv->timezone; }