diff options
Diffstat (limited to 'addressbook/gui/widgets/eab-gui-util.c')
-rw-r--r-- | addressbook/gui/widgets/eab-gui-util.c | 517 |
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; +} |