38be4c0e48851ccf82214369d30c3ef54bfa3f14
[modest] / src / hildon2 / modest-country-picker.c
1 /* Copyright (c) 2008, 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-country-picker.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 (ModestCountryPicker, modest_country_picker, HILDON_TYPE_PICKER_BUTTON);
56
57 typedef struct
58 {
59         gint locale_mcc;
60 /*      GtkTreeModel *model; */
61 } ModestCountryPickerPrivate;
62
63 #define MODEST_COUNTRY_PICKER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
64                                                                            MODEST_TYPE_COUNTRY_PICKER, \
65                                                                            ModestCountryPickerPrivate))
66
67 static void
68 modest_country_picker_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 modest_country_picker_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 modest_country_picker_dispose (GObject *object)
89 {
90         if (G_OBJECT_CLASS (modest_country_picker_parent_class)->dispose)
91                 G_OBJECT_CLASS (modest_country_picker_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 modest_country_picker_finalize (GObject *object)
102 {
103         G_OBJECT_CLASS (modest_country_picker_parent_class)->finalize (object);
104 }
105
106 static void
107 modest_country_picker_class_init (ModestCountryPickerClass *klass)
108 {
109         GObjectClass *object_class = G_OBJECT_CLASS (klass);
110
111         g_type_class_add_private (klass, sizeof (ModestCountryPickerPrivate));
112
113         object_class->get_property = modest_country_picker_get_property;
114         object_class->set_property = modest_country_picker_set_property;
115         object_class->dispose = modest_country_picker_dispose;
116         object_class->finalize = modest_country_picker_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 (ModestCountryPicker *self, GtkListStore *liststore)
194 {
195         ModestCountryPickerPrivate *priv = MODEST_COUNTRY_PICKER_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 modest_country_picker_init (ModestCountryPicker *self)
261 {
262         ModestCountryPickerPrivate *priv = MODEST_COUNTRY_PICKER_GET_PRIVATE (self);
263         priv->locale_mcc = 0;
264 }
265
266 void
267 modest_country_picker_load_data(ModestCountryPicker *self)
268 {
269         GtkCellRenderer *renderer;
270         GtkWidget *selector;
271         GtkListStore *model;
272
273         /* Create a tree model for the combo box,
274          * with a string for the name, and an int for the MCC ID.
275          * This must match our MODEL_COLS enum constants.
276          */
277         model = gtk_list_store_new (2,  G_TYPE_STRING, G_TYPE_INT);
278         
279         /* Country column:
280          * The ID model column in not shown in the view. */
281         renderer = gtk_cell_renderer_text_new ();
282         g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
283
284         selector = hildon_touch_selector_new ();
285         hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (self), HILDON_TOUCH_SELECTOR (selector));
286         hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (selector), GTK_TREE_MODEL (model),
287                                              renderer, "text", MODEL_COL_NAME, NULL);
288
289         /* Fill the model with rows: */
290         load_from_file (self, model);
291
292         /* Set this _after_ loading from file, it makes loading faster */
293         hildon_touch_selector_set_model (HILDON_TOUCH_SELECTOR (selector),
294                                          0, GTK_TREE_MODEL (model));
295         g_object_unref (model);
296 }
297
298 ModestCountryPicker*
299 modest_country_picker_new (void)
300 {
301         return g_object_new (MODEST_TYPE_COUNTRY_PICKER, 
302                              "arrangement", HILDON_BUTTON_ARRANGEMENT_VERTICAL,
303                              "size", HILDON_SIZE_AUTO,
304                              NULL);
305 }
306
307 /**
308  * Returns the MCC number of the selected country, or 0 if no country was selected. 
309  */
310 gint
311 modest_country_picker_get_active_country_mcc (ModestCountryPicker *self)
312 {
313         GtkTreeIter active;
314         gboolean found;
315
316         found = hildon_touch_selector_get_selected (hildon_picker_button_get_selector
317                                                     (HILDON_PICKER_BUTTON (self)), 0, &active);
318         if (found) {
319                 gint mcc = 0;
320                 gtk_tree_model_get (hildon_touch_selector_get_model (hildon_picker_button_get_selector
321                                                                      (HILDON_PICKER_BUTTON (self)), 
322                                                                      0), 
323                                     &active, MODEL_COL_MCC, &mcc, -1);
324                 return mcc;     
325         }
326         return 0; /* Failed. */
327 }
328
329
330 /**
331  * Selects the MCC number of the selected country.
332  * Specify 0 to select no country. 
333  */
334 gboolean
335 modest_country_picker_set_active_country_locale (ModestCountryPicker *self)
336 {
337         ModestCountryPickerPrivate *priv = MODEST_COUNTRY_PICKER_GET_PRIVATE (self);
338         GtkWidget *selector;
339         GtkTreeIter iter;
340         gint current_mcc;
341         GtkTreeModel *model;
342
343         selector = GTK_WIDGET (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)));
344         model = hildon_touch_selector_get_model (HILDON_TOUCH_SELECTOR (selector), 0);
345         if (!gtk_tree_model_get_iter_first (model, &iter))
346                 return FALSE;
347         do {
348                 gtk_tree_model_get (model, &iter, MODEL_COL_MCC, &current_mcc, -1);
349                 if (priv->locale_mcc == current_mcc) {
350                         hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector), 0, 
351                                                            &iter, TRUE);
352                         hildon_button_set_value (HILDON_BUTTON (self), 
353                                                  hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector)));
354                         return TRUE;
355                 }
356         } while (gtk_tree_model_iter_next (model, &iter));
357         
358         return FALSE; /* not found */
359 }
360