X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmaemo%2Feasysetup%2Fmodest-easysetup-country-combo-box.c;h=0f7b3dc2ea805efdd31fd2f461af83062cc52e39;hp=21c36d36101ce7e33d56bbbd0936df14d6f11e8e;hb=f6d386ff93e929092ba105385a29d760aeba9ff7;hpb=f35a7a6e63502d159ced905e065084ca6c010f14 diff --git a/src/maemo/easysetup/modest-easysetup-country-combo-box.c b/src/maemo/easysetup/modest-easysetup-country-combo-box.c index 21c36d3..0f7b3dc 100644 --- a/src/maemo/easysetup/modest-easysetup-country-combo-box.c +++ b/src/maemo/easysetup/modest-easysetup-country-combo-box.c @@ -33,6 +33,7 @@ #include +#include "../modest-maemo-utils.h" #include "modest-easysetup-country-combo-box.h" #include #include @@ -40,7 +41,8 @@ #include #include /* For memcpy() */ - +#include +#include #include /* For dgettext(). */ /* Include config.h so that _() works: */ @@ -48,17 +50,23 @@ #include #endif -G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, GTK_TYPE_COMBO_BOX); - -#define COUNTRY_COMBO_BOX_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), EASYSETUP_TYPE_COUNTRY_COMBO_BOX, EasysetupCountryComboBoxPrivate)) +#define MAX_LINE_LEN 128 /* max length of a line in MCC file */ -typedef struct _EasysetupCountryComboBoxPrivate EasysetupCountryComboBoxPrivate; +#if MODEST_HILDON_API < 2 +G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, GTK_TYPE_COMBO_BOX); +#else +G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, HILDON_TYPE_PICKER_BUTTON); +#endif -struct _EasysetupCountryComboBoxPrivate +typedef struct { - GtkTreeModel *model; -}; + gint locale_mcc; +/* GtkTreeModel *model; */ +} ModestEasysetupCountryComboBoxPrivate; + +#define MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, \ + ModestEasysetupCountryComboBoxPrivate)) static void easysetup_country_combo_box_get_property (GObject *object, guint property_id, @@ -89,29 +97,13 @@ easysetup_country_combo_box_dispose (GObject *object) enum MODEL_COLS { MODEL_COL_NAME = 0, /* string */ - MODEL_COL_IDS = 1 /* A GSList* of guints. */ + MODEL_COL_MCC = 1 /* the 'effective mcc' for this country */ }; -static gboolean -on_model_foreach_release (GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - GSList *list = NULL; - gtk_tree_model_get (model, iter, MODEL_COL_IDS, &list, -1); - if (list) - g_slist_free (list); - - return FALSE; /* keep walking. */ -} static void easysetup_country_combo_box_finalize (GObject *object) { - EasysetupCountryComboBoxPrivate *priv = COUNTRY_COMBO_BOX_GET_PRIVATE (object); - - gtk_tree_model_foreach (priv->model, on_model_foreach_release, NULL); - g_object_unref (G_OBJECT (priv->model)); - G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->finalize (object); } @@ -120,7 +112,7 @@ easysetup_country_combo_box_class_init (EasysetupCountryComboBoxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (EasysetupCountryComboBoxPrivate)); + g_type_class_add_private (klass, sizeof (ModestEasysetupCountryComboBoxPrivate)); object_class->get_property = easysetup_country_combo_box_get_property; object_class->set_property = easysetup_country_combo_box_set_property; @@ -128,286 +120,275 @@ easysetup_country_combo_box_class_init (EasysetupCountryComboBoxClass *klass) object_class->finalize = easysetup_country_combo_box_finalize; } -/** id and country must be freed. + + + +/* cluster mcc's, based on the list + * http://en.wikipedia.org/wiki/Mobile_country_code */ -static void parse_mcc_mapping_line (const char* line, char** id, char** country) +static int +effective_mcc (gint mcc) { - /* Initialize output parameters: */ - *id = NULL; - *country = NULL; - - g_assert(line); - - const gboolean is_valid_utf8 = g_utf8_validate (line, -1, NULL); - if(!is_valid_utf8) { - g_warning("UTF8 validation failed."); - return; + switch (mcc) { + case 405: return 404; /* india */ + case 441: return 440; /* japan */ + case 235: return 234; /* united kingdom */ + case 311: + case 312: + case 313: + case 314: + case 315: + case 316: return 310; /* united states */ + default: return mcc; + } +} + + +/* each line is of the form: + xxx logical_id + + NOTE: this function is NOT re-entrant, the out-param country + are static strings that should NOT be freed. and will change when + calling this function again + + also note, this function will return the "effective mcc", which + is the normalized mcc for a country - ie. even if the there + are multiple entries for the United States with various mccs, + this function will always return 310, even if the real mcc parsed + would be 314. see the 'effective_mcc' function above. +*/ +static int +parse_mcc_mapping_line (const char* line, char** country) +{ + int i, j; + char mcc[4]; /* the mcc code, always 3 bytes*/ + static char my_country[128]; + + if (!line) { + *country = NULL; + return 0; } - /* Look at each character, to find the whitespace between the ID and name: */ - char* result_id = NULL; - char* result_country = NULL; - - const char* p = line; - const char* p_start_of_country = NULL; - while (p && *p) - { - p = g_utf8_next_char(p); - gunichar ch = g_utf8_get_char(p); - if (g_unichar_isspace(ch)) { /* Note: This checks for any whitespace, not just space. */ - if(!result_id) { - /* The text before this must be the ID: */ - const int length = p - line; - result_id = g_malloc (length + 1); /* 1 for null-termination. */ - memcpy(result_id, line, length); - result_id[length] = 0; /* Null-termination. */ - } - else if(p_start_of_country) - { - /* This whitespace is probably the newline after the country. */ - - /* The text after the whitespace, after the ID, must be the country: */ - int length = p - p_start_of_country; - result_country = g_malloc(length + 1); - memcpy(result_country, p_start_of_country, length); - result_country[length] = 0; /* Null-termination. */ - break; - } - } - else if(result_id && !p_start_of_country) { - p_start_of_country = p; - } + for (i = 3, j = 0; i < 128; ++i) { + char kar = line[i]; + if (kar == '\0') + break; + else if (kar < '_') + continue; + else + my_country [j++] = kar; } + my_country[j] = '\0'; + + mcc[0] = line[0]; + mcc[1] = line[1]; + mcc[2] = line[2]; + mcc[3] = '\0'; - *id = result_id; - *country = result_country; + *country = my_country; + + return effective_mcc ((int) strtol ((const char*)mcc, NULL, 10)); } /** Note that the mcc_mapping file is installed * by the operator-wizard-settings package. */ -static void load_from_file (EasysetupCountryComboBox *self) +static void +load_from_file (EasysetupCountryComboBox *self, GtkListStore *liststore) { - EasysetupCountryComboBoxPrivate *priv = COUNTRY_COMBO_BOX_GET_PRIVATE (self); - - /* Load the file one line at a time: */ -#ifndef MODEST_HILDON_VERSION_0 - const gchar* filepath = PROVIDER_DATA_DIR "/mcc_mapping"; -#else - /* this is the official version, in the 'operator-wizard-settings' package */ - const gchar* filepath = "/usr/share/operator-wizard/mcc_mapping"; -#endif /*MODEST_HILDON_VERSION_0*/ - printf ("DEBUG: %s: filepath=%s\n", __FUNCTION__, filepath); - FILE *file = fopen(filepath, "r"); - if (!file) - { - const gchar* filepath_hack = HACK_TOP_SRCDIR "src/maemo/easysetup/mcc_mapping"; - g_warning ("Could not locate the official mcc_mapping countries list file from %s, " - "so attempting to load it instead from %s", filepath, filepath_hack); - file = fopen(filepath_hack, "r"); - } + ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self); + char line[MAX_LINE_LEN]; + guint previous_mcc = 0; + gchar *territory, *fallback = NULL; + gchar *current_locale; + + /* Get the territory specified for the current locale */ + territory = nl_langinfo (_NL_ADDRESS_COUNTRY_NAME); + + /* Tricky stuff, the translations of the OSSO countries does + not always correspond to the country names in locale + databases. Add all these cases here. sergio */ + if (!strcmp (territory, "United Kingdom")) + fallback = g_strdup ("UK"); + + current_locale = setlocale (LC_ALL ,NULL); + + FILE *file = modest_maemo_open_mcc_mapping_file (); if (!file) { g_warning("Could not open mcc_mapping file"); return; } - GtkListStore *liststore = GTK_LIST_STORE (priv->model); - - /* We use the getline() GNU extension, - * because it reads per line, which simplifies our code, - * and it doesn't require us to hard-code a buffer length. - * TODO: Could we make this faster? - */ - int len = 0; - char *line = NULL; - guint previous_id = 0; - gchar* previous_country = NULL; - GSList *list = NULL; - while (getline (&line, &len, file) > 0) { /* getline will realloc line if necessary. */ - /* printf ("DBEUG: len=%d, line: %s\n", len, line); */ - - char *id_str = NULL; + while (fgets (line, MAX_LINE_LEN, file) != NULL) { + + int mcc; char *country = NULL; - parse_mcc_mapping_line (line, &id_str, &country); - /* printf("DEBUG: parsed: id=%s, country=%s\n", id_str, country); */ - - if(id_str && country) { - - if (previous_country) { - /* printf (" debug: storing id=%d for country=%s\n", previous_id, previous_country); */ - list = g_slist_append (list, GUINT_TO_POINTER (previous_id)); - } - - /* Group multiple MMC IDs for the same country together: - * This assumes that they are in sequence. - * We don't know why some countries, such as the USA, have several MMC IDs. - * If they are regions in the country, and we need to show them separately, then - * we would need to have that information in the file to distinguish them. - */ - if (!previous_country || - (previous_country && strcmp (previous_country, country) != 0)) { - - /* Get the translation for the country name: - * Note that the osso_countries_1.0 translation domain files are installed - * by the operator-wizard-settings package. */ - /* For post-Bora, there is a separate (meta)package osso-countries-l10n-mr0 */ - - /* Note: Even when the untranslated names are different, there may still be - * duplicate translated names. They would be translation bugs. - */ - const gchar *name_translated = dgettext ("osso-countries", previous_country); - if(!name_translated) - name_translated = previous_country; - - /* Add the row to the model: */ - GtkTreeIter iter; - gtk_list_store_append (liststore, &iter); - gtk_list_store_set(liststore, &iter, MODEL_COL_IDS, list, MODEL_COL_NAME, name_translated, -1); - - /* The list will be freed in our finalize(). */ - list = NULL; - } - - g_free (previous_country); - previous_country = g_strdup (country); - - const guint id = (guint)g_ascii_strtod(id_str, NULL); /* Note that this parses locale-independent text. */ - previous_id = id; + const gchar *name_translated, *english_translation; + + mcc = parse_mcc_mapping_line (line, &country); + if (!country || mcc == 0) { + g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line); + continue; } - - g_free (id_str); - g_free (country); - } - - /* Deal with the last country: */ - const gchar *name_translated = dgettext ("osso-countries", previous_country); - if(!name_translated) - name_translated = previous_country; - - /* Add the row to the model: */ - GtkTreeIter iter; - gtk_list_store_append (liststore, &iter); - gtk_list_store_set(liststore, &iter, MODEL_COL_IDS, list, MODEL_COL_NAME, name_translated, -1); - - if (list) { - g_slist_free (list); - list = NULL; - } - - if (line) - free (line); + if (mcc == previous_mcc) { + /* g_warning ("already seen: %s", line); */ + continue; + } + previous_mcc = mcc; + + if (!priv->locale_mcc) { + english_translation = dgettext ("osso-countries", country); + if (!strcmp (english_translation, territory) || + (fallback && !strcmp (english_translation, fallback))) + priv->locale_mcc = mcc; + } + name_translated = dgettext ("osso-countries", country); + /* Add the row to the model: */ + GtkTreeIter iter; + gtk_list_store_append (liststore, &iter); + gtk_list_store_set(liststore, &iter, MODEL_COL_MCC, mcc, MODEL_COL_NAME, name_translated, -1); + } fclose (file); + + /* Sort the items: */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (liststore), + MODEL_COL_NAME, GTK_SORT_ASCENDING); + + g_free (fallback); } static void easysetup_country_combo_box_init (EasysetupCountryComboBox *self) { - EasysetupCountryComboBoxPrivate *priv = COUNTRY_COMBO_BOX_GET_PRIVATE (self); + ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self); + priv->locale_mcc = 0; +} + +void +easysetup_country_combo_box_load_data(EasysetupCountryComboBox *self) +{ + GtkListStore *model; /* Create a tree model for the combo box, * with a string for the name, and an int for the MCC ID. * This must match our MODEL_COLS enum constants. */ - priv->model = GTK_TREE_MODEL (gtk_list_store_new (2, - G_TYPE_STRING, - G_TYPE_POINTER)); - - /* Setup the combo box: */ - GtkComboBox *combobox = GTK_COMBO_BOX (self); - gtk_combo_box_set_model (combobox, priv->model); - + model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); + /* Country column: * The ID model column in not shown in the view. */ GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combobox), renderer, TRUE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, - "text", MODEL_COL_NAME, NULL); - + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + +#if MODEST_HILDON_API < 2 + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (self), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), renderer, + "text", MODEL_COL_NAME, NULL); +#else + GtkWidget *selector = hildon_touch_selector_new (); + hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (self), HILDON_TOUCH_SELECTOR (selector)); + hildon_touch_selector_append_column (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)), + GTK_TREE_MODEL (model), + renderer, "text", MODEL_COL_NAME, NULL); +#endif + /* Fill the model with rows: */ - load_from_file (self); + load_from_file (self, model); + + /* Set this _after_ loading from file, it makes loading faster */ +#if MODEST_HILDON_API < 2 + gtk_combo_box_set_model (GTK_COMBO_BOX (self), GTK_TREE_MODEL (model)); +#else + hildon_touch_selector_set_model (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)), + 0, GTK_TREE_MODEL (model)); +#endif } EasysetupCountryComboBox* easysetup_country_combo_box_new (void) { - return g_object_new (EASYSETUP_TYPE_COUNTRY_COMBO_BOX, NULL); +#if MODEST_HILDON_API >= 2 + return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, + "arrangement", HILDON_BUTTON_ARRANGEMENT_VERTICAL, + "size", HILDON_SIZE_AUTO, + NULL); +#else + return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, + NULL); +#endif } /** * Returns the MCC number of the selected country, or 0 if no country was selected. - * The list should not be freed. */ -GSList * -easysetup_country_combo_box_get_active_country_ids (EasysetupCountryComboBox *self) +gint +easysetup_country_combo_box_get_active_country_mcc (EasysetupCountryComboBox *self) { GtkTreeIter active; - const gboolean found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self), &active); - if (found) { - EasysetupCountryComboBoxPrivate *priv = COUNTRY_COMBO_BOX_GET_PRIVATE (self); + gboolean found; - GSList *list = NULL; - gtk_tree_model_get (priv->model, &active, MODEL_COL_IDS, &list, -1); - return list; +#if MODEST_HILDON_API < 2 + found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self), &active); +#else + found = hildon_touch_selector_get_selected (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), 0, &active); +#endif + if (found) { + gint mcc = 0; +#if MODEST_HILDON_API < 2 + gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (self)), + &active, MODEL_COL_MCC, &mcc, -1); +#else + gtk_tree_model_get (hildon_touch_selector_get_model (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), + 0), + &active, MODEL_COL_MCC, &mcc, -1); +#endif + return mcc; } - - return NULL; /* Failed. */ + return 0; /* Failed. */ } -/* This allows us to pass more than one piece of data to the signal handler, - * and get a result: */ -typedef struct -{ - EasysetupCountryComboBox* self; - guint mcc_id; - gboolean found; -} ForEachData; - -static gboolean -on_model_foreach_select_id(GtkTreeModel *model, - GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) -{ - ForEachData *state = (ForEachData*)(user_data); - - /* Select the item if it has the matching ID: */ - GSList *list = NULL; - gtk_tree_model_get (model, iter, MODEL_COL_IDS, &list, -1); - if(list && g_slist_find (list, GUINT_TO_POINTER (state->mcc_id))) { - gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->self), iter); - - state->found = TRUE; - return TRUE; /* Stop walking the tree. */ - } - - return FALSE; /* Keep walking the tree. */ -} - /** * Selects the MCC number of the selected country. * Specify 0 to select no country. */ gboolean -easysetup_country_combo_box_set_active_country_id (EasysetupCountryComboBox *self, guint mcc_id) +easysetup_country_combo_box_set_active_country_locale (EasysetupCountryComboBox *self) { - EasysetupCountryComboBoxPrivate *priv = COUNTRY_COMBO_BOX_GET_PRIVATE (self); - - /* Create a state instance so we can send two items of data to the signal handler: */ - ForEachData *state = g_new0 (ForEachData, 1); - state->self = self; - state->mcc_id = mcc_id; - state->found = FALSE; - - /* Look at each item, and select the one with the correct ID: */ - gtk_tree_model_foreach (priv->model, &on_model_foreach_select_id, state); + ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self); + GtkTreeIter iter; + gint current_mcc; + GtkTreeModel *model; - const gboolean result = state->found; - - /* Free the state instance: */ - g_free(state); +#if MODEST_HILDON_API < 2 + model = gtk_combo_box_get_model (GTK_COMBO_BOX (self)); + g_message ("HILDON < 2"); +#else + model = hildon_touch_selector_get_model (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), 0); + g_message ("HILDON >= 2"); +#endif + if (!gtk_tree_model_get_iter_first (model, &iter)) + return FALSE; + do { + gtk_tree_model_get (model, &iter, MODEL_COL_MCC, ¤t_mcc, -1); + if (priv->locale_mcc == current_mcc) { +#if MODEST_HILDON_API < 2 + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), &iter); +#else + hildon_touch_selector_select_iter (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), 0, + &iter, TRUE); +#endif + return TRUE; + } + } while (gtk_tree_model_iter_next (model, &iter)); - return result; + return FALSE; /* not found */ }