Back to index

indicator-datetime  12.10.0
utils.c
Go to the documentation of this file.
00001 /* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*-
00002 
00003 A dialog for setting time and date preferences.
00004 
00005 Copyright 2010 Canonical Ltd.
00006 
00007 Authors:
00008     Michael Terry <michael.terry@canonical.com>
00009 
00010 This program is free software: you can redistribute it and/or modify it 
00011 under the terms of the GNU General Public License version 3, as published 
00012 by the Free Software Foundation.
00013 
00014 This program is distributed in the hope that it will be useful, but 
00015 WITHOUT ANY WARRANTY; without even the implied warranties of 
00016 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
00017 PURPOSE.  See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License along 
00020 with this program.  If not, see <http://www.gnu.org/licenses/>.
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 #include <glib/gi18n-lib.h>
00028 #include <gio/gio.h>
00029 #include <locale.h>
00030 #include <langinfo.h>
00031 #include <string.h>
00032 #include "utils.h"
00033 #include "settings-shared.h"
00034 
00035 /* Check the system locale setting to see if the format is 24-hour
00036    time or 12-hour time */
00037 gboolean
00038 is_locale_12h (void)
00039 {
00040        static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL};
00041        const char *t_fmt = nl_langinfo (T_FMT);
00042        int i;
00043 
00044        for (i = 0; formats_24h[i]; ++i) {
00045               if (strstr (t_fmt, formats_24h[i])) {
00046                      return FALSE;
00047               }
00048        }
00049 
00050        return TRUE;
00051 }
00052 
00053 void
00054 split_settings_location (const gchar * location, gchar ** zone, gchar ** name)
00055 {
00056   gchar * location_dup = g_strdup (location);
00057   gchar * first = strchr (location_dup, ' ');
00058 
00059   if (first) {
00060     first[0] = 0;
00061   }
00062 
00063   if (zone) {
00064     *zone = location_dup;
00065   }
00066 
00067   if (name) {
00068     gchar * after = first ? g_strstrip (first + 1) : NULL;
00069     if (after == NULL || after[0] == 0) {
00070       /* Make up name from zone */
00071       gchar * chr = strrchr (location_dup, '/');
00072       after = g_strdup (chr ? chr + 1 : location_dup);
00073       while ((chr = strchr (after, '_')) != NULL) { /* and turn underscores to spaces */
00074         *chr = ' ';
00075       }
00076       *name = after;
00077     }
00078     else {
00079       *name = g_strdup (after);
00080     }
00081   }
00082 }
00083 
00084 gchar *
00085 get_current_zone_name (const gchar * location)
00086 {
00087   gchar * new_zone, * new_name;
00088   gchar * old_zone, * old_name;
00089   gchar * rv;
00090 
00091   split_settings_location (location, &new_zone, &new_name);
00092 
00093   GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
00094   gchar * tz_name = g_settings_get_string (conf, SETTINGS_TIMEZONE_NAME_S);
00095   split_settings_location (tz_name, &old_zone, &old_name);
00096   g_free (tz_name);
00097   g_object_unref (conf);
00098 
00099   // new_name is always just a sanitized version of a timezone.
00100   // old_name is potentially a saved "pretty" version of a timezone name from
00101   // geonames.  So we prefer to use it if available and the zones match.
00102 
00103   if (g_strcmp0 (old_zone, new_zone) == 0) {
00104     rv = old_name;
00105     old_name = NULL;
00106   }
00107   else {
00108     rv = new_name;
00109     new_name = NULL;
00110   }
00111 
00112   g_free (new_zone);
00113   g_free (old_zone);
00114   g_free (new_name);
00115   g_free (old_name);
00116 
00117   return rv;
00118 }
00119 
00120 gchar *
00121 read_timezone ()
00122 {
00123        GError * error = NULL;
00124        gchar * tempzone = NULL;
00125        if (!g_file_get_contents(TIMEZONE_FILE, &tempzone, NULL, &error)) {
00126               g_warning("Unable to read timezone file '" TIMEZONE_FILE "': %s", error->message);
00127               g_error_free(error);
00128               return NULL;
00129        }
00130 
00131        /* This shouldn't happen, so let's make it a big boom! */
00132        g_return_val_if_fail(tempzone != NULL, NULL);
00133 
00134        /* Note: this really makes sense as strstrip works in place
00135           so we end up with something a little odd without the dup
00136           so we have the dup to make sure everything is as expected
00137           for everyone else. */
00138        gchar * rv = g_strdup(g_strstrip(tempzone));
00139        g_free(tempzone);
00140 
00141   return rv;
00142 }
00143 
00144 /* Translate msg according to the locale specified by LC_TIME */
00145 static char *
00146 T_(const char *msg)
00147 {
00148        /* General strategy here is to make sure LANGUAGE is empty (since that
00149           trumps all LC_* vars) and then to temporarily swap LC_TIME and
00150           LC_MESSAGES.  Then have gettext translate msg.
00151 
00152           We strdup the strings because the setlocale & *env functions do not
00153           guarantee anything about the storage used for the string, and thus
00154           the string may not be portably safe after multiple calls.
00155 
00156           Note that while you might think g_dcgettext would do the trick here,
00157           that actually looks in /usr/share/locale/XX/LC_TIME, not the
00158           LC_MESSAGES directory, so we won't find any translation there.
00159        */
00160        char *message_locale = g_strdup(setlocale(LC_MESSAGES, NULL));
00161        char *time_locale = g_strdup(setlocale(LC_TIME, NULL));
00162        char *language = g_strdup(g_getenv("LANGUAGE"));
00163        char *rv;
00164        if (language)
00165               g_unsetenv("LANGUAGE");
00166        setlocale(LC_MESSAGES, time_locale);
00167 
00168        /* Get the LC_TIME version */
00169        rv = _(msg);
00170 
00171        /* Put everything back the way it was */
00172        setlocale(LC_MESSAGES, message_locale);
00173        if (language)
00174               g_setenv("LANGUAGE", language, TRUE);
00175        g_free(message_locale);
00176        g_free(time_locale);
00177        g_free(language);
00178        return rv;
00179 }
00180 
00181 /* Tries to figure out what our format string should be.  Lots
00182    of translator comments in here. */
00183 gchar *
00184 generate_format_string_full (gboolean show_day, gboolean show_date)
00185 {
00186        gboolean twelvehour = TRUE;
00187 
00188        GSettings * settings = g_settings_new (SETTINGS_INTERFACE);
00189        gint time_mode = g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S);
00190        gboolean show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S);
00191        g_object_unref (settings);
00192 
00193        if (time_mode == SETTINGS_TIME_LOCALE) {
00194               twelvehour = is_locale_12h();
00195        } else if (time_mode == SETTINGS_TIME_24_HOUR) {
00196               twelvehour = FALSE;
00197        }
00198 
00199        const gchar * time_string = NULL;
00200        if (twelvehour) {
00201               if (show_seconds) {
00202                      /* TRANSLATORS: A format string for the strftime function for
00203                         a clock showing 12-hour time with seconds. */
00204                      time_string = T_("%l:%M:%S %p");
00205               } else {
00206                      time_string = T_(DEFAULT_TIME_12_FORMAT);
00207               }
00208        } else {
00209               if (show_seconds) {
00210                      /* TRANSLATORS: A format string for the strftime function for
00211                         a clock showing 24-hour time with seconds. */
00212                      time_string = T_("%H:%M:%S");
00213               } else {
00214                      time_string = T_(DEFAULT_TIME_24_FORMAT);
00215               }
00216        }
00217        
00218        /* Checkpoint, let's not fail */
00219        g_return_val_if_fail(time_string != NULL, g_strdup(DEFAULT_TIME_FORMAT));
00220 
00221        /* If there's no date or day let's just leave now and
00222           not worry about the rest of this code */
00223        if (!show_date && !show_day) {
00224               return g_strdup(time_string);
00225        }
00226 
00227        const gchar * date_string = NULL;
00228        if (show_date && show_day) {
00229               /* TRANSLATORS:  This is a format string passed to strftime to represent
00230                  the day of the week, the month and the day of the month. */
00231               date_string = T_("%a %b %e");
00232        } else if (show_date) {
00233               /* TRANSLATORS:  This is a format string passed to strftime to represent
00234                  the month and the day of the month. */
00235               date_string = T_("%b %e");
00236        } else if (show_day) {
00237               /* TRANSLATORS:  This is a format string passed to strftime to represent
00238                  the day of the week. */
00239               date_string = T_("%a");
00240        }
00241 
00242        /* Check point, we should have a date string */
00243        g_return_val_if_fail(date_string != NULL, g_strdup(time_string));
00244 
00245        /* TRANSLATORS: This is a format string passed to strftime to combine the
00246           date and the time.  The value of "%s\xE2\x80\x82%s" would result in a string like
00247           this in US English 12-hour time: 'Fri Jul 16 11:50 AM'.
00248           The space in between date and time is a Unicode en space (E28082 in UTF-8 hex). */
00249        return g_strdup_printf(T_("%s\xE2\x80\x82%s"), date_string, time_string);
00250 }
00251 
00252 gchar *
00253 generate_format_string_at_time (GDateTime * time)
00254 {
00255        /* This is a bit less free-form than for the main "now" time label. */
00256        /* If it is today, just the time should be shown (e.g. “3:55 PM”)
00257            If it is a different day this week, the day and time should be shown (e.g. “Wed 3:55 PM”)
00258            If it is after this week, the day, date, and time should be shown (e.g. “Wed 21 Apr 3:55 PM”). 
00259            In addition, when presenting the times of upcoming events, the time should be followed by the timezone if it is different from the one the computer is currently set to. For example, “Wed 3:55 PM UTC−5”. */
00260        gboolean show_day = FALSE;
00261        gboolean show_date = FALSE;
00262 
00263        GDateTime * now = g_date_time_new_now_local();
00264 
00265        /* First, are we same day? */
00266        gint time_year, time_month, time_day;
00267        gint now_year, now_month, now_day;
00268        g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
00269        g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
00270 
00271        if (time_year != now_year ||
00272            time_month != now_month ||
00273            time_day != now_day) {
00274               /* OK, different days so we must at least show the day. */
00275               show_day = TRUE;
00276 
00277               /* Is it this week? */
00278               /* Here, we define "is this week" as yesterday, today, or the next five days */
00279               GDateTime * past = g_date_time_add_days(now, -1);
00280               GDateTime * future = g_date_time_add_days(now, 5);
00281               GDateTime * past_bound = g_date_time_new_local(g_date_time_get_year(past),
00282                                                              g_date_time_get_month(past),
00283                                                              g_date_time_get_day_of_month(past),
00284                                                              0, 0, 0.0);
00285               GDateTime * future_bound = g_date_time_new_local(g_date_time_get_year(future),
00286                                                                g_date_time_get_month(future),
00287                                                                g_date_time_get_day_of_month(future),
00288                                                                23, 59, 59.9);
00289               if (g_date_time_compare(time, past_bound) < 0 ||
00290                   g_date_time_compare(time, future_bound) > 0) {
00291                      show_date = TRUE;
00292               }
00293               g_date_time_unref(past);
00294               g_date_time_unref(future);
00295               g_date_time_unref(past_bound);
00296               g_date_time_unref(future_bound);
00297        }
00298 
00299        g_date_time_unref (now);
00300 
00301        return generate_format_string_full(show_day, show_date);
00302 }
00303