* Fixes NB#86700 select the default region in the accounts wizard dialog by default
[modest] / src / maemo / easysetup / modest-easysetup-country-combo-box.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #ifndef _GNU_SOURCE
31 #define _GNU_SOURCE /* So we can use the getline() function, which is a convenient GNU extension. */
32 #endif
33
34 #include <stdio.h>
35
36 #include "../modest-maemo-utils.h"
37 #include "modest-easysetup-country-combo-box.h"
38 #include <gtk/gtkliststore.h>
39 #include <gtk/gtkcelllayout.h>
40 #include <gtk/gtkcellrenderertext.h>
41
42 #include <stdlib.h>
43 #include <string.h> /* For memcpy() */
44 #include <langinfo.h>
45 #include <locale.h>
46 #include <libintl.h> /* For dgettext(). */
47
48 /* Include config.h so that _() works: */
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif
52
53 #define MAX_LINE_LEN 128 /* max length of a line in MCC file */
54
55 G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, GTK_TYPE_COMBO_BOX);
56
57 typedef struct
58 {
59         gint locale_mcc;
60 /*      GtkTreeModel *model; */
61 } ModestEasysetupCountryComboBoxPrivate;
62
63 #define MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
64                                                         MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, \
65                                                         ModestEasysetupCountryComboBoxPrivate))
66
67 static void
68 easysetup_country_combo_box_get_property (GObject *object, guint property_id,
69                                                                                                                         GValue *value, GParamSpec *pspec)
70 {
71         switch (property_id) {
72         default:
73                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
74         }
75 }
76
77 static void
78 easysetup_country_combo_box_set_property (GObject *object, guint property_id,
79                                                                                                                         const GValue *value, GParamSpec *pspec)
80 {
81         switch (property_id) {
82         default:
83                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
84         }
85 }
86
87 static void
88 easysetup_country_combo_box_dispose (GObject *object)
89 {
90         if (G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->dispose)
91                 G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->dispose (object);
92 }
93
94 enum MODEL_COLS {
95         MODEL_COL_NAME = 0, /* string */
96         MODEL_COL_MCC  = 1 /* the 'effective mcc' for this country */
97 };
98
99         
100 static void
101 easysetup_country_combo_box_finalize (GObject *object)
102 {
103         G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->finalize (object);
104 }
105
106 static void
107 easysetup_country_combo_box_class_init (EasysetupCountryComboBoxClass *klass)
108 {
109         GObjectClass *object_class = G_OBJECT_CLASS (klass);
110
111         g_type_class_add_private (klass, sizeof (ModestEasysetupCountryComboBoxPrivate));
112
113         object_class->get_property = easysetup_country_combo_box_get_property;
114         object_class->set_property = easysetup_country_combo_box_set_property;
115         object_class->dispose = easysetup_country_combo_box_dispose;
116         object_class->finalize = easysetup_country_combo_box_finalize;
117 }
118
119
120
121
122 /* cluster mcc's, based on the list
123  * http://en.wikipedia.org/wiki/Mobile_country_code
124  */
125 static int
126 effective_mcc (gint mcc)
127 {
128         switch (mcc) {
129         case 405: return 404; /* india */
130         case 441: return 440; /* japan */       
131         case 235: return 234; /* united kingdom */
132         case 311:
133         case 312:
134         case 313:
135         case 314:
136         case 315:
137         case 316: return 310; /* united states */
138         default:  return mcc;
139         }
140 }
141
142
143 /* each line is of the form:
144    xxx    logical_id
145    
146   NOTE: this function is NOT re-entrant, the out-param country
147   are static strings that should NOT be freed. and will change when
148   calling this function again
149
150   also note, this function will return the "effective mcc", which
151   is the normalized mcc for a country - ie. even if the there
152   are multiple entries for the United States with various mccs,
153   this function will always return 310, even if the real mcc parsed
154   would be 314. see the 'effective_mcc' function above.
155 */
156 static int
157 parse_mcc_mapping_line (const char* line,  char** country)
158 {
159         int i, j;
160         char mcc[4];  /* the mcc code, always 3 bytes*/
161         static char my_country[128];
162
163         if (!line) {
164                 *country = NULL;
165                 return 0;
166         }
167         
168         for (i = 3, j = 0; i < 128; ++i) {
169                 char kar = line[i];
170                 if (kar == '\0')
171                         break;
172                 else if (kar < '_')
173                         continue;
174                 else 
175                         my_country [j++] = kar;
176         }
177         my_country[j] = '\0';
178
179         mcc[0] = line[0];
180         mcc[1] = line[1];
181         mcc[2] = line[2];
182         mcc[3] = '\0';
183         
184         *country = my_country;
185
186         return effective_mcc ((int) strtol ((const char*)mcc, NULL, 10));
187 }
188
189 /** Note that the mcc_mapping file is installed 
190  * by the operator-wizard-settings package.
191  */
192 static void
193 load_from_file (EasysetupCountryComboBox *self, GtkListStore *liststore)
194 {
195         ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self);
196         
197         char line[MAX_LINE_LEN];
198         guint previous_mcc = 0;
199         gchar *territory, *fallback = NULL;
200         gchar *current_locale;
201
202         /* Get the territory specified for the current locale */
203         territory = nl_langinfo (_NL_ADDRESS_COUNTRY_NAME);
204
205         /* Tricky stuff, the translations of the OSSO countries does
206            not always correspond to the country names in locale
207            databases. Add all these cases here. sergio */
208         if (!strcmp (territory, "United Kingdom"))
209                 fallback = g_strdup ("UK");
210
211         current_locale = setlocale (LC_ALL ,NULL);
212
213         FILE *file = modest_maemo_open_mcc_mapping_file ();
214         if (!file) {
215                 g_warning("Could not open mcc_mapping file");
216                 return;
217         }
218
219         while (fgets (line, MAX_LINE_LEN, file) != NULL) { 
220
221                 int mcc;
222                 char *country = NULL;
223                 const gchar *name_translated, *english_translation;
224
225                 mcc = parse_mcc_mapping_line (line, &country);
226                 if (!country || mcc == 0) {
227                         g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line);
228                         continue;
229                 }
230
231                 if (mcc == previous_mcc) {
232                         /* g_warning ("already seen: %s", line); */
233                         continue;
234                 }
235                 previous_mcc = mcc;
236
237                 if (!priv->locale_mcc) {
238                         english_translation = dgettext ("osso-countries", country);
239                         if (!strcmp (english_translation, territory) ||
240                             (fallback && !strcmp (english_translation, fallback)))
241                                 priv->locale_mcc = mcc;
242                 }
243                 name_translated = dgettext ("osso-countries", country);
244                 
245                 /* Add the row to the model: */
246                 GtkTreeIter iter;
247                 gtk_list_store_append (liststore, &iter);
248                 gtk_list_store_set(liststore, &iter, MODEL_COL_MCC, mcc, MODEL_COL_NAME, name_translated, -1);
249         }       
250         fclose (file);
251
252         /* Sort the items: */
253         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (liststore), 
254                                               MODEL_COL_NAME, GTK_SORT_ASCENDING);
255
256         g_free (fallback);
257 }
258
259 static void
260 easysetup_country_combo_box_init (EasysetupCountryComboBox *self)
261 {
262         ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self);
263         priv->locale_mcc = 0;
264 }
265
266 void
267 easysetup_country_combo_box_load_data(EasysetupCountryComboBox *self)
268 {
269         GtkListStore *model;
270
271         /* Create a tree model for the combo box,
272          * with a string for the name, and an int for the MCC ID.
273          * This must match our MODEL_COLS enum constants.
274          */
275         model = gtk_list_store_new (2,  G_TYPE_STRING, G_TYPE_INT);
276         
277         /* Setup the combo box: */
278         GtkComboBox *combobox = GTK_COMBO_BOX (self);
279         
280         /* Country column:
281          * The ID model column in not shown in the view. */
282         GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
283         g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
284         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combobox), renderer, TRUE);
285         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, 
286         "text", MODEL_COL_NAME, NULL);
287
288         /* Fill the model with rows: */
289         load_from_file (self, model);
290
291         /* Set this _after_ loading from file, it makes loading faster */
292         gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (model));
293 }
294
295 EasysetupCountryComboBox*
296 easysetup_country_combo_box_new (void)
297 {
298         return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, NULL);
299 }
300
301 /**
302  * Returns the MCC number of the selected country, or 0 if no country was selected. 
303  */
304 gint
305 easysetup_country_combo_box_get_active_country_mcc (EasysetupCountryComboBox *self)
306 {
307         GtkTreeIter active;
308         const gboolean found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self), &active);
309         if (found) {
310                 gint mcc = 0;
311                 gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (self)), 
312                                     &active, MODEL_COL_MCC, &mcc, -1);
313                 return mcc;     
314         }
315         return 0; /* Failed. */
316 }
317
318
319 /**
320  * Selects the MCC number of the selected country.
321  * Specify 0 to select no country. 
322  */
323 gboolean
324 easysetup_country_combo_box_set_active_country_locale (EasysetupCountryComboBox *self)
325 {
326         ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self);
327         GtkTreeIter iter;
328         gint current_mcc;
329         GtkTreeModel *model;
330
331         model = gtk_combo_box_get_model (GTK_COMBO_BOX (self));
332         if (!gtk_tree_model_get_iter_first (model, &iter))
333                 return FALSE;
334         do {
335                 gtk_tree_model_get (model, &iter, MODEL_COL_MCC, &current_mcc, -1);
336                 if (priv->locale_mcc == current_mcc) {
337                         gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), &iter);
338                         return TRUE;
339                 }
340         } while (gtk_tree_model_iter_next (model, &iter));
341         
342         return FALSE; /* not found */
343 }
344