* Changes in the autotools stuff affecting a lot of platform dependent
[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 #if MODEST_HILDON_API < 2
56 G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, GTK_TYPE_COMBO_BOX);
57 #else
58 G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, HILDON_TYPE_PICKER_BUTTON);
59 #endif
60
61 typedef struct
62 {
63         gint locale_mcc;
64 /*      GtkTreeModel *model; */
65 } ModestEasysetupCountryComboBoxPrivate;
66
67 #define MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
68                                                         MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, \
69                                                         ModestEasysetupCountryComboBoxPrivate))
70
71 static void
72 easysetup_country_combo_box_get_property (GObject *object, guint property_id,
73                                                                                                                         GValue *value, GParamSpec *pspec)
74 {
75         switch (property_id) {
76         default:
77                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
78         }
79 }
80
81 static void
82 easysetup_country_combo_box_set_property (GObject *object, guint property_id,
83                                                                                                                         const GValue *value, GParamSpec *pspec)
84 {
85         switch (property_id) {
86         default:
87                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
88         }
89 }
90
91 static void
92 easysetup_country_combo_box_dispose (GObject *object)
93 {
94         if (G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->dispose)
95                 G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->dispose (object);
96 }
97
98 enum MODEL_COLS {
99         MODEL_COL_NAME = 0, /* string */
100         MODEL_COL_MCC  = 1 /* the 'effective mcc' for this country */
101 };
102
103         
104 static void
105 easysetup_country_combo_box_finalize (GObject *object)
106 {
107         G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->finalize (object);
108 }
109
110 static void
111 easysetup_country_combo_box_class_init (EasysetupCountryComboBoxClass *klass)
112 {
113         GObjectClass *object_class = G_OBJECT_CLASS (klass);
114
115         g_type_class_add_private (klass, sizeof (ModestEasysetupCountryComboBoxPrivate));
116
117         object_class->get_property = easysetup_country_combo_box_get_property;
118         object_class->set_property = easysetup_country_combo_box_set_property;
119         object_class->dispose = easysetup_country_combo_box_dispose;
120         object_class->finalize = easysetup_country_combo_box_finalize;
121 }
122
123
124
125
126 /* cluster mcc's, based on the list
127  * http://en.wikipedia.org/wiki/Mobile_country_code
128  */
129 static int
130 effective_mcc (gint mcc)
131 {
132         switch (mcc) {
133         case 405: return 404; /* india */
134         case 441: return 440; /* japan */       
135         case 235: return 234; /* united kingdom */
136         case 311:
137         case 312:
138         case 313:
139         case 314:
140         case 315:
141         case 316: return 310; /* united states */
142         default:  return mcc;
143         }
144 }
145
146
147 /* each line is of the form:
148    xxx    logical_id
149    
150   NOTE: this function is NOT re-entrant, the out-param country
151   are static strings that should NOT be freed. and will change when
152   calling this function again
153
154   also note, this function will return the "effective mcc", which
155   is the normalized mcc for a country - ie. even if the there
156   are multiple entries for the United States with various mccs,
157   this function will always return 310, even if the real mcc parsed
158   would be 314. see the 'effective_mcc' function above.
159 */
160 static int
161 parse_mcc_mapping_line (const char* line,  char** country)
162 {
163         int i, j;
164         char mcc[4];  /* the mcc code, always 3 bytes*/
165         static char my_country[128];
166
167         if (!line) {
168                 *country = NULL;
169                 return 0;
170         }
171         
172         for (i = 3, j = 0; i < 128; ++i) {
173                 char kar = line[i];
174                 if (kar == '\0')
175                         break;
176                 else if (kar < '_')
177                         continue;
178                 else 
179                         my_country [j++] = kar;
180         }
181         my_country[j] = '\0';
182
183         mcc[0] = line[0];
184         mcc[1] = line[1];
185         mcc[2] = line[2];
186         mcc[3] = '\0';
187         
188         *country = my_country;
189
190         return effective_mcc ((int) strtol ((const char*)mcc, NULL, 10));
191 }
192
193 /** Note that the mcc_mapping file is installed 
194  * by the operator-wizard-settings package.
195  */
196 static void
197 load_from_file (EasysetupCountryComboBox *self, GtkListStore *liststore)
198 {
199         ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self);
200         
201         char line[MAX_LINE_LEN];
202         guint previous_mcc = 0;
203         gchar *territory, *fallback = NULL;
204         gchar *current_locale;
205
206         /* Get the territory specified for the current locale */
207         territory = nl_langinfo (_NL_ADDRESS_COUNTRY_NAME);
208
209         /* Tricky stuff, the translations of the OSSO countries does
210            not always correspond to the country names in locale
211            databases. Add all these cases here. sergio */
212         if (!strcmp (territory, "United Kingdom"))
213                 fallback = g_strdup ("UK");
214
215         current_locale = setlocale (LC_ALL ,NULL);
216
217         FILE *file = modest_maemo_open_mcc_mapping_file ();
218         if (!file) {
219                 g_warning("Could not open mcc_mapping file");
220                 return;
221         }
222
223         while (fgets (line, MAX_LINE_LEN, file) != NULL) { 
224
225                 int mcc;
226                 char *country = NULL;
227                 const gchar *name_translated, *english_translation;
228
229                 mcc = parse_mcc_mapping_line (line, &country);
230                 if (!country || mcc == 0) {
231                         g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line);
232                         continue;
233                 }
234
235                 if (mcc == previous_mcc) {
236                         /* g_warning ("already seen: %s", line); */
237                         continue;
238                 }
239                 previous_mcc = mcc;
240
241                 if (!priv->locale_mcc) {
242                         english_translation = dgettext ("osso-countries", country);
243                         if (!strcmp (english_translation, territory) ||
244                             (fallback && !strcmp (english_translation, fallback)))
245                                 priv->locale_mcc = mcc;
246                 }
247                 name_translated = dgettext ("osso-countries", country);
248                 
249                 /* Add the row to the model: */
250                 GtkTreeIter iter;
251                 gtk_list_store_append (liststore, &iter);
252                 gtk_list_store_set(liststore, &iter, MODEL_COL_MCC, mcc, MODEL_COL_NAME, name_translated, -1);
253         }       
254         fclose (file);
255
256         /* Sort the items: */
257         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (liststore), 
258                                               MODEL_COL_NAME, GTK_SORT_ASCENDING);
259
260         g_free (fallback);
261 }
262
263 static void
264 easysetup_country_combo_box_init (EasysetupCountryComboBox *self)
265 {
266         ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self);
267         priv->locale_mcc = 0;
268 }
269
270 void
271 easysetup_country_combo_box_load_data(EasysetupCountryComboBox *self)
272 {
273         GtkListStore *model;
274
275         /* Create a tree model for the combo box,
276          * with a string for the name, and an int for the MCC ID.
277          * This must match our MODEL_COLS enum constants.
278          */
279         model = gtk_list_store_new (2,  G_TYPE_STRING, G_TYPE_INT);
280         
281         /* Country column:
282          * The ID model column in not shown in the view. */
283         GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
284         g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
285
286 #if MODEST_HILDON_API < 2
287         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (self), renderer, TRUE);
288         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), renderer, 
289                                         "text", MODEL_COL_NAME, NULL);
290 #else
291         GtkWidget *selector = hildon_touch_selector_new ();
292         hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (self), HILDON_TOUCH_SELECTOR (selector));
293         hildon_touch_selector_append_column (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)),
294                                              GTK_TREE_MODEL (model),
295                                              renderer, "text", MODEL_COL_NAME, NULL);
296 #endif
297
298         /* Fill the model with rows: */
299         load_from_file (self, model);
300
301         /* Set this _after_ loading from file, it makes loading faster */
302 #if MODEST_HILDON_API < 2
303         gtk_combo_box_set_model (GTK_COMBO_BOX (self), GTK_TREE_MODEL (model));
304 #else
305         hildon_touch_selector_set_model (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)),
306                                          0, GTK_TREE_MODEL (model));
307 #endif
308 }
309
310 EasysetupCountryComboBox*
311 easysetup_country_combo_box_new (void)
312 {
313 #if MODEST_HILDON_API >= 2
314         return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, 
315                              "arrangement", HILDON_BUTTON_ARRANGEMENT_VERTICAL,
316                              "size", HILDON_SIZE_AUTO,
317                              NULL);
318 #else
319         return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, 
320                              NULL);
321 #endif
322 }
323
324 /**
325  * Returns the MCC number of the selected country, or 0 if no country was selected. 
326  */
327 gint
328 easysetup_country_combo_box_get_active_country_mcc (EasysetupCountryComboBox *self)
329 {
330         GtkTreeIter active;
331         gboolean found;
332
333 #if MODEST_HILDON_API < 2
334         found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self), &active);
335 #else
336         found = hildon_touch_selector_get_selected (hildon_picker_button_get_selector
337                                                     (HILDON_PICKER_BUTTON (self)), 0, &active);
338 #endif
339         if (found) {
340                 gint mcc = 0;
341 #if MODEST_HILDON_API < 2
342                 gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (self)), 
343                                     &active, MODEL_COL_MCC, &mcc, -1);
344 #else
345                 gtk_tree_model_get (hildon_touch_selector_get_model (hildon_picker_button_get_selector
346                                                                      (HILDON_PICKER_BUTTON (self)), 
347                                                                      0), 
348                                     &active, MODEL_COL_MCC, &mcc, -1);
349 #endif
350                 return mcc;     
351         }
352         return 0; /* Failed. */
353 }
354
355
356 /**
357  * Selects the MCC number of the selected country.
358  * Specify 0 to select no country. 
359  */
360 gboolean
361 easysetup_country_combo_box_set_active_country_locale (EasysetupCountryComboBox *self)
362 {
363         ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self);
364         GtkTreeIter iter;
365         gint current_mcc;
366         GtkTreeModel *model;
367
368 #if MODEST_HILDON_API < 2
369         model = gtk_combo_box_get_model (GTK_COMBO_BOX (self));
370         g_message ("HILDON < 2");
371 #else
372         model = hildon_touch_selector_get_model (hildon_picker_button_get_selector 
373                                                  (HILDON_PICKER_BUTTON (self)), 0);
374         g_message ("HILDON >= 2");
375 #endif
376         if (!gtk_tree_model_get_iter_first (model, &iter))
377                 return FALSE;
378         do {
379                 gtk_tree_model_get (model, &iter, MODEL_COL_MCC, &current_mcc, -1);
380                 if (priv->locale_mcc == current_mcc) {
381 #if MODEST_HILDON_API < 2
382                         gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), &iter);
383 #else
384                         hildon_touch_selector_select_iter (hildon_picker_button_get_selector 
385                                                            (HILDON_PICKER_BUTTON (self)), 0, 
386                                                            &iter, TRUE);
387 #endif
388                         return TRUE;
389                 }
390         } while (gtk_tree_model_iter_next (model, &iter));
391         
392         return FALSE; /* not found */
393 }
394