aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/datetime/utils.h50
-rw-r--r--panel-gnome/CMakeLists.txt3
-rw-r--r--panel-unity/CMakeLists.txt3
-rw-r--r--panel/datetime-prefs-locations.c2
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/utils.c307
-rw-r--r--src/utils.cpp154
7 files changed, 342 insertions, 180 deletions
diff --git a/include/datetime/utils.h b/include/datetime/utils.h
index 10e881f..7cac9fd 100644
--- a/include/datetime/utils.h
+++ b/include/datetime/utils.h
@@ -1,24 +1,22 @@
-/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*-
-
-A dialog for setting time and date preferences.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Michael Terry <michael.terry@canonical.com>
-
-This program is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License version 3, as published
-by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranties of
-MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.
-*/
+/*
+ * Copyright 2010, 2014 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.
+ *
+ * Authors:
+ * Michael Terry <michael.terry@canonical.com>
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
#ifndef INDICATOR_DATETIME_UTILS_H
#define INDICATOR_DATETIME_UTILS_H
@@ -28,6 +26,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
G_BEGIN_DECLS
+/** \brief Returns true if the current locale prefers 12h display instead of 24h */
gboolean is_locale_12h (void);
void split_settings_location (const char * location,
@@ -41,7 +40,14 @@ gchar * get_beautified_timezone_name (const char * timezone,
const char * saved_location);
gchar * generate_full_format_string_at_time (GDateTime * now,
- GDateTime * time);
+ GDateTime * then_begin,
+ GDateTime * then_end);
+
+/** \brief Translate the string based on LC_TIME instead of LC_MESSAGES.
+ The intent of this is to let users set LC_TIME to override
+ their other locale settings when generating time format string */
+const char* T_ (const char * msg);
+
G_END_DECLS
diff --git a/panel-gnome/CMakeLists.txt b/panel-gnome/CMakeLists.txt
index 1135cd4..82c511d 100644
--- a/panel-gnome/CMakeLists.txt
+++ b/panel-gnome/CMakeLists.txt
@@ -3,13 +3,14 @@ set (PANEL_LIB "gnome-indicator-datetime")
add_definitions (-DPKGDATADIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}")
+SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g")
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}")
add_library (${PANEL_LIB} SHARED
${CMAKE_SOURCE_DIR}/panel/datetime-prefs.c
${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.c
${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.h
- ${CMAKE_SOURCE_DIR}/src/utils.cpp
+ ${CMAKE_SOURCE_DIR}/src/utils.c
${CMAKE_SOURCE_DIR}/include/datetime/utils.h
${CMAKE_SOURCE_DIR}/include/datetime/settings-shared.h)
set_property (TARGET ${PANEL_LIB} PROPERTY OUTPUT_NAME indicator-datetime)
diff --git a/panel-unity/CMakeLists.txt b/panel-unity/CMakeLists.txt
index 150034c..c8ebde0 100644
--- a/panel-unity/CMakeLists.txt
+++ b/panel-unity/CMakeLists.txt
@@ -2,13 +2,14 @@ set (PANEL_LIB "unity-indicator-datetime")
add_definitions (-DPKGDATADIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}")
+SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g")
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}")
add_library (${PANEL_LIB} SHARED
${CMAKE_SOURCE_DIR}/panel/datetime-prefs.c
${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.c
${CMAKE_SOURCE_DIR}/panel/datetime-prefs-locations.h
- ${CMAKE_SOURCE_DIR}/src/utils.cpp
+ ${CMAKE_SOURCE_DIR}/src/utils.c
${CMAKE_SOURCE_DIR}/include/datetime/utils.h
${CMAKE_SOURCE_DIR}/include/datetime/settings-shared.h)
set_property (TARGET ${PANEL_LIB} PROPERTY OUTPUT_NAME indicator-datetime)
diff --git a/panel/datetime-prefs-locations.c b/panel/datetime-prefs-locations.c
index b79c014..0437eb4 100644
--- a/panel/datetime-prefs-locations.c
+++ b/panel/datetime-prefs-locations.c
@@ -439,7 +439,7 @@ update_times (GtkWidget * dlg)
if (strzone && *strzone) {
GTimeZone * tz = g_time_zone_new (strzone);
GDateTime * now_tz = g_date_time_to_timezone (now, tz);
- gchar * format = generate_full_format_string_at_time (now, now_tz);
+ gchar * format = generate_full_format_string_at_time (now, now_tz, NULL);
gchar * time_str = g_date_time_format (now_tz, format);
gchar * old_time_str;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b8c62fd..eb716d4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,7 @@
set (SERVICE_LIB "indicatordatetimeservice")
set (SERVICE_EXEC "indicator-datetime-service")
+SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}")
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}")
add_definitions (-DTIMEZONE_FILE="/etc/timezone"
@@ -29,7 +30,7 @@ add_library (${SERVICE_LIB} STATIC
timezone-file.cpp
timezone-geoclue.cpp
timezones-live.cpp
- utils.cpp)
+ utils.c)
include_directories (${CMAKE_SOURCE_DIR})
link_directories (${SERVICE_DEPS_LIBRARY_DIRS})
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..f4eb53f
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2010, 2014 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.
+ *
+ * Authors:
+ * Michael Terry <michael.terry@canonical.com>
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+
+#include <datetime/utils.h>
+#include <datetime/settings-shared.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <locale.h>
+#include <langinfo.h>
+#include <string.h>
+
+/* Check the system locale setting to see if the format is 24-hour
+ time or 12-hour time */
+gboolean
+is_locale_12h(void)
+{
+ int i;
+ static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL};
+ const char* t_fmt = nl_langinfo(T_FMT);
+
+ for (i=0; formats_24h[i]!=NULL; i++)
+ if (strstr(t_fmt, formats_24h[i]) != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+void
+split_settings_location(const gchar* location, gchar** zone, gchar** name)
+{
+ gchar* location_dup = g_strdup(location);
+ if(location_dup != NULL)
+ g_strstrip(location_dup);
+
+ gchar* first;
+ if(location_dup && (first = strchr(location_dup, ' ')))
+ *first = '\0';
+
+ if(zone)
+ *zone = location_dup;
+
+ if(name != NULL)
+ {
+ gchar* after = first ? g_strstrip(first + 1) : NULL;
+
+ if(after && *after)
+ {
+ *name = g_strdup(after);
+ }
+ else if (location_dup) // make the name from zone
+ {
+ gchar * chr = strrchr(location_dup, '/');
+ after = g_strdup(chr ? chr + 1 : location_dup);
+
+ // replace underscores with spaces
+ for(chr=after; chr && *chr; chr++)
+ if(*chr == '_')
+ *chr = ' ';
+
+ *name = after;
+ }
+ else
+ {
+ *name = NULL;
+ }
+ }
+}
+
+/**
+ * Our Locations come from two places: (1) direct user input and (2) ones
+ * guessed by the system, such as from geoclue or timedate1.
+ *
+ * Since the latter only have a timezone (eg, "America/Chicago") and the
+ * former have a descriptive name provided by the end user (eg,
+ * "America/Chicago Oklahoma City"), this function tries to make a
+ * more human-readable name by using the user-provided name if the guessed
+ * timezone matches the last one the user manually clicked on.
+ *
+ * In the example above, this allows the menuitem for the system-guessed
+ * timezone ("America/Chicago") to read "Oklahoma City" after the user clicks
+ * on the "Oklahoma City" menuitem.
+ */
+gchar*
+get_beautified_timezone_name(const char* timezone_, const char* saved_location)
+{
+ gchar* zone;
+ gchar* name;
+ split_settings_location(timezone_, &zone, &name);
+
+ gchar* saved_zone;
+ gchar* saved_name;
+ split_settings_location(saved_location, &saved_zone, &saved_name);
+
+ gchar* rv;
+ if (g_strcmp0(zone, saved_zone) == 0)
+ {
+ rv = saved_name;
+ saved_name = NULL;
+ }
+ else
+ {
+ rv = name;
+ name = NULL;
+ }
+
+ g_free(zone);
+ g_free(name);
+ g_free(saved_zone);
+ g_free(saved_name);
+ return rv;
+}
+
+gchar*
+get_timezone_name(const gchar* timezone_, GSettings* settings)
+{
+ gchar* saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S);
+ gchar* rv = get_beautified_timezone_name(timezone_, saved_location);
+ g_free(saved_location);
+ return rv;
+}
+
+/***
+****
+***/
+
+typedef enum
+{
+ DATE_PROXIMITY_TODAY,
+ DATE_PROXIMITY_TOMORROW,
+ DATE_PROXIMITY_WEEK,
+ DATE_PROXIMITY_FAR
+}
+date_proximity_t;
+
+static date_proximity_t
+getDateProximity(GDateTime* now, GDateTime* time)
+{
+ date_proximity_t prox = DATE_PROXIMITY_FAR;
+ gint now_year, now_month, now_day;
+ gint time_year, time_month, time_day;
+
+ // does it happen today?
+ g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
+ g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
+ if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day))
+ prox = DATE_PROXIMITY_TODAY;
+
+ // does it happen tomorrow?
+ if (prox == DATE_PROXIMITY_FAR)
+ {
+ GDateTime* tomorrow = g_date_time_add_days(now, 1);
+
+ gint tom_year, tom_month, tom_day;
+ g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day);
+ if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day))
+ prox = DATE_PROXIMITY_TOMORROW;
+
+ g_date_time_unref(tomorrow);
+ }
+
+ // does it happen this week?
+ if (prox == DATE_PROXIMITY_FAR)
+ {
+ GDateTime* week = g_date_time_add_days(now, 6);
+ GDateTime* week_bound = g_date_time_new_local(g_date_time_get_year(week),
+ g_date_time_get_month(week),
+ g_date_time_get_day_of_month(week),
+ 23, 59, 59.9);
+
+ if (g_date_time_compare(time, week_bound) <= 0)
+ prox = DATE_PROXIMITY_WEEK;
+
+ g_date_time_unref(week_bound);
+ g_date_time_unref(week);
+ }
+
+ return prox;
+}
+
+const char*
+T_(const char *msg)
+{
+ /* General strategy here is to make sure LANGUAGE is empty (since that
+ trumps all LC_* vars) and then to temporarily swap LC_TIME and
+ LC_MESSAGES. Then have gettext translate msg.
+
+ We strdup the strings because the setlocale & *env functions do not
+ guarantee anything about the storage used for the string, and thus
+ the string may not be portably safe after multiple calls.
+
+ Note that while you might think g_dcgettext would do the trick here,
+ that actually looks in /usr/share/locale/XX/LC_TIME, not the
+ LC_MESSAGES directory, so we won't find any translation there.
+ */
+
+ gchar* message_locale = g_strdup(setlocale(LC_MESSAGES, NULL));
+ const char* time_locale = setlocale(LC_TIME, NULL);
+ gchar* language = g_strdup(g_getenv("LANGUAGE"));
+
+ if (language)
+ g_unsetenv("LANGUAGE");
+ setlocale(LC_MESSAGES, time_locale);
+
+ /* Get the LC_TIME version */
+ const char* rv = _(msg);
+
+ /* Put everything back the way it was */
+ setlocale(LC_MESSAGES, message_locale);
+ if (language)
+ g_setenv("LANGUAGE", language, TRUE);
+
+ g_free(message_locale);
+ g_free(language);
+ return rv;
+}
+
+
+/**
+ * _ a time today should be shown as just the time (e.g. “3:55 PM”)
+ * _ a full-day event today should be shown as “Today”
+ * _ a time any other day this week should be shown as the short version of the
+ * day and time (e.g. “Wed 3:55 PM”)
+ * _ a full-day event tomorrow should be shown as “Tomorrow”
+ * _ a full-day event another day this week should be shown as the
+ * weekday (e.g. “Friday”)
+ * _ a time after this week should be shown as the short version of the day,
+ * date, and time (e.g. “Wed 21 Apr 3:55 PM”)
+ * _ a full-day event after this week should be shown as the short version of
+ * the day and date (e.g. “Wed 21 Apr”).
+ * _ 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”.
+ */
+char* generate_full_format_string_at_time (GDateTime* now,
+ GDateTime* then,
+ GDateTime* then_end)
+{
+ GString* ret = g_string_new (NULL);
+
+ if (then != NULL)
+ {
+ const gboolean full_day = then_end && (g_date_time_difference(then_end, then) >= G_TIME_SPAN_DAY);
+ const date_proximity_t prox = getDateProximity(now, then);
+
+ if (full_day)
+ {
+ switch (prox)
+ {
+ case DATE_PROXIMITY_TODAY: g_string_assign (ret, T_("Today")); break;
+ case DATE_PROXIMITY_TOMORROW: g_string_assign (ret, T_("Tomorrow")); break;
+ case DATE_PROXIMITY_WEEK: g_string_assign (ret, T_("%A")); break;
+ case DATE_PROXIMITY_FAR: g_string_assign (ret, T_("%a %d %b")); break;
+ }
+ }
+ else if (is_locale_12h())
+ {
+ switch (prox)
+ {
+ case DATE_PROXIMITY_TODAY: g_string_assign (ret, T_("%l:%M %p")); break;
+ case DATE_PROXIMITY_TOMORROW: g_string_assign (ret, T_("Tomorrow\u2003%l:%M %p")); break;
+ case DATE_PROXIMITY_WEEK: g_string_assign (ret, T_("%a\u2003%l:%M %p")); break;
+ case DATE_PROXIMITY_FAR: g_string_assign (ret, T_("%a %d %b\u2003%l:%M %p")); break;
+ }
+ }
+ else
+ {
+ switch (prox)
+ {
+ case DATE_PROXIMITY_TODAY: g_string_assign (ret, T_("%H:%M")); break;
+ case DATE_PROXIMITY_TOMORROW: g_string_assign (ret, T_("Tomorrow\u2003%H:%M")); break;
+ case DATE_PROXIMITY_WEEK: g_string_assign (ret, T_("%a\u2003%H:%M")); break;
+ case DATE_PROXIMITY_FAR: g_string_assign (ret, T_("%a %d %b\u2003%H:%M")); break;
+ }
+ }
+
+ /* if it's an appointment in a different timezone (and doesn't run for a full day)
+ then the time should be followed by its timezone. */
+ if ((then_end != NULL) &&
+ (!full_day) &&
+ ((g_date_time_get_utc_offset(now) != g_date_time_get_utc_offset(then))))
+ {
+ g_string_append_printf (ret, " %s", g_date_time_get_timezone_abbreviation(then));
+ }
+ }
+
+ return g_string_free (ret, FALSE);
+}
diff --git a/src/utils.cpp b/src/utils.cpp
deleted file mode 100644
index e97b654..0000000
--- a/src/utils.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*-
-
-A dialog for setting time and date preferences.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Michael Terry <michael.terry@canonical.com>
-
-This program is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License version 3, as published
-by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranties of
-MERCHANTABILITY, SATISFACTORY QUALITY, 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 <datetime/utils.h>
-
-#include <datetime/clock.h>
-#include <datetime/clock-mock.h>
-#include <datetime/formatter.h>
-#include <datetime/settings-live.h>
-
-#include <glib.h>
-
-#include <locale.h>
-#include <langinfo.h>
-#include <string.h>
-
-/* Check the system locale setting to see if the format is 24-hour
- time or 12-hour time */
-gboolean
-is_locale_12h()
-{
- static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"};
- const auto t_fmt = nl_langinfo(T_FMT);
-
- for (const auto& needle : formats_24h)
- if (strstr(t_fmt, needle) != nullptr)
- return false;
-
- return true;
-}
-
-void
-split_settings_location(const gchar* location, gchar** zone, gchar** name)
-{
- auto location_dup = g_strdup(location);
- if(location_dup != nullptr)
- g_strstrip(location_dup);
-
- gchar* first;
- if(location_dup && (first = strchr(location_dup, ' ')))
- *first = '\0';
-
- if(zone)
- *zone = location_dup;
-
- if(name != nullptr)
- {
- gchar* after = first ? g_strstrip(first + 1) : nullptr;
-
- if(after && *after)
- {
- *name = g_strdup(after);
- }
- else if (location_dup) // make the name from zone
- {
- gchar * chr = strrchr(location_dup, '/');
- after = g_strdup(chr ? chr + 1 : location_dup);
-
- // replace underscores with spaces
- for(chr=after; chr && *chr; chr++)
- if(*chr == '_')
- *chr = ' ';
-
- *name = after;
- }
- else
- {
- *name = nullptr;
- }
- }
-}
-
-/**
- * Our Locations come from two places: (1) direct user input and (2) ones
- * guessed by the system, such as from geoclue or timedate1.
- *
- * Since the latter only have a timezone (eg, "America/Chicago") and the
- * former have a descriptive name provided by the end user (eg,
- * "America/Chicago Oklahoma City"), this function tries to make a
- * more human-readable name by using the user-provided name if the guessed
- * timezone matches the last one the user manually clicked on.
- *
- * In the example above, this allows the menuitem for the system-guessed
- * timezone ("America/Chicago") to read "Oklahoma City" after the user clicks
- * on the "Oklahoma City" menuitem.
- */
-gchar*
-get_beautified_timezone_name(const char* timezone_, const char* saved_location)
-{
- gchar* zone;
- gchar* name;
- split_settings_location(timezone_, &zone, &name);
-
- gchar* saved_zone;
- gchar* saved_name;
- split_settings_location(saved_location, &saved_zone, &saved_name);
-
- gchar* rv;
- if (g_strcmp0(zone, saved_zone) == 0)
- {
- rv = saved_name;
- saved_name = nullptr;
- }
- else
- {
- rv = name;
- name = nullptr;
- }
-
- g_free(zone);
- g_free(name);
- g_free(saved_zone);
- g_free(saved_name);
- return rv;
-}
-
-gchar*
-get_timezone_name(const gchar* timezone_, GSettings* settings)
-{
- auto saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S);
- auto rv = get_beautified_timezone_name(timezone_, saved_location);
- g_free(saved_location);
- return rv;
-}
-
-using namespace unity::indicator::datetime;
-
-gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then)
-{
- std::shared_ptr<Clock> clock(new MockClock(DateTime(now)));
- std::shared_ptr<Settings> settings(new LiveSettings);
- DesktopFormatter formatter(clock, settings);
- return g_strdup(formatter.getRelativeFormat(then).c_str());
-}
-