In wizard, if country picker change, only refresh the plugins entries in
[modest] / src / widgets / modest-provider-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 #include "modest-provider-combo-box.h"
31 #include <gtk/gtkliststore.h>
32 #include <gtk/gtkcelllayout.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <glib/gi18n.h>
35 #include <modest-text-utils.h>
36 #include "modest-protocol-registry.h"
37 #include "modest-runtime.h"
38 #include <modest-account-protocol.h>
39
40 #include <stdlib.h>
41 #include <string.h> /* For memcpy() */
42
43 /* Include config.h so that _() works: */
44 #ifdef HAVE_CONFIG_H
45 #include <config.h>
46 #endif
47
48 G_DEFINE_TYPE (ModestProviderComboBox, modest_provider_combo_box, GTK_TYPE_COMBO_BOX);
49
50 #define MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE(o) \
51         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_PROVIDER_COMBO_BOX, ModestProviderComboBoxPrivate))
52
53 typedef struct _ModestProviderComboBoxPrivate ModestProviderComboBoxPrivate;
54
55 struct _ModestProviderComboBoxPrivate
56 {
57         GtkTreeModel *model;
58         GHashTable *enabled_plugin_ids;
59 };
60
61 static void
62 modest_provider_combo_box_get_property (GObject *object, guint property_id,
63                                         GValue *value, GParamSpec *pspec)
64 {
65         switch (property_id) {
66         default:
67                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
68         }
69 }
70
71 static void
72 modest_provider_combo_box_set_property (GObject *object, guint property_id,
73                                         const 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 modest_provider_combo_box_dispose (GObject *object)
83 {
84         if (G_OBJECT_CLASS (modest_provider_combo_box_parent_class)->dispose)
85                 G_OBJECT_CLASS (modest_provider_combo_box_parent_class)->dispose (object);
86 }
87
88 static void
89 modest_provider_combo_box_finalize (GObject *object)
90 {
91         ModestProviderComboBoxPrivate *priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (object);
92
93         g_object_unref (G_OBJECT (priv->model));
94
95         g_hash_table_destroy (priv->enabled_plugin_ids);
96         
97         G_OBJECT_CLASS (modest_provider_combo_box_parent_class)->finalize (object);
98 }
99
100 static void
101 modest_provider_combo_box_class_init (ModestProviderComboBoxClass *klass)
102 {
103         GObjectClass *object_class = G_OBJECT_CLASS (klass);
104
105         g_type_class_add_private (klass, sizeof (ModestProviderComboBoxPrivate));
106
107         object_class->get_property = modest_provider_combo_box_get_property;
108         object_class->set_property = modest_provider_combo_box_set_property;
109         object_class->dispose = modest_provider_combo_box_dispose;
110         object_class->finalize = modest_provider_combo_box_finalize;
111 }
112
113 enum MODEL_COLS {
114         MODEL_COL_ID, /* a string, not an int. */
115         MODEL_COL_NAME,
116         MODEL_COL_ID_TYPE
117 };
118
119
120 /*
121  * strictly, we should sort providers with mcc=0 after the other ones.... but, we don't have
122  * that info here, so ignoring for now.
123  */
124 static gint
125 provider_sort_func (GtkTreeModel *model, GtkTreeIter *iter1, GtkTreeIter *iter2, gpointer user_data)
126 {
127         gchar *prov1, *prov2;
128         gint retval;
129         
130         gtk_tree_model_get (model, iter1, MODEL_COL_NAME, &prov1, -1);
131         gtk_tree_model_get (model, iter2, MODEL_COL_NAME, &prov2, -1);
132
133         if (strcmp (prov1, prov2) == 0) 
134                 retval = 0;
135         else if (strcmp (_("mcen_va_serviceprovider_other"), prov1) == 0)
136                 retval = -1;
137         else if (strcmp (_("mcen_va_serviceprovider_other"), prov2) == 0)
138                 retval = 1;
139         else
140                 retval = modest_text_utils_utf8_strcmp (prov1, prov2, TRUE);
141         
142         g_free (prov1);
143         g_free (prov2);
144
145         return retval;
146 }
147
148 static void
149 modest_provider_combo_box_init (ModestProviderComboBox *self)
150 {
151         ModestProviderComboBoxPrivate *priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (self);
152
153         /* Create a tree model for the combo box,
154          * with a string for the name, and a string for the ID (e.g. "vodafone.it"), and the mcc
155          * This must match our MODEL_COLS enum constants.
156          */
157         priv->model = GTK_TREE_MODEL (gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT));
158         priv->enabled_plugin_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
159                                                           g_free, NULL);
160
161         /* Setup the combo box: */
162         GtkComboBox *combobox = GTK_COMBO_BOX (self);
163         gtk_combo_box_set_model (combobox, priv->model);
164
165         /* Provider column:
166          * The ID model column in not shown in the view. */
167         GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
168         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combobox), renderer, TRUE);
169         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,  "text", MODEL_COL_NAME, NULL);
170         
171         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(priv->model),
172                                               MODEL_COL_NAME, GTK_SORT_ASCENDING);
173         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(priv->model),
174                                           MODEL_COL_NAME,
175                                           (GtkTreeIterCompareFunc)provider_sort_func,
176                                           NULL, NULL);
177 }
178
179 ModestProviderComboBox*
180 modest_provider_combo_box_new (void)
181 {
182         ModestProviderComboBox *self;
183
184         self =  g_object_new (MODEST_TYPE_PROVIDER_COMBO_BOX, NULL);
185         modest_provider_combo_box_set_others_provider (MODEST_PROVIDER_COMBO_BOX (self));
186
187         return self;
188 }
189
190 void
191 modest_provider_combo_box_fill (ModestProviderComboBox *combobox, 
192                                 ModestPresets *presets,
193                                 gint mcc)
194 {       
195         GtkTreeIter other_iter;
196         ModestProviderComboBoxPrivate *priv;
197         GtkListStore *liststore;        
198         GSList *provider_ids_used_already = NULL, *provider_protos, *tmp;
199         gchar ** provider_ids = NULL;
200         gchar ** provider_names;        
201         gchar ** iter_provider_names;
202         gchar ** iter_provider_ids;
203         ModestProtocolRegistry *registry;
204
205         g_return_if_fail (MODEST_IS_PROVIDER_COMBO_BOX(combobox));
206
207         priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (combobox);
208         liststore = GTK_LIST_STORE (priv->model);
209         gtk_list_store_clear (liststore);
210         provider_names = modest_presets_get_providers (presets, mcc, TRUE, &provider_ids);
211
212         iter_provider_names = provider_names;
213         iter_provider_ids = provider_ids;
214
215         g_hash_table_remove_all (priv->enabled_plugin_ids);
216
217         while(iter_provider_names && *iter_provider_names && iter_provider_ids && *iter_provider_ids) {
218                 const gchar* provider_name = *iter_provider_names;
219                 const gchar* provider_id = *iter_provider_ids;
220
221                 /* Prevent duplicate providers: */
222                 if (g_slist_find_custom (provider_ids_used_already, 
223                                          provider_id, (GCompareFunc)strcmp) == NULL) {
224                         /* printf("debug: provider_name=%s\n", provider_name); */
225
226                         /* Add the row: */
227                         GtkTreeIter iter;
228                         gtk_list_store_append (liststore, &iter);
229                         
230                         gtk_list_store_set(liststore, &iter, 
231                                            MODEL_COL_ID, provider_id, 
232                                            MODEL_COL_NAME, provider_name, 
233                                            MODEL_COL_ID_TYPE, MODEST_PROVIDER_COMBO_BOX_ID_PROVIDER,
234                                            -1);
235         
236                         provider_ids_used_already = g_slist_prepend (
237                                 provider_ids_used_already, (gpointer)g_strdup (provider_id));
238                 }
239                 
240                 ++iter_provider_names;
241                 ++iter_provider_ids;
242         }
243         
244         /* Free the result of modest_presets_get_providers()
245          * as specified by its documentation: */
246         g_strfreev (provider_names);
247         g_strfreev (provider_ids);
248
249         /* Add the provider protocols */
250         registry = modest_runtime_get_protocol_registry ();
251         provider_protos = modest_protocol_registry_get_by_tag (registry, 
252                                                                MODEST_PROTOCOL_REGISTRY_PROVIDER_PROTOCOLS);
253         tmp = provider_protos;
254         while (tmp) {
255                 GtkTreeIter iter;
256                 ModestProtocol *proto = MODEST_PROTOCOL (tmp->data);
257
258                 /* only add store protocols, no need to duplicate them */
259                 if (modest_protocol_registry_protocol_type_has_tag (registry, 
260                                                                     modest_protocol_get_type_id (proto),
261                                                                     MODEST_PROTOCOL_REGISTRY_STORE_PROTOCOLS)) {
262                         gboolean supported;
263
264                         supported = modest_account_protocol_is_supported (MODEST_ACCOUNT_PROTOCOL (proto));
265
266                         if (supported) {
267                                 const gchar *name = modest_protocol_get_display_name (proto);
268
269                                 gtk_list_store_append (liststore, &iter);
270                                 gtk_list_store_set (liststore, &iter,
271                                                     MODEL_COL_ID, modest_protocol_get_name (proto),
272                                                     MODEL_COL_NAME, name,
273                                                     MODEL_COL_ID_TYPE, MODEST_PROVIDER_COMBO_BOX_ID_PLUGIN_PROTOCOL,
274                                                     -1);
275                                 g_hash_table_insert (priv->enabled_plugin_ids, g_strdup (modest_protocol_get_name (proto)), NULL);
276                         }
277                 }
278                 tmp = g_slist_next (tmp);
279         }
280         g_slist_free (provider_protos);
281         
282         /* Add the "Other" item: */
283         /* Note that ID 0 means "Other" for us: */
284         gtk_list_store_prepend (liststore, &other_iter);
285         gtk_list_store_set (liststore, &other_iter,
286                             MODEL_COL_ID, 0,
287                             MODEL_COL_NAME, _("mcen_va_serviceprovider_other"),
288                             MODEL_COL_ID_TYPE, MODEST_PROVIDER_COMBO_BOX_ID_OTHER,
289                             -1);
290
291         /* Select the "Other" item: */
292         gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &other_iter);
293         
294         g_slist_foreach (provider_ids_used_already, (GFunc)g_free, NULL);
295         g_slist_free (provider_ids_used_already);
296 }
297
298 void
299 modest_provider_combo_box_refresh (ModestProviderComboBox *self)
300 {       
301         ModestProviderComboBoxPrivate *priv;
302         GtkListStore *liststore;        
303         GSList *provider_protos, *tmp;
304         ModestProtocolRegistry *registry;
305
306         g_return_if_fail (MODEST_IS_PROVIDER_COMBO_BOX(self));
307
308         priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (self);
309         liststore = GTK_LIST_STORE (priv->model);
310         /* Add the provider protocols */
311         registry = modest_runtime_get_protocol_registry ();
312         provider_protos = modest_protocol_registry_get_by_tag (registry, 
313                                                                MODEST_PROTOCOL_REGISTRY_PROVIDER_PROTOCOLS);
314         for (tmp = provider_protos; tmp != NULL; tmp = g_slist_next (tmp)) {
315
316                 GtkTreeIter iter;
317                 ModestProtocol *proto = MODEST_PROTOCOL (tmp->data);
318                 const gchar *name = modest_protocol_get_display_name (proto);
319                 gboolean provider_exists;
320
321                 /* only add store protocols, no need to duplicate them */
322                 if (!modest_protocol_registry_protocol_type_has_tag (registry, 
323                                                                      modest_protocol_get_type_id (proto),
324                                                                      MODEST_PROTOCOL_REGISTRY_STORE_PROTOCOLS))
325                         continue;
326                   
327                 if (modest_protocol_registry_protocol_type_has_tag 
328                     (registry,
329                      modest_protocol_get_type_id (proto),
330                      MODEST_PROTOCOL_REGISTRY_SINGLETON_PROVIDER_PROTOCOLS)) {
331                         /* Check if there's already an account configured with this account type */
332                         if (modest_account_mgr_singleton_protocol_exists (modest_runtime_get_account_mgr (),
333                                                                           modest_protocol_get_type_id (proto)))
334                                 continue;
335                 }
336
337                 provider_exists = g_hash_table_lookup_extended (priv->enabled_plugin_ids, modest_protocol_get_name (proto),
338                                                                 NULL, NULL);
339
340                 if (MODEST_ACCOUNT_PROTOCOL (proto) && 
341                     !modest_account_protocol_is_supported (MODEST_ACCOUNT_PROTOCOL (proto))) {
342
343                         if (provider_exists) {
344                                 GtkTreeIter iter;
345
346                                 if (!gtk_tree_model_get_iter_first (priv->model, &iter))
347                                         continue;
348
349                                 do {
350                                         const gchar *id;
351                                         gtk_tree_model_get (priv->model, &iter, 
352                                                             MODEL_COL_ID, id,
353                                                             -1);
354
355                                         if (g_strcmp0 (id, modest_protocol_get_name (proto)) == 0) {
356                                                 gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
357                                                 break;
358                                         }
359
360                                 } while (gtk_tree_model_iter_next (priv->model, &iter));
361                         }
362
363                         continue;
364                 }
365
366                 if (!provider_exists) {
367                         gtk_list_store_append (liststore, &iter);
368                         gtk_list_store_set (liststore, &iter,
369                                             MODEL_COL_ID, modest_protocol_get_name (proto),
370                                             MODEL_COL_NAME, name,
371                                             MODEL_COL_ID_TYPE, MODEST_PROVIDER_COMBO_BOX_ID_PLUGIN_PROTOCOL,
372                                             -1);
373                         g_hash_table_insert (priv->enabled_plugin_ids, g_strdup (modest_protocol_get_name (proto)), NULL);
374                 }
375         }
376         g_slist_free (provider_protos);
377         
378 }
379
380 /**
381  * Returns the MCC number of the selected provider, 
382  * or NULL if no provider was selected, or "Other" was selected. 
383  */
384 gchar*
385 modest_provider_combo_box_get_active_provider_id (ModestProviderComboBox *combobox)
386 {
387         GtkTreeIter active;
388
389         g_return_val_if_fail (MODEST_IS_PROVIDER_COMBO_BOX(combobox), NULL);
390
391         const gboolean found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &active);
392         if (found) {
393                 ModestProviderComboBoxPrivate *priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (combobox);
394
395                 gchar *id = NULL;
396                 gtk_tree_model_get (priv->model, &active, MODEL_COL_ID, &id, -1);
397                 return g_strdup(id);    
398         }
399
400         return NULL; /* Failed. */
401 }
402
403 gchar*
404 modest_provider_combo_box_get_active_provider_label (ModestProviderComboBox *combobox)
405 {
406         GtkTreeIter active;
407
408         g_return_val_if_fail (MODEST_IS_PROVIDER_COMBO_BOX(combobox), NULL);
409
410         const gboolean found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &active);
411         if (found) {
412                 ModestProviderComboBoxPrivate *priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (combobox);
413
414                 gchar *label = NULL;
415                 gtk_tree_model_get (priv->model, &active, MODEL_COL_NAME, &label, -1);
416                 return g_strdup(label); 
417         }
418
419         return NULL; /* Failed. */
420 }
421
422 void 
423 modest_provider_combo_box_set_others_provider (ModestProviderComboBox *combobox)
424 {
425         GtkTreeModel *model;
426         GtkTreeIter others_iter;
427
428         g_return_if_fail (MODEST_IS_PROVIDER_COMBO_BOX(combobox));
429         
430         model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox));
431         
432         if (gtk_tree_model_get_iter_first (model, &others_iter))
433                 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &others_iter);
434 }
435
436 ModestProviderComboBoxIdType 
437 modest_provider_combo_box_get_active_id_type (ModestProviderComboBox *combobox)
438 {
439         GtkTreeIter active;
440
441         g_return_val_if_fail (MODEST_IS_PROVIDER_COMBO_BOX (combobox), 
442                               MODEST_PROVIDER_COMBO_BOX_ID_OTHER);
443
444         if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &active)) {
445                 ModestProviderComboBoxPrivate *priv = MODEST_PROVIDER_COMBO_BOX_GET_PRIVATE (combobox);
446                 ModestProviderComboBoxIdType id_type;
447
448                 gtk_tree_model_get (priv->model, &active, MODEL_COL_ID_TYPE, &id_type, -1);
449                 return id_type; 
450         } else {
451                 /* Fallback to other */
452                 return MODEST_PROVIDER_COMBO_BOX_ID_OTHER;
453         }
454 }
455