aboutsummaryrefslogtreecommitdiffstats
path: root/addressbook/gui/widgets/eab-gui-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'addressbook/gui/widgets/eab-gui-util.c')
-rw-r--r--addressbook/gui/widgets/eab-gui-util.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/addressbook/gui/widgets/eab-gui-util.c b/addressbook/gui/widgets/eab-gui-util.c
index ae6467bf87..d1fc8fd254 100644
--- a/addressbook/gui/widgets/eab-gui-util.c
+++ b/addressbook/gui/widgets/eab-gui-util.c
@@ -15,6 +15,7 @@
*
* Authors:
* Chris Toshok <toshok@ximian.com>
+ * Dan Vratil <dvratil@redhat.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
@@ -28,6 +29,8 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
+#include <locale.h>
+#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
@@ -48,6 +51,39 @@
/* we link to camel for decoding quoted printable email addresses */
#include <camel/camel.h>
+/* Template tags for address format localization */
+#define ADDRESS_REALNAME "%n"
+#define ADDRESS_REALNAME_UPPER "%N"
+#define ADDRESS_COMPANY "%m"
+#define ADDRESS_COMPANY_UPPER "%M"
+#define ADDRESS_POBOX "%p"
+#define ADDRESS_STREET "%s"
+#define ADDRESS_STREET_UPPER "%S"
+#define ADDRESS_ZIPCODE "%z"
+#define ADDRESS_LOCATION "%l"
+#define ADDRESS_LOCATION_UPPER "%L"
+#define ADDRESS_REGION "%r"
+#define ADDRESS_REGION_UPPER "%R"
+#define ADDRESS_CONDCOMMA "%," /* Conditional comma is removed when a surrounding tag is evaluated to zero */
+#define ADDRESS_CONDWHITE "%w" /* Conditional whitespace is removed when a surrounding tag is evaluated to zero */
+#define ADDRESS_COND_PURGEEMPTY "%0" /* Purge empty has following syntax: %0(...) and is removed when no tag within () is evaluated non-zero */
+
+/* Fallback formats */
+#define ADDRESS_DEFAULT_FORMAT "%0(%n\n)%0(%m\n)%0(%s\n)%0(PO BOX %p\n)%0(%l%w%r)%,%z"
+#define ADDRESS_DEFAULT_COUNTRY_POSITION "below"
+
+enum {
+ LOCALES_LANGUAGE = 0,
+ LOCALES_COUNTRY = 1
+};
+
+typedef enum {
+ ADDRESS_FORMAT_HOME = 0,
+ ADDRESS_FORMAT_BUSINESS = 1
+} AddressFormat;
+
+
+
void
eab_error_dialog (EAlertSink *alert_sink, const gchar *msg, const GError *error)
{
@@ -547,3 +583,484 @@ eab_parse_qp_email_to_html (const gchar *string)
return value;
}
+
+
+/*
+ * eab_format_address helper function
+ *
+ * Splits locales from en_US to array "en","us",NULL. When
+ * locales don't have the second part (for example "C"),
+ * the output array is "c",NULL
+ */
+static gchar **
+get_locales (void)
+{
+ gchar *locale, *l_locale;
+ gchar *dot;
+ gchar **split;
+
+ locale = g_strdup (setlocale (LC_ADDRESS, NULL));
+ if (!locale)
+ return NULL;
+
+ l_locale = g_utf8_strdown (locale, -1);
+ g_free (locale);
+
+ dot = strchr (l_locale, '.');
+ if (dot != NULL) {
+ gchar *p = l_locale;
+ l_locale = g_strndup (l_locale, dot - l_locale);
+ g_free (p);
+ }
+
+ split = g_strsplit (l_locale, "_", 2);
+
+ g_free (l_locale);
+ return split;
+
+}
+
+static gchar *
+get_locales_str (void)
+{
+ gchar *ret;
+ gchar **loc = get_locales ();
+
+ if (!loc)
+ return g_strdup ("C");
+
+ if (!loc[0] ||
+ (loc[0] && !loc[1])) /* We don't care about language now, we need a country at first! */
+ ret = g_strdup ("C");
+ else if (loc[0] && loc[1]) {
+ if (*loc[0])
+ ret = g_strconcat (loc[LOCALES_COUNTRY], "_", loc[LOCALES_LANGUAGE], NULL);
+ else
+ ret = g_strdup (loc[LOCALES_COUNTRY]);
+ }
+
+ g_strfreev (loc);
+ return ret;
+}
+
+
+/*
+ * Reads countrytransl.map file, which contains map of localized
+ * country names and their ISO codes and tries to find matching record
+ * for given country. The search is case insensitive.
+ * When no record is found (country is probably in untranslated language), returns
+ * code of local computer country (from locales)
+ */
+static gchar *
+country_to_ISO (const gchar *country)
+{
+ FILE *file = fopen (EVOLUTION_RULEDIR "/countrytransl.map", "r");
+ gchar buffer[100];
+ gint length = 100;
+ gchar **pair;
+ gchar *res;
+ gchar *l_country = g_utf8_strdown (country, -1);
+
+ if (!file) {
+ gchar **loc;
+ g_warning ("%s: Failed to open countrytransl.map. Check your installation.", G_STRFUNC);
+ loc = get_locales ();
+ res = g_strdup (loc ? loc[LOCALES_COUNTRY] : NULL);
+ g_free (l_country);
+ g_strfreev (loc);
+ return res;
+ }
+
+ while (fgets (buffer, length, file) != NULL) {
+ gchar *low;
+ pair = g_strsplit (buffer, "\t", 2);
+
+ if (pair[0]) {
+ low = g_utf8_strdown (pair[0], -1);
+ if (g_utf8_collate (low, l_country) == 0) {
+ gchar *ret = g_strdup (pair[1]);
+ gchar *pos;
+ /* Remove trailing newline character */
+ if ((pos = g_strrstr (ret, "\n")) != NULL)
+ pos[0] = '\0';
+ fclose (file);
+ g_strfreev (pair);
+ g_free (low);
+ g_free (l_country);
+ return ret;
+ }
+ }
+
+ g_strfreev (pair);
+ g_free (low);
+ }
+
+ /* If we get here, then no match was found in the map file and we
+ fallback to local system locales */
+ fclose (file);
+
+ pair = get_locales ();
+ res = g_strdup (pair ? pair[LOCALES_COUNTRY] : NULL);
+ g_strfreev (pair);
+ g_free (l_country);
+ return res;
+}
+
+
+/*
+ * Tries to find given key in "country_LANGUAGE" group. When fails to find
+ * such group, then fallbacks to "country" group. When such group does not
+ * exist either, NULL is returned
+ */
+static gchar *
+get_key_file_locale_string (GKeyFile *key_file,
+ const gchar *key,
+ const gchar *locale)
+{
+ gchar *result;
+ gchar *group;
+
+ g_return_val_if_fail (locale, NULL);
+
+ /* Default locale is in "country_lang", but such group may not exist. In such case use group "country" */
+ if (g_key_file_has_group (key_file, locale))
+ group = g_strdup (locale);
+ else {
+ gchar **locales = g_strsplit (locale, "_", 0);
+ group = g_strdup (locales[LOCALES_COUNTRY]);
+ g_strfreev (locales);
+ }
+
+ /* When group or key does not exist, returns NULL and fallback string will be used */
+ result = g_key_file_get_string (key_file, group, key, NULL);
+ g_free (group);
+ return result;
+}
+
+static void
+get_address_format (AddressFormat address_format,
+ const gchar *locale,
+ gchar **format,
+ gchar **country_position)
+{
+ GKeyFile *key_file;
+ GError *error;
+ gchar *loc;
+ const gchar *addr_key, *country_key;
+
+ if (address_format == ADDRESS_FORMAT_HOME) {
+ addr_key = "AddressFormat";
+ country_key = "CountryPosition";
+ } else if (address_format == ADDRESS_FORMAT_BUSINESS) {
+ addr_key = "BusinessAddressFormat";
+ country_key = "BusinessCountryPosition";
+ } else {
+ return;
+ }
+
+ if (locale == NULL)
+ loc = get_locales_str ();
+ else
+ loc = g_strdup (locale);
+
+ error = NULL;
+ key_file = g_key_file_new ();
+ g_key_file_load_from_file (key_file, EVOLUTION_RULEDIR "/address_formats.dat", 0, &error);
+ if (error) {
+ g_warning ("%s: Failed to load address_formats.dat file: %s", G_STRFUNC, error->message);
+ *format = g_strdup (ADDRESS_DEFAULT_FORMAT);
+ *country_position = g_strdup (ADDRESS_DEFAULT_COUNTRY_POSITION);
+ g_key_file_free (key_file);
+ g_free (loc);
+ g_error_free (error);
+ return;
+ }
+
+ if (format) {
+ if (*format)
+ g_free (*format);
+ *format = get_key_file_locale_string (key_file, addr_key, loc);
+ if (!*format && address_format == ADDRESS_FORMAT_HOME) {
+ *format = g_strdup (ADDRESS_DEFAULT_FORMAT);
+ } else if (!*format && address_format == ADDRESS_FORMAT_BUSINESS)
+ get_address_format (ADDRESS_FORMAT_HOME, loc, format, NULL);
+ }
+
+ if (country_position) {
+ if (*country_position)
+ g_free (*country_position);
+ *country_position = get_key_file_locale_string (key_file, country_key, loc);
+ if (!*country_position && address_format == ADDRESS_FORMAT_HOME)
+ *country_position = g_strdup (ADDRESS_DEFAULT_COUNTRY_POSITION);
+ else if (!*country_position && address_format == ADDRESS_FORMAT_BUSINESS)
+ get_address_format (ADDRESS_FORMAT_HOME, loc, NULL, country_position);
+ }
+
+ g_free (loc);
+ g_key_file_free (key_file);
+}
+
+static const gchar *
+find_balanced_bracket (const gchar *str)
+{
+ gint balance_counter = 0;
+ gint i = 0;
+
+ do {
+ if (str[i] == '(')
+ balance_counter++;
+
+ if (str[i] == ')')
+ balance_counter--;
+
+ i++;
+
+ } while ((balance_counter > 0) && (str[i]));
+
+ if (balance_counter > 0)
+ return str;
+
+ return str + i;
+}
+
+static GString *
+string_append_upper (GString *str, const gchar *c)
+{
+ gchar *up_c;
+
+ g_return_val_if_fail (str, NULL);
+
+ if (!c || !*c)
+ return str;
+
+ up_c = g_utf8_strup (c, -1);
+ str = g_string_append (str, up_c);
+ g_free (up_c);
+
+ return str;
+}
+
+static gboolean
+parse_address_template_section (const gchar *format,
+ const gchar *realname,
+ const gchar *org_name,
+ EContactAddress *address,
+ gchar **result)
+
+{
+ const gchar *pos, *old_pos;
+ gboolean ret = FALSE; /* Indicates, wheter at least something was replaced */
+
+ GString *res = g_string_new ("");
+
+ pos = format;
+ old_pos = pos;
+ while ((pos = strchr (pos, '%')) != NULL) {
+
+ if (old_pos != pos)
+ g_string_append_len (res, old_pos, pos - old_pos);
+
+ switch (pos[1]) {
+ case 'n':
+ g_string_append (res, realname);
+ ret = TRUE;
+ pos += 2; /* Jump behind the modifier, see what's next */
+ break;
+ case 'N':
+ if (realname && *realname) {
+ string_append_upper (res, realname);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'm':
+ if (org_name && *org_name) {
+ g_string_append (res, org_name);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'M':
+ if (org_name && *org_name) {
+ string_append_upper (res, org_name);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'p':
+ if (address->po && *(address->po)) {
+ g_string_append (res, address->po);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 's':
+ if (address->street && *(address->street)) {
+ g_string_append (res, address->street);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'S':
+ if (address->street && *(address->street)) {
+ string_append_upper (res, address->street);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'z':
+ if (address->code && *(address->code)) {
+ g_string_append (res, address->code);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'l':
+ if (address->locality && *(address->locality)) {
+ g_string_append (res, address->locality);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'L':
+ if (address->locality && *(address->locality)) {
+ string_append_upper (res, address->locality);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'r':
+ if (address->region && *(address->region)) {
+ g_string_append (res, address->region);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case 'R':
+ if (address->region && *(address->region)) {
+ string_append_upper (res, address->region);
+ ret = TRUE;
+ }
+ pos += 2;
+ break;
+ case ',':
+ if (ret && (pos >= format + 2) && /* If there's something before %, */
+ (g_ascii_strcasecmp (pos - 2, "\n") != 0) && /* And if it is not a newline */
+ (g_ascii_strcasecmp (pos - 2, "%w") != 0)) /* Nor whitespace */
+ g_string_append (res, ", ");
+ pos += 2;
+ break;
+ case 'w':
+ if (ret && (pos >= format + 2) &&
+ (g_ascii_strcasecmp (pos - 2, "\n") != 0) &&
+ (g_ascii_strcasecmp (pos - 1, " ") != 0))
+ g_string_append (res, " ");
+ pos += 2;
+ break;
+ case '0': {
+ const gchar *bpos1, *bpos2;
+ gchar *inner;
+ gchar *ires;
+ gboolean replaced;
+
+ bpos1 = pos + 2;
+ bpos2 = find_balanced_bracket (bpos1);
+
+ inner = g_strndup (bpos1 + 1, bpos2 - bpos1 - 2); /* Get inner content of the %0(...) */
+ replaced = parse_address_template_section (inner, realname, org_name, address, &ires);
+ if (replaced)
+ g_string_append (res, ires);
+
+ g_free (ires);
+ g_free (inner);
+
+ ret = replaced;
+ pos += (bpos2 - bpos1 + 2);
+ } break;
+ }
+
+ old_pos = pos;
+ }
+ g_string_append (res, old_pos);
+
+ *result = g_strdup (res->str);
+
+ g_string_free (res, TRUE);
+
+ return ret;
+}
+
+gchar *
+eab_format_address (EContact *contact,
+ EContactField address_type)
+{
+ gchar *result;
+ gchar *format = NULL;
+ gchar *country_position = NULL;
+ gchar *locale;
+ EContactAddress *addr = e_contact_get (contact, address_type);
+
+ if (!addr)
+ return NULL;
+
+ if (!addr->po && !addr->ext && !addr->street && !addr->locality && !addr->region &&
+ !addr->code && !addr->country) {
+ e_contact_address_free (addr);
+ return NULL;
+ }
+
+ if (addr->country) {
+ gchar *cntry = country_to_ISO (addr->country);
+ gchar **loc = get_locales ();
+ locale = g_strconcat (loc ? loc[LOCALES_LANGUAGE] : "C", "_", cntry, NULL);
+ g_strfreev (loc);
+ g_free (cntry);
+ } else
+ locale = get_locales_str ();
+
+ if (address_type == E_CONTACT_ADDRESS_HOME)
+ get_address_format (ADDRESS_FORMAT_HOME, locale, &format, &country_position);
+ else if (address_type == E_CONTACT_ADDRESS_WORK)
+ get_address_format (ADDRESS_FORMAT_BUSINESS, locale, &format, &country_position);
+ else {
+ e_contact_address_free (addr);
+ g_free (locale);
+ return NULL;
+ }
+
+ /* Expand all the variables in format.
+ Don't display organization in home address */
+ parse_address_template_section (format,
+ e_contact_get_const (contact, E_CONTACT_FULL_NAME),
+ (address_type == E_CONTACT_ADDRESS_WORK) ? e_contact_get_const (contact, E_CONTACT_ORG): NULL,
+ addr,
+ &result);
+
+ /* Add the country line. In some countries, the address can be located above the
+ rest of the address */
+ if (addr->country && country_position) {
+ gchar *country_upper = g_utf8_strup (addr->country, -1);
+ gchar *p = result;
+ if (g_strcmp0 (country_position, "BELOW") == 0) {
+ result = g_strconcat (p, "\n\n", country_upper, NULL);
+ g_free (p);
+ } else if (g_strcmp0 (country_position, "below") == 0) {
+ result = g_strconcat (p, "\n\n", addr->country, NULL);
+ g_free (p);
+ } else if (g_strcmp0 (country_position, "ABOVE") == 0) {
+ result = g_strconcat (country_upper, "\n\n", p, NULL);
+ g_free (p);
+ } else if (g_strcmp0 (country_position, "above") == 0) {
+ result = g_strconcat (addr->country, "\n\n", p, NULL);
+ g_free (p);
+ }
+ g_free (country_upper);
+ }
+
+ e_contact_address_free (addr);
+ g_free (locale);
+ g_free (format);
+ g_free (country_position);
+
+ return result;
+}