Fixes NB#63571
[modest] / src / maemo / modest-platform.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 <config.h>
31 #include <glib/gi18n.h>
32 #include <modest-platform.h>
33 #include <modest-runtime.h>
34 #include <modest-main-window.h>
35 #include <modest-header-view.h>
36 #include "maemo/modest-maemo-global-settings-dialog.h"
37 #include "modest-widget-memory.h"
38 #include <modest-hildon-includes.h>
39 #include <modest-maemo-utils.h>
40 #include <dbus_api/modest-dbus-callbacks.h>
41 #include <maemo/modest-osso-autosave-callbacks.h>
42 #include <libosso.h>
43 #include <tny-maemo-conic-device.h>
44 #include <tny-simple-list.h>
45 #include <tny-folder.h>
46 #include <tny-camel-imap-store-account.h>
47 #include <tny-camel-pop-store-account.h>
48 #include <gtk/gtkicontheme.h>
49 #include <gtk/gtkmenuitem.h>
50 #include <gtk/gtkmain.h>
51 #include <modest-text-utils.h>
52 #include "modest-tny-folder.h"
53 #include "modest-tny-account.h"
54 #include <string.h>
55 #include <libgnomevfs/gnome-vfs-mime-utils.h>
56
57 #ifdef MODEST_HAVE_ABOOK
58 #include <libosso-abook/osso-abook.h>
59 #endif /*MODEST_HAVE_ABOOK*/
60
61 #ifdef MODEST_HAVE_LIBALARM
62 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
63 #endif /*MODEST_HAVE_LIBALARM*/
64
65
66 #define HILDON_OSSO_URI_ACTION "uri-action"
67 #define URI_ACTION_COPY "copy:"
68
69 static void     
70 on_modest_conf_update_interval_changed (ModestConf* self, 
71                                         const gchar *key, 
72                                         ModestConfEvent event,
73                                         ModestConfNotificationId id, 
74                                         gpointer user_data)
75 {
76         g_return_if_fail (key);
77         
78         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
79                 const guint update_interval_minutes = 
80                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
81                 modest_platform_set_update_interval (update_interval_minutes);
82         }
83 }
84
85
86
87 static gboolean
88 check_required_files (void)
89 {
90         FILE *mcc_file = modest_maemo_open_mcc_mapping_file ();
91         if (!mcc_file) {
92                 g_printerr ("modest: check for mcc file failed\n");
93                 return FALSE;
94         } else 
95                 fclose (mcc_file);
96         
97         if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
98             access(MODEST_MAEMO_PROVIDER_DATA_FILE, R_OK) != 0) {
99                 g_printerr ("modest: cannot find providers data\n");
100                 return FALSE;
101         }
102         
103         return TRUE;
104 }
105
106
107 /* the gpointer here is the osso_context. */
108 gboolean
109 modest_platform_init (int argc, char *argv[])
110 {
111         osso_context_t *osso_context;
112         
113         osso_hw_state_t hw_state = { 0 };
114         DBusConnection *con;    
115         GSList *acc_names;
116         
117         if (!check_required_files ()) {
118                 g_printerr ("modest: missing required files\n");
119                 return FALSE;
120         }
121         
122         osso_context =  osso_initialize(PACKAGE,PACKAGE_VERSION,
123                                         FALSE, NULL);   
124         if (!osso_context) {
125                 g_printerr ("modest: failed to acquire osso context\n");
126                 return FALSE;
127         }
128         modest_maemo_utils_set_osso_context (osso_context);
129
130         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
131                 g_printerr ("modest: could not get dbus connection\n");
132                 return FALSE;
133         }
134
135         /* Add a D-Bus handler to be used when the main osso-rpc 
136          * D-Bus handler has not handled something.
137          * We use this for D-Bus methods that need to use more complex types 
138          * than osso-rpc supports. 
139          */
140         if (!dbus_connection_add_filter (con,
141                                          modest_dbus_req_filter,
142                                          NULL,
143                                          NULL)) {
144
145                 g_printerr ("modest: Could not add D-Bus filter\n");
146                 return FALSE;
147         }
148
149         /* Register our simple D-Bus callbacks, via the osso API: */
150         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
151                                MODEST_DBUS_SERVICE, 
152                                MODEST_DBUS_OBJECT, 
153                                MODEST_DBUS_IFACE,
154                                modest_dbus_req_handler, NULL /* user_data */);
155         if (result != OSSO_OK) {
156                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
157                 return FALSE;
158         }
159
160         /* Register hardware event dbus callback: */
161         hw_state.shutdown_ind = TRUE;
162         osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
163
164         /* Register osso auto-save callbacks: */
165         result = osso_application_set_autosave_cb (osso_context, 
166                 modest_on_osso_application_autosave, NULL /* user_data */);
167         if (result != OSSO_OK) {
168                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
169                 return FALSE;
170         }
171         
172
173         /* Make sure that the update interval is changed whenever its gconf key 
174          * is changed */
175         /* CAUTION: we're not using here the
176            modest_conf_listen_to_namespace because we know that there
177            are other parts of Modest listening for this namespace, so
178            we'll receive the notifications anyway. We basically do not
179            use it because there is no easy way to do the
180            modest_conf_forget_namespace */
181         ModestConf *conf = modest_runtime_get_conf ();
182         g_signal_connect (G_OBJECT(conf),
183                           "key_changed",
184                           G_CALLBACK (on_modest_conf_update_interval_changed), 
185                           NULL);
186
187         /* only force the setting of the default interval, if there are actually
188          * any accounts */
189         acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
190         if (acc_names) {
191                 /* Get the initial update interval from gconf: */
192                 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
193                                                        MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
194                 modest_account_mgr_free_account_names (acc_names);
195         }
196
197         
198 #ifdef MODEST_HAVE_ABOOK
199         /* initialize the addressbook */
200         if (!osso_abook_init (&argc, &argv, osso_context)) {
201                 g_printerr ("modest: failed to initialized addressbook\n");
202                 return FALSE;
203         }
204 #endif /*MODEST_HAVE_ABOOK*/
205
206         return TRUE;
207 }
208
209 gboolean
210 modest_platform_uninit (void)
211 {
212         osso_context_t *osso_context =
213                 modest_maemo_utils_get_osso_context ();
214         if (osso_context)
215                 osso_deinitialize (osso_context);
216
217         return TRUE;
218 }
219
220
221
222
223 TnyDevice*
224 modest_platform_get_new_device (void)
225 {
226         return TNY_DEVICE (tny_maemo_conic_device_new ());
227 }
228
229 gchar*
230 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
231                                     gchar **effective_mime_type)
232 {
233         GString *mime_str = NULL;
234         gchar *icon_name  = NULL;
235         gchar **icons, **cursor;
236         
237         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
238                 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
239         else {
240                 mime_str = g_string_new (mime_type);
241                 g_string_ascii_down (mime_str);
242         }
243
244         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
245         for (cursor = icons; cursor; ++cursor) {
246                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
247                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
248                         icon_name = g_strdup ("qgn_list_messagin");
249                         break;
250                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
251                         icon_name = g_strdup (*cursor);
252                         break;
253                 }
254         }
255         g_strfreev (icons);
256
257         if (effective_mime_type)
258                 *effective_mime_type = g_string_free (mime_str, FALSE);
259         else
260                 g_string_free (mime_str, TRUE);
261
262         return icon_name;
263 }
264
265
266 static gboolean
267 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
268 {
269         GError *err = NULL;
270         gboolean result;
271
272         g_return_val_if_fail (uri, FALSE);
273         
274         result = hildon_uri_open (uri, action, &err);
275         if (!result) {
276                 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
277                             uri, action,  err && err->message ? err->message : "unknown error");
278                 if (err)
279                         g_error_free (err);
280         }
281         return result;
282 }
283
284
285
286 gboolean 
287 modest_platform_activate_uri (const gchar *uri)
288 {
289         HildonURIAction *action;
290         gboolean result = FALSE;
291         GSList *actions, *iter = NULL;
292         
293         g_return_val_if_fail (uri, FALSE);
294         if (!uri)
295                 return FALSE;
296         
297         actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
298         
299         for (iter = actions; iter; iter = g_slist_next (iter)) {
300                 action = (HildonURIAction*) iter->data;
301                 if (action && strcmp (hildon_uri_action_get_service (action),
302                                       "com.nokia.modest") == 0) {
303                         result = checked_hildon_uri_open (uri, action);
304                         break;
305                 }
306         }
307         
308         /* if we could not open it with email, try something else */
309         if (!result)
310                 result = checked_hildon_uri_open (uri, NULL);   
311
312         if (!result) {
313                 ModestWindow *parent =
314                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
315                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
316                                                 _("mcen_ib_unsupported_link"));
317         }
318         
319         return result;
320 }
321
322 gboolean 
323 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
324 {
325         gint result = 0;
326         DBusConnection *con;
327         gchar *uri_path = NULL;
328
329         uri_path = g_strconcat ("file://", path, NULL); 
330         con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
331         
332         if (mime_type)
333                 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
334         if (result != 1)
335                 result = hildon_mime_open_file (con, uri_path);
336         if (result != 1)
337                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
338         
339         return result != 1;
340 }
341
342 typedef struct  {
343         GSList *actions;
344         gchar  *uri;
345 } ModestPlatformPopupInfo;
346
347 static gboolean
348 delete_uri_popup (GtkWidget *menu,
349                   GdkEvent *event,
350                   gpointer userdata)
351 {
352         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
353
354         g_free (popup_info->uri);
355         hildon_uri_free_actions (popup_info->actions);
356
357         return FALSE;
358 }
359
360 static void
361 activate_uri_popup_item (GtkMenuItem *menu_item,
362                          gpointer userdata)
363 {
364         GSList *node;
365         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
366         const gchar* action_name;
367
368         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
369         if (!action_name) {
370                 g_printerr ("modest: no action name defined\n");
371                 return;
372         }
373
374         /* special handling for the copy menu item -- copy the uri to the clipboard */
375         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
376         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
377                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
378                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
379
380                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
381                         action_name += strlen ("mailto:");
382                 
383                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
384                 return; /* we're done */
385         }
386         
387         /* now, the real uri-actions... */
388         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
389                 HildonURIAction *action = (HildonURIAction *) node->data;
390                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
391                         if (!checked_hildon_uri_open (popup_info->uri, action)) {
392                                 ModestWindow *parent =
393                                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
394                                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
395                                                                 _("mcen_ib_unsupported_link"));
396                         }
397                         break;
398                 }
399         }
400 }
401
402 gboolean 
403 modest_platform_show_uri_popup (const gchar *uri)
404 {
405         GSList *actions_list;
406
407         if (uri == NULL)
408                 return FALSE;
409
410         actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
411         if (actions_list != NULL) {
412                 GSList *node;
413                 GtkWidget *menu = gtk_menu_new ();
414                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
415
416                 popup_info->actions = actions_list;
417                 popup_info->uri = g_strdup (uri);
418               
419                 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
420                         GtkWidget *menu_item;
421                         const gchar *action_name;
422                         const gchar *translation_domain;
423                         HildonURIAction *action = (HildonURIAction *) node->data;
424                         action_name = hildon_uri_action_get_name (action);
425                         translation_domain = hildon_uri_action_get_translation_domain (action);
426                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
427                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
428                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
429                                           popup_info);
430                                                                   
431                         if (hildon_uri_is_default_action (action, NULL)) {
432                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
433                         } else {
434                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
435                         }
436                         gtk_widget_show (menu_item);
437                 }
438
439                 /* always add the copy item */
440                 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
441                 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
442                                         g_strconcat (URI_ACTION_COPY, uri, NULL),
443                                         g_free);
444                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
445                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
446                 gtk_widget_show (menu_item);
447
448                 
449                 /* and what to do when the link is deleted */
450                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
451                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
452                                                   
453         } else {
454                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
455         }
456
457         return TRUE;
458 }
459
460
461 GdkPixbuf*
462 modest_platform_get_icon (const gchar *name)
463 {
464         GError *err = NULL;
465         GdkPixbuf* pixbuf = NULL;
466         GtkIconTheme *current_theme = NULL;
467
468         g_return_val_if_fail (name, NULL);
469
470         /* strlen == 0 is not really an error; it just
471          * means the icon is not available
472          */
473         if (!name || strlen(name) == 0)
474                 return NULL;
475         
476 #if 0 /* do we still need this? */
477         if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
478                 pixbuf = gdk_pixbuf_new_from_file (name, &err);
479                 if (!pixbuf) {
480                         g_printerr ("modest: error loading icon '%s': %s\n",
481                                     name, err->message);
482                         g_error_free (err);
483                         return NULL;
484                 }
485                 return pixbuf;
486         }
487 #endif /* */
488         current_theme = gtk_icon_theme_get_default ();
489         pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
490                                            GTK_ICON_LOOKUP_NO_SVG,
491                                            &err);
492         if (!pixbuf) {
493                 g_printerr ("modest: error loading theme icon '%s': %s\n",
494                             name, err->message);
495                 g_error_free (err);
496         } 
497         return pixbuf;
498 }
499
500 const gchar*
501 modest_platform_get_app_name (void)
502 {
503         return _("mcen_ap_name");
504 }
505
506 static void 
507 entry_insert_text (GtkEditable *editable,
508                    const gchar *text,
509                    gint         length,
510                    gint        *position,
511                    gpointer     data)
512 {
513         gchar *chars;
514         gint chars_length;
515
516         chars = gtk_editable_get_chars (editable, 0, -1);
517         chars_length = g_utf8_strlen (chars, -1);
518
519         /* Show WID-INF036 */
520         if (chars_length >= 20) {
521                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
522                                                  _CS("ckdg_ib_maximum_characters_reached"));
523         } else {
524                 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
525                         /* Show an error */
526                         gchar *tmp, *msg;
527                         
528                         tmp = g_strndup (folder_name_forbidden_chars, 
529                                          FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
530                         msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
531                         hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), 
532                                                          NULL, msg);
533                         g_free (msg);
534                         g_free (tmp);
535                 } else {        
536                         /* Write the text in the entry if it's valid */
537                         g_signal_handlers_block_by_func (editable,
538                                                          (gpointer) entry_insert_text, data);
539                         gtk_editable_insert_text (editable, text, length, position);
540                         g_signal_handlers_unblock_by_func (editable,
541                                                            (gpointer) entry_insert_text, data);
542                 }
543         }
544         /* Do not allow further processing */
545         g_signal_stop_emission_by_name (editable, "insert_text");
546 }
547
548 static void
549 entry_changed (GtkEditable *editable,
550                gpointer     user_data)
551 {
552         gchar *chars;
553         GtkWidget *ok_button;
554         GList *buttons;
555
556         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
557         ok_button = GTK_WIDGET (buttons->next->data);
558         
559         chars = gtk_editable_get_chars (editable, 0, -1);
560         g_return_if_fail (chars != NULL);
561
562         
563         if (g_utf8_strlen (chars,-1) >= 21)
564                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
565                                                  _CS("ckdg_ib_maximum_characters_reached"));
566         else
567                 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
568                 
569         /* Free */
570         g_list_free (buttons);
571         g_free (chars);
572 }
573
574 static guint
575 checked_hildon_sort_dialog_add_sort_key (HildonSortDialog *dialog, const gchar* key, guint max)
576 {
577         gint sort_key;
578         
579         g_return_val_if_fail (dialog && HILDON_IS_SORT_DIALOG(dialog), 0);
580         g_return_val_if_fail (key, 0);
581         
582         sort_key = hildon_sort_dialog_add_sort_key (dialog, key);
583         if (sort_key < 0 || sort_key >= max) {
584                 g_warning ("%s: out of range (%d) for %s", __FUNCTION__, sort_key, key);
585                 return 0;
586         } else
587                 return (guint)sort_key; 
588 }
589
590
591 static void
592 launch_sort_headers_dialog (GtkWindow *parent_window,
593                             HildonSortDialog *dialog)
594 {
595         ModestHeaderView *header_view = NULL;
596         GList *cols = NULL;
597         GtkSortType sort_type;
598         gint sort_key;
599         gint default_key = 0;
600         gint result;
601         gboolean outgoing = FALSE;
602         gint current_sort_colid = -1;
603         GtkSortType current_sort_type;
604         gint attachments_sort_id;
605         gint priority_sort_id;
606         GtkTreeSortable *sortable;
607         
608         /* Get header window */
609         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
610                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
611                                                                                       MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
612         }
613         if (!header_view)
614                 return;
615         
616         /* Add sorting keys */
617         cols = modest_header_view_get_columns (header_view);
618         if (cols == NULL) 
619                 return;
620 #define SORT_ID_NUM 6
621         int sort_model_ids[SORT_ID_NUM];
622         int sort_ids[SORT_ID_NUM];
623
624         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
625                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
626
627         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"),
628                                                             SORT_ID_NUM - 1);
629         if (outgoing) {
630                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
631                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
632         } else {
633                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
634                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
635         }
636
637         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"),
638                                                             SORT_ID_NUM - 1);
639         if (outgoing) {
640                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
641                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
642         } else {
643                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
644                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
645         }
646         default_key = sort_key;
647
648         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"),
649                                                             SORT_ID_NUM -1);
650         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
651         if (outgoing)
652                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
653         else
654                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
655
656         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"),
657                                                             SORT_ID_NUM - 1);
658         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
659         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
660         attachments_sort_id = sort_key;
661
662         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"),
663                                                             SORT_ID_NUM - 1);
664         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
665         sort_ids[sort_key] = 0;
666
667         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"),
668                                                             SORT_ID_NUM - 1);
669         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
670         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY_MASK;
671         priority_sort_id = sort_key;
672         
673         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
674                                       (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
675         /* Launch dialogs */
676         if (!gtk_tree_sortable_get_sort_column_id (sortable,
677                                                    &current_sort_colid, &current_sort_type)) {
678                 hildon_sort_dialog_set_sort_key (dialog, default_key);
679                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
680         } else {
681                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
682                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
683                         gpointer flags_sort_type_pointer;
684                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
685                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
686                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
687                         else
688                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
689                 } else {
690                         gint current_sort_keyid = 0;
691                         while (current_sort_keyid < 6) {
692                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
693                                         break;
694                                 else 
695                                         current_sort_keyid++;
696                         }
697                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
698                 }
699         }
700
701         result = gtk_dialog_run (GTK_DIALOG (dialog));
702         if (result == GTK_RESPONSE_OK) {
703                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
704                 if (sort_key < 0 || sort_key > SORT_ID_NUM -1) {
705                         g_warning ("%s: out of range (%d)", __FUNCTION__, sort_key);
706                         sort_key = 0;
707                 }
708
709                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
710                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
711                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
712                                            GINT_TO_POINTER (sort_ids[sort_key]));
713                         /* This is a hack to make it resort rows always when flag fields are
714                          * selected. If we do not do this, changing sort field from priority to
715                          * attachments does not work */
716                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
717                 } else {
718                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
719                                                                  sort_model_ids[sort_key]);
720                 }
721
722                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
723                 gtk_tree_sortable_sort_column_changed (sortable);
724         }
725
726         modest_widget_memory_save (modest_runtime_get_conf (),
727                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
728         
729         /* free */
730         g_list_free(cols);      
731 }
732
733
734
735 static void
736 on_response (GtkDialog *dialog,
737              gint response,
738              gpointer user_data)
739 {
740         GList *child_vbox, *child_hbox;
741         GtkWidget *hbox, *entry;
742         TnyFolderStore *parent;
743         const gchar *new_name;
744         gboolean exists;
745
746         if (response != GTK_RESPONSE_ACCEPT)
747                 return;
748         
749         /* Get entry */
750         child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
751         hbox = child_vbox->data;
752         child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
753         entry = child_hbox->next->data;
754         
755         parent = TNY_FOLDER_STORE (user_data);
756         new_name = gtk_entry_get_text (GTK_ENTRY (entry));
757         exists = FALSE;
758         
759         /* Look for another folder with the same name */
760         if (modest_tny_folder_has_subfolder_with_name (parent, 
761                                                        new_name,
762                                                        TRUE)) {
763                 exists = TRUE;
764         }
765         
766         if (!exists) {
767                 if (TNY_IS_ACCOUNT (parent) &&
768                     modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
769                     modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
770                                                                          new_name)) {
771                         exists = TRUE;
772                 }
773         }
774         
775         if (exists) {
776                 
777                 /* Show an error */
778                 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), 
779                                                 NULL, _CS("ckdg_ib_folder_already_exists"));
780                 /* Select the text */
781                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
782                 gtk_widget_grab_focus (entry);
783                 /* Do not close the dialog */
784                 g_signal_stop_emission_by_name (dialog, "response");
785         }
786 }
787
788
789
790 static gint
791 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
792                                         TnyFolderStore *parent,
793                                         const gchar *dialog_title,
794                                         const gchar *label_text,
795                                         const gchar *suggested_name,
796                                         gchar **folder_name)
797 {
798         GtkWidget *accept_btn = NULL; 
799         GtkWidget *dialog, *entry, *label, *hbox;
800         GList *buttons = NULL;
801         gint result;
802
803         /* Ask the user for the folder name */
804         dialog = gtk_dialog_new_with_buttons (dialog_title,
805                                               parent_window,
806                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
807                                               _("mcen_bd_dialog_ok"),
808                                               GTK_RESPONSE_ACCEPT,
809                                               _("mcen_bd_dialog_cancel"),
810                                               GTK_RESPONSE_REJECT,
811                                               NULL);
812
813         /* Add accept button (with unsensitive handler) */
814         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
815         accept_btn = GTK_WIDGET (buttons->next->data);
816         /* Create label and entry */
817         label = gtk_label_new (label_text);
818         /* TODO: check that the suggested name does not exist */
819         /* We set 21 as maximum because we want to show WID-INF036
820            when the user inputs more that 20 */
821         entry = gtk_entry_new_with_max_length (21);
822         if (suggested_name)
823                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
824         else
825                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
826         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
827
828         /* Connect to the response method to avoid closing the dialog
829            when an invalid name is selected*/
830         g_signal_connect (dialog,
831                           "response",
832                           G_CALLBACK (on_response),
833                           parent);
834
835         /* Track entry changes */
836         g_signal_connect (entry,
837                           "insert-text",
838                           G_CALLBACK (entry_insert_text),
839                           dialog);
840         g_signal_connect (entry,
841                           "changed",
842                           G_CALLBACK (entry_changed),
843                           dialog);
844
845         /* Create the hbox */
846         hbox = gtk_hbox_new (FALSE, 12);
847         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
848         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
849
850         /* Add hbox to dialog */
851         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
852                             hbox, FALSE, FALSE, 0);
853         
854         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
855         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
856         
857         result = gtk_dialog_run (GTK_DIALOG(dialog));
858         if (result == GTK_RESPONSE_ACCEPT)
859                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
860
861         gtk_widget_destroy (dialog);
862
863         while (gtk_events_pending ())
864                 gtk_main_iteration ();
865
866         return result;
867 }
868
869 gint
870 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
871                                        TnyFolderStore *parent_folder,
872                                        gchar *suggested_name,
873                                        gchar **folder_name)
874 {
875         gchar *real_suggested_name = NULL;
876         gint result;
877
878         if(suggested_name == NULL)
879         {
880                 const gchar *default_name = _("mcen_ia_default_folder_name");
881                 unsigned int i;
882                 gchar num_str[3];
883
884                 for(i = 0; i < 100; ++ i) {
885                         gboolean exists = FALSE;
886
887                         sprintf(num_str, "%.2u", i);
888
889                         if (i == 0)
890                                 real_suggested_name = g_strdup (default_name);
891                         else
892                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
893                                                                        num_str);
894                         exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
895                                                                             real_suggested_name,
896                                                                             TRUE);
897
898                         if (!exists)
899                                 break;
900
901                         g_free (real_suggested_name);
902                 }
903
904                 /* Didn't find a free number */
905                 if (i == 100)
906                         real_suggested_name = g_strdup (default_name);
907         } else {
908                 real_suggested_name = suggested_name;
909         }
910
911         result = modest_platform_run_folder_name_dialog (parent_window, 
912                                                          parent_folder,
913                                                          _("mcen_ti_new_folder"),
914                                                          _("mcen_fi_new_folder_name"),
915                                                          real_suggested_name,
916                                                          folder_name);
917         if (suggested_name == NULL)
918                 g_free(real_suggested_name);
919
920         return result;
921 }
922
923 gint
924 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
925                                           TnyFolderStore *parent_folder,
926                                           const gchar *suggested_name,
927                                           gchar **folder_name)
928 {
929         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
930
931         return modest_platform_run_folder_name_dialog (parent_window, 
932                                                        parent_folder,
933                                                        _HL("ckdg_ti_rename_folder"),
934                                                        _HL("ckdg_fi_rename_name"),
935                                                        suggested_name,
936                                                        folder_name);
937 }
938
939
940
941 static void
942 on_destroy_dialog (GtkDialog *dialog)
943 {
944         gtk_widget_destroy (GTK_WIDGET(dialog));
945         if (gtk_events_pending ())
946                 gtk_main_iteration ();
947 }
948
949 gint
950 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
951                                          const gchar *message)
952 {
953         GtkWidget *dialog;
954         gint response;
955         
956         dialog = hildon_note_new_confirmation (parent_window, message);
957         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
958                                      GTK_WINDOW (dialog));
959
960         response = gtk_dialog_run (GTK_DIALOG (dialog));
961
962         on_destroy_dialog (GTK_DIALOG(dialog));
963         
964         while (gtk_events_pending ())
965                 gtk_main_iteration ();
966
967         return response;
968 }
969         
970 gint
971 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
972                                    const gchar *message)
973 {
974         GtkWidget *dialog;
975         gint response;
976         
977         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
978                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
979                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
980                                                            NULL);
981         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
982         response = gtk_dialog_run (GTK_DIALOG (dialog));
983         
984         on_destroy_dialog (GTK_DIALOG(dialog));
985
986         while (gtk_events_pending ())
987                 gtk_main_iteration ();
988
989         return response;
990 }
991
992
993
994 void
995 modest_platform_run_information_dialog (GtkWindow *parent_window,
996                                         const gchar *message)
997 {
998         GtkWidget *note;
999         
1000         note = hildon_note_new_information (parent_window, message);
1001         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1002                                      GTK_WINDOW (note));
1003         
1004         g_signal_connect_swapped (note,
1005                                   "response", 
1006                                   G_CALLBACK (on_destroy_dialog),
1007                                   note);
1008
1009         gtk_widget_show_all (note);
1010 }
1011
1012
1013
1014 typedef struct _ConnectAndWaitData {
1015         GMutex *mutex;
1016         GMainLoop *wait_loop;
1017         gboolean has_callback;
1018         gulong handler;
1019 } ConnectAndWaitData;
1020
1021
1022 static void
1023 quit_wait_loop (TnyAccount *account,
1024                 ConnectAndWaitData *data) 
1025 {
1026         /* Set the has_callback to TRUE (means that the callback was
1027            executed and wake up every code waiting for cond to be
1028            TRUE */
1029         g_mutex_lock (data->mutex);
1030         data->has_callback = TRUE;
1031         if (data->wait_loop)
1032                 g_main_loop_quit (data->wait_loop);
1033         g_mutex_unlock (data->mutex);
1034 }
1035
1036 static void
1037 on_connection_status_changed (TnyAccount *account, 
1038                               TnyConnectionStatus status,
1039                               gpointer user_data)
1040 {
1041         TnyConnectionStatus conn_status;
1042         ConnectAndWaitData *data;
1043                         
1044         /* Ignore if reconnecting or disconnected */
1045         conn_status = tny_account_get_connection_status (account);
1046         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1047             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1048                 return;
1049
1050         /* Remove the handler */
1051         data = (ConnectAndWaitData *) user_data;
1052         g_signal_handler_disconnect (account, data->handler);
1053
1054         /* Quit from wait loop */
1055         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1056 }
1057
1058 static void
1059 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1060                                     gboolean canceled, 
1061                                     GError *err, 
1062                                     gpointer user_data)
1063 {
1064         /* Quit from wait loop */
1065         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1066 }
1067
1068 gboolean 
1069 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1070                                   TnyAccount *account)
1071 {
1072         ConnectAndWaitData *data = NULL;
1073         gboolean device_online;
1074         TnyDevice *device;
1075         TnyConnectionStatus conn_status;
1076         gboolean user_requested;
1077         
1078         device = modest_runtime_get_device();
1079         device_online = tny_device_is_online (device);
1080
1081         /* Whether the connection is user requested or automatically
1082            requested, for example via D-Bus */
1083         user_requested = (parent_window) ? TRUE : FALSE;
1084
1085         /* If there is no account check only the device status */
1086         if (!account) {
1087                 if (device_online)
1088                         return TRUE;
1089                 else
1090                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1091                                                                NULL, user_requested);
1092         }
1093
1094         /* Return if the account is already connected */
1095         conn_status = tny_account_get_connection_status (account);
1096         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1097                 return TRUE;
1098
1099         /* Create the helper */
1100         data = g_slice_new0 (ConnectAndWaitData);
1101         data->mutex = g_mutex_new ();
1102         data->has_callback = FALSE;
1103
1104         /* Connect the device */
1105         if (!device_online) {
1106                 /* Track account connection status changes */
1107                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1108                                                   G_CALLBACK (on_connection_status_changed),
1109                                                   data);
1110                 /* Try to connect the device */
1111                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1112                                                                 NULL, user_requested);
1113
1114                 /* If the device connection failed then exit */
1115                 if (!device_online && data->handler)
1116                         goto frees;
1117         } else {
1118                 /* Force a reconnection of the account */
1119                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1120                                               on_tny_camel_account_set_online_cb, data);
1121         }
1122
1123         /* Wait until the callback is executed */
1124         g_mutex_lock (data->mutex);
1125         if (!data->has_callback) {
1126                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1127                 gdk_threads_leave ();
1128                 g_mutex_unlock (data->mutex);
1129                 g_main_loop_run (data->wait_loop);
1130                 g_mutex_lock (data->mutex);
1131                 gdk_threads_enter ();
1132         }
1133         g_mutex_unlock (data->mutex);
1134
1135  frees:
1136         if (data) {
1137                 if (g_signal_handler_is_connected (account, data->handler))
1138                         g_signal_handler_disconnect (account, data->handler);
1139                 g_mutex_free (data->mutex);
1140                 g_main_loop_unref (data->wait_loop);
1141                 g_slice_free (ConnectAndWaitData, data);
1142         }
1143
1144         conn_status = tny_account_get_connection_status (account);
1145         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1146 }
1147
1148 gboolean 
1149 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1150 {
1151         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1152                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1153                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1154                         /* This must be a maildir account, which does not require a connection: */
1155                         return TRUE;
1156                 }
1157         }
1158
1159         return modest_platform_connect_and_wait (parent_window, account);
1160 }
1161
1162 gboolean 
1163 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1164 {
1165         if (!folder_store)
1166                 return TRUE; /* Maybe it is something local. */
1167                 
1168         gboolean result = TRUE;
1169         if (TNY_IS_FOLDER (folder_store)) {
1170                 /* Get the folder's parent account: */
1171                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1172                 if (account != NULL) {
1173                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1174                         g_object_unref (account);
1175                 }
1176         } else if (TNY_IS_ACCOUNT (folder_store)) {
1177                 /* Use the folder store as an account: */
1178                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1179         }
1180
1181         return result;
1182 }
1183
1184 void
1185 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1186                                  ModestSortDialogType type)
1187 {
1188         GtkWidget *dialog = NULL;
1189
1190         /* Build dialog */
1191         dialog = hildon_sort_dialog_new (parent_window);
1192         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1193                                      GTK_WINDOW (dialog));
1194
1195         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1196                                         "applications_email_sort",
1197                                         modest_maemo_utils_get_osso_context());
1198
1199         /* Fill sort keys */
1200         switch (type) {
1201         case MODEST_SORT_HEADERS:
1202                 launch_sort_headers_dialog (parent_window, 
1203                                             HILDON_SORT_DIALOG(dialog));
1204                 break;
1205         }
1206         
1207         /* Free */
1208         on_destroy_dialog (GTK_DIALOG(dialog));
1209 }
1210
1211
1212 gboolean 
1213 modest_platform_set_update_interval (guint minutes)
1214 {
1215 #ifdef MODEST_HAVE_LIBALARM
1216         
1217         ModestConf *conf = modest_runtime_get_conf ();
1218         if (!conf)
1219                 return FALSE;
1220                 
1221         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1222
1223         /* Delete any existing alarm,
1224          * because we will replace it: */
1225         if (alarm_cookie) {
1226                 if (alarm_event_del(alarm_cookie) != 1)
1227                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1228                 alarm_cookie = 0;
1229                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1230         }
1231         
1232         /* 0 means no updates: */
1233         if (minutes == 0)
1234                 return TRUE;
1235         
1236      
1237         /* Register alarm: */
1238         
1239         /* Set the interval in alarm_event_t structure: */
1240         alarm_event_t *event = g_new0(alarm_event_t, 1);
1241         event->alarm_time = minutes * 60; /* seconds */
1242         
1243         /* Set recurrence every few minutes: */
1244         event->recurrence = minutes;
1245         event->recurrence_count = -1; /* Means infinite */
1246
1247         /* Specify what should happen when the alarm happens:
1248          * It should call this D-Bus method: */
1249          
1250         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1251         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1252         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1253         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1254
1255         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1256          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1257          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1258          * This is why we want to use the Alarm API instead of just g_timeout_add().
1259          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1260          */
1261         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1262         
1263         alarm_cookie = alarm_event_add (event);
1264
1265         /* now, free it */
1266         alarm_event_free (event);
1267         
1268         /* Store the alarm ID in GConf, so we can remove it later:
1269          * This is apparently valid between application instances. */
1270         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1271         
1272         if (!alarm_cookie) {
1273             /* Error */
1274             const alarm_error_t alarm_error = alarmd_get_error ();
1275             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1276             
1277             /* Give people some clue: */
1278             /* The alarm API should have a function for this: */
1279             if (alarm_error == ALARMD_ERROR_DBUS) {
1280                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1281             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1282                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1283             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1284                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1285             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1286                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1287             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1288                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1289             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1290                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1291             }
1292             
1293             return FALSE;
1294         }
1295 #endif /* MODEST_HAVE_LIBALARM */       
1296         return TRUE;
1297 }
1298
1299 void 
1300 modest_platform_on_new_headers_received (TnyList *header_list) 
1301 {
1302 #ifdef MODEST_HAVE_HILDON_NOTIFY
1303         HildonNotification *notification;
1304         TnyIterator *iter;
1305         GSList *notifications_list = NULL;
1306
1307         /* Get previous notifications ids */
1308         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1309                                                    MODEST_CONF_NOTIFICATION_IDS, 
1310                                                    MODEST_CONF_VALUE_INT, NULL);
1311
1312         iter = tny_list_create_iterator (header_list);
1313         while (!tny_iterator_is_done (iter)) {
1314                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1315                 const gchar *display_date;
1316                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1317                 TnyFolder *folder = tny_header_get_folder (header);
1318                 gboolean first_notification = TRUE;
1319                 gint notif_id;
1320
1321                 /* constant string, don't free */
1322                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1323
1324                 display_address = g_strdup(tny_header_get_from (header));
1325                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1326                 
1327                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1328                 notification = hildon_notification_new (summary,
1329                                                         tny_header_get_subject (header),
1330                                                         "qgn_list_messagin",
1331                                                         "email.arrive");
1332                 /* Create the message URL */
1333                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1334                                        tny_header_get_uid (header));
1335
1336                 hildon_notification_add_dbus_action(notification,
1337                                                     "default",
1338                                                     "Cancel",
1339                                                     MODEST_DBUS_SERVICE,
1340                                                     MODEST_DBUS_OBJECT,
1341                                                     MODEST_DBUS_IFACE,
1342                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1343                                                     G_TYPE_STRING, url,
1344                                                     -1);
1345
1346                 /* Play sound if the user wants. Show the LED
1347                    pattern. Show and play just one */
1348                 if (G_UNLIKELY (first_notification)) {
1349                         first_notification = FALSE;
1350                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1351                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1352                                                   NULL))  {
1353                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1354                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1355                         }
1356
1357                         /* Set the led pattern */
1358                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1359                                                             "dialog-type", 4);
1360                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1361                                                             "led-pattern",
1362                                                             "PatternCommunicationEmail");                       
1363                 }
1364
1365                 /* Notify. We need to do this in an idle because this function
1366                    could be called from a thread */
1367                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1368
1369                 /* Save id in the list */
1370                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1371                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1372                 /* We don't listen for the "closed" signal, because we
1373                    don't care about if the notification was removed or
1374                    not to store the list in gconf */
1375         
1376                 /* Free & carry on */
1377                 g_free (display_address);
1378                 g_free (summary);
1379                 g_free (url);
1380                 g_object_unref (folder);
1381                 g_object_unref (header);
1382                 tny_iterator_next (iter);
1383         }
1384         g_object_unref (iter);
1385
1386         /* Save the ids */
1387         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1388                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1389
1390         g_slist_free (notifications_list);
1391         
1392 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1393 }
1394
1395 void
1396 modest_platform_remove_new_mail_notifications (void) 
1397 {
1398 #ifdef MODEST_HAVE_HILDON_NOTIFY
1399         GSList *notif_list = NULL;
1400
1401         /* Get previous notifications ids */
1402         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1403                                            MODEST_CONF_NOTIFICATION_IDS, 
1404                                            MODEST_CONF_VALUE_INT, NULL);
1405
1406         while (notif_list) {
1407                 gint notif_id;
1408                 NotifyNotification *notif;
1409
1410                 /* Nasty HACK to remove the notifications, set the id
1411                    of the existing ones and then close them */
1412                 notif_id = GPOINTER_TO_INT(notif_list->data);
1413                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1414                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1415
1416                 /* Close the notification, note that some ids could be
1417                    already invalid, but we don't care because it does
1418                    not fail */
1419                 notify_notification_close(notif, NULL);
1420                 g_object_unref(notif);
1421
1422                 /* Delete the link, it's like going to the next */
1423                 notif_list = g_slist_delete_link (notif_list, notif_list);
1424         }
1425
1426         /* Save the ids */
1427         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1428                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1429
1430         g_slist_free (notif_list);
1431
1432 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1433 }
1434
1435
1436
1437 GtkWidget * 
1438 modest_platform_get_global_settings_dialog ()
1439 {
1440         return modest_maemo_global_settings_dialog_new ();
1441 }
1442
1443 void
1444 modest_platform_show_help (GtkWindow *parent_window, 
1445                            const gchar *help_id)
1446 {
1447         osso_return_t result;
1448         g_return_if_fail (help_id);
1449
1450         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1451                                    help_id, HILDON_HELP_SHOW_DIALOG);
1452         
1453         if (result != OSSO_OK) {
1454                 gchar *error_msg;
1455                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1456                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1457                                                 NULL,
1458                                                 error_msg);
1459                 g_free (error_msg);
1460         }
1461 }
1462
1463 void 
1464 modest_platform_show_search_messages (GtkWindow *parent_window)
1465 {
1466         osso_return_t result = OSSO_ERROR;
1467         
1468         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1469                                              "osso_global_search",
1470                                              "search_email", NULL, DBUS_TYPE_INVALID);
1471
1472         if (result != OSSO_OK) {
1473                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1474         }
1475 }
1476
1477 void 
1478 modest_platform_show_addressbook (GtkWindow *parent_window)
1479 {
1480         osso_return_t result = OSSO_ERROR;
1481         
1482         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1483                                              "osso_addressbook",
1484                                              "top_application", NULL, DBUS_TYPE_INVALID);
1485
1486         if (result != OSSO_OK) {
1487                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1488         }
1489 }
1490
1491 GtkWidget *
1492 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1493 {
1494         GtkWidget *widget = modest_folder_view_new (query);
1495
1496         /* Show one account by default */
1497         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1498                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1499
1500
1501         /* Restore settings */
1502         modest_widget_memory_restore (modest_runtime_get_conf(), 
1503                                       G_OBJECT (widget),
1504                                       MODEST_CONF_FOLDER_VIEW_KEY);
1505
1506         return widget;
1507 }
1508
1509 void 
1510 modest_platform_information_banner (GtkWidget *parent,
1511                                     const gchar *icon_name,
1512                                     const gchar *text)
1513 {
1514         hildon_banner_show_information (parent, icon_name, text);
1515 }
1516
1517 GtkWidget *
1518 modest_platform_animation_banner (GtkWidget *parent,
1519                                   const gchar *animation_name,
1520                                   const gchar *text)
1521 {
1522         GtkWidget *inf_note = NULL;
1523
1524         g_return_val_if_fail (text != NULL, NULL);
1525
1526         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1527
1528         return inf_note;
1529 }
1530
1531 typedef struct
1532 {
1533         GMainLoop* loop;
1534         TnyAccount *account;
1535         gboolean is_online;
1536         gint count_tries;
1537 } CheckAccountIdleData;
1538
1539 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1540
1541 static gboolean 
1542 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1543 {
1544         gboolean stop_trying = FALSE;
1545         g_return_val_if_fail (data && data->account, FALSE);
1546         
1547         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1548                 tny_account_get_connection_status (data->account));     
1549         
1550         if (data && data->account && 
1551                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1552                  * after which the account is likely to be usable, or never likely to be usable soon: */
1553                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1554         {
1555                 data->is_online = TRUE;
1556                 
1557                 stop_trying = TRUE;
1558         } else {
1559                 /* Give up if we have tried too many times: */
1560                 if (data->count_tries >= NUMBER_OF_TRIES) {
1561                         stop_trying = TRUE;
1562                 } else {
1563                         /* Wait for another timeout: */
1564                         ++(data->count_tries);
1565                 }
1566         }
1567         
1568         if (stop_trying) {
1569                 /* Allow the function that requested this idle callback to continue: */
1570                 if (data->loop)
1571                         g_main_loop_quit (data->loop);
1572                         
1573                 if (data->account)
1574                         g_object_unref (data->account);
1575                 
1576                 return FALSE; /* Don't call this again. */
1577         } else {
1578                 return TRUE; /* Call this timeout callback again. */
1579         }
1580 }
1581
1582 /* Return TRUE immediately if the account is already online,
1583  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1584  * soon as the account is online, or FALSE if the account does 
1585  * not become online in the NUMBER_OF_TRIES seconds.
1586  * This is useful when the D-Bus method was run immediately after 
1587  * the application was started (when using D-Bus activation), 
1588  * because the account usually takes a short time to go online.
1589  * The return value is maybe not very useful.
1590  */
1591 gboolean
1592 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1593 {
1594         g_return_val_if_fail (account, FALSE);
1595         
1596         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1597         
1598         if (!tny_device_is_online (modest_runtime_get_device())) {
1599                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1600                 return FALSE;
1601         }
1602         
1603         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1604          * so we avoid wait unnecessarily: */
1605         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1606                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1607                 return TRUE;            
1608         }
1609                 
1610         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1611                 __FUNCTION__, tny_account_get_connection_status (account));
1612         
1613         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1614          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1615          * we want to avoid. */
1616         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1617                 return TRUE;
1618                 
1619         /* This blocks on the result: */
1620         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1621         data->is_online = FALSE;
1622         data->account = account;
1623         g_object_ref (data->account);
1624         data->count_tries = 0;
1625                 
1626         GMainContext *context = NULL; /* g_main_context_new (); */
1627         data->loop = g_main_loop_new (context, FALSE /* not running */);
1628
1629         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1630
1631         /* This main loop will run until the idle handler has stopped it: */
1632         g_main_loop_run (data->loop);
1633
1634         g_main_loop_unref (data->loop);
1635         /* g_main_context_unref (context); */
1636
1637         g_slice_free (CheckAccountIdleData, data);
1638         
1639         return data->is_online; 
1640 }
1641
1642
1643
1644 static void
1645 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1646 {
1647         /* GTK_RESPONSE_HELP means we need to show the certificate */
1648         if (response_id == GTK_RESPONSE_HELP) {
1649                 GtkWidget *note;
1650                 gchar *msg;
1651                 
1652                 /* Do not close the dialog */
1653                 g_signal_stop_emission_by_name (dialog, "response");
1654
1655                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1656                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1657                 gtk_dialog_run (GTK_DIALOG(note));
1658                 gtk_widget_destroy (note);
1659         }
1660 }
1661
1662
1663 gboolean
1664 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1665                                                      const gchar *certificate)
1666 {
1667         GtkWidget *note;
1668         gint response;
1669         ModestWindow *main_win;
1670         
1671         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1672                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1673                            __FUNCTION__);
1674                 return FALSE;
1675         }
1676
1677         /* don't create it */
1678         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1679         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1680         
1681         
1682         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1683                                            server_name);
1684         
1685         note = hildon_note_new_confirmation_add_buttons  (
1686                 GTK_WINDOW(main_win),
1687                 question,
1688                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1689                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1690                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1691                 NULL, NULL);
1692         
1693         g_signal_connect (G_OBJECT(note), "response", 
1694                           G_CALLBACK(on_cert_dialog_response),
1695                           (gpointer) certificate);
1696         
1697         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1698                                      GTK_WINDOW (note));
1699         response = gtk_dialog_run(GTK_DIALOG(note));
1700
1701         on_destroy_dialog (GTK_DIALOG(note));
1702         g_free (question);
1703         
1704         return response == GTK_RESPONSE_OK;
1705 }
1706
1707 gboolean
1708 modest_platform_run_alert_dialog (const gchar* prompt, 
1709                                   gboolean is_question)
1710 {       
1711         ModestWindow *main_win; 
1712
1713         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1714                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1715                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1716                 return is_question ? FALSE : TRUE;
1717         }
1718
1719         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1720         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1721         
1722         gboolean retval = TRUE;
1723         if (is_question) {
1724                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1725                  * when it is a question.
1726                  * Obviously, we need tinymail to use more specific error codes instead,
1727                  * so we know what buttons to show. */
1728                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1729                                                                               prompt));
1730                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1731                                              GTK_WINDOW (dialog));
1732                 
1733                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1734                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1735                 
1736                 on_destroy_dialog (GTK_DIALOG(dialog));         
1737         } else {
1738                 /* Just show the error text and use the default response: */
1739                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1740                                                         prompt);
1741         }
1742         return retval;
1743 }
1744
1745 /***************/
1746 typedef struct {
1747         GtkWindow *parent_window;
1748         ModestConnectedPerformer callback;
1749         TnyAccount *account;
1750         gpointer user_data;
1751         gchar *iap;
1752         TnyDevice *device;
1753 } OnWentOnlineInfo;
1754  
1755 static void 
1756 on_went_online_info_free (OnWentOnlineInfo *info)
1757 {
1758         /* And if we cleanup, we DO cleanup  :-)  */
1759         
1760         if (info->device)
1761                 g_object_unref (info->device);
1762         if (info->iap)
1763                 g_free (info->iap);
1764         if (info->parent_window)
1765                 g_object_unref (info->parent_window);
1766         if (info->account)
1767                 g_object_unref (info->account);
1768         
1769         g_slice_free (OnWentOnlineInfo, info);
1770         
1771         /* We're done ... */
1772         
1773         return;
1774 }
1775  
1776 static void
1777 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1778 {
1779         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1780  
1781         /* Now it's really time to callback to the caller. If going online didn't succeed,
1782          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1783          * canceled will be set. Etcetera etcetera. */
1784         
1785         if (info->callback) {
1786                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1787         }
1788         
1789         /* This is our last call, we must cleanup here if we didn't yet do that */
1790         on_went_online_info_free (info);
1791         
1792         return;
1793 }
1794  
1795  
1796 static void
1797 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1798 {
1799         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1800         info->iap = g_strdup (iap_id);
1801         
1802         if (canceled || err || !info->account) {
1803         
1804                 /* If there's a problem or if there's no account (then that's it for us, we callback
1805                  * the caller's callback now. He'll have to handle err or canceled, of course.
1806                  * We are not really online, as the account is not really online here ... */    
1807                 
1808                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1809                  * this info. We don't cleanup err, Tinymail does that! */
1810                 
1811                 if (info->callback) {
1812                         
1813                         /* info->account can be NULL here, this means that the user did not
1814                          * provide a nice account instance. We'll assume that the user knows
1815                          * what he's doing and is happy with just the device going online. 
1816                          * 
1817                          * We can't do magic, we don't know what account the user wants to
1818                          * see going online. So just the device goes online, end of story */
1819                         
1820                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1821                 }
1822                 
1823         } else if (info->account) {
1824                 
1825                 /* If there's no problem and if we have an account, we'll put the account
1826                  * online too. When done, the callback of bringing the account online
1827                  * will callback the caller's callback. This is the most normal case. */
1828  
1829                 info->device = TNY_DEVICE (g_object_ref (device));
1830                 
1831                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1832                                               on_account_went_online, info);
1833                 
1834                 /* The on_account_went_online cb frees up the info, go look if you
1835                  * don't believe me! (so we return here) */
1836                 
1837                 return;
1838         }
1839         
1840         /* We cleanup if we are not bringing the account online too */
1841         on_went_online_info_free (info);
1842  
1843         return; 
1844 }
1845         
1846 void 
1847 modest_platform_connect_and_perform (GtkWindow *parent_window, 
1848                                      TnyAccount *account, 
1849                                      ModestConnectedPerformer callback, 
1850                                      gpointer user_data)
1851 {
1852         gboolean device_online;
1853         TnyDevice *device;
1854         TnyConnectionStatus conn_status;
1855         OnWentOnlineInfo *info;
1856         gboolean user_requested;
1857         
1858         device = modest_runtime_get_device();
1859         device_online = tny_device_is_online (device);
1860
1861         /* Whether the connection is user requested or automatically
1862            requested, for example via D-Bus */
1863         user_requested = (parent_window) ? TRUE : FALSE;
1864
1865         /* If there is no account check only the device status */
1866         if (!account) {
1867                 
1868                 if (device_online) {
1869  
1870                         /* We promise to instantly perform the callback, so ... */
1871                         if (callback) {
1872                                 callback (FALSE, NULL, parent_window, account, user_data);
1873                         }
1874                         
1875                 } else {
1876                         
1877                         info = g_slice_new0 (OnWentOnlineInfo);
1878                         
1879                         info->iap = NULL;
1880                         info->device = NULL;
1881                         info->account = NULL;
1882                 
1883                         if (parent_window)
1884                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1885                         else
1886                                 info->parent_window = NULL;
1887                         info->user_data = user_data;
1888                         info->callback = callback;
1889                 
1890                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1891                                                               user_requested, on_conic_device_went_online, 
1892                                                               info);
1893  
1894                         /* We'll cleanup in on_conic_device_went_online */
1895                 }
1896  
1897                 /* The other code has no more reason to run. This is all that we can do for the
1898                  * caller (he should have given us a nice and clean account instance!). We
1899                  * can't do magic, we don't know what account he intends to bring online. So
1900                  * we'll just bring the device online (and await his false bug report). */
1901                 
1902                 return;
1903         }
1904  
1905         
1906         /* Return if the account is already connected */
1907         
1908         conn_status = tny_account_get_connection_status (account);
1909         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1910  
1911                 /* We promise to instantly perform the callback, so ... */
1912                 if (callback) {
1913                         callback (FALSE, NULL, parent_window, account, user_data);
1914                 }
1915                 
1916                 return;
1917         }
1918         
1919         /* Else, we are in a state that requires that we go online before we
1920          * call the caller's callback. */
1921         
1922         info = g_slice_new0 (OnWentOnlineInfo);
1923         
1924         info->device = NULL;
1925         info->iap = NULL;
1926         info->account = TNY_ACCOUNT (g_object_ref (account));
1927         
1928         if (parent_window)
1929                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1930         else
1931                 info->parent_window = NULL;
1932         
1933         /* So we'll put the callback away for later ... */
1934         
1935         info->user_data = user_data;
1936         info->callback = callback;
1937         
1938         if (!device_online) {
1939  
1940                 /* If also the device is offline, then we connect both the device 
1941                  * and the account */
1942                 
1943                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1944                                                       user_requested, on_conic_device_went_online, 
1945                                                       info);
1946                 
1947         } else {
1948                 
1949                 /* If the device is online, we'll just connect the account */
1950                 
1951                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1952                                               on_account_went_online, info);
1953         }
1954  
1955         /* The info gets freed by on_account_went_online or on_conic_device_went_online
1956          * in both situations, go look if you don't believe me! */
1957         
1958         return;
1959 }
1960
1961 void
1962 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
1963                                                             TnyFolderStore *folder_store, 
1964                                                             ModestConnectedPerformer callback, 
1965                                                             gpointer user_data)
1966 {
1967         TnyAccount *account = NULL;
1968         
1969         if (!folder_store) {
1970                 /* We promise to instantly perform the callback, so ... */
1971                 if (callback) {
1972                         callback (FALSE, NULL, parent_window, NULL, user_data);
1973                 }
1974                 return; 
1975                 
1976                 /* Original comment: Maybe it is something local. */
1977                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
1978                 
1979         } else if (TNY_IS_FOLDER (folder_store)) {
1980                 /* Get the folder's parent account: */
1981                 account = tny_folder_get_account(TNY_FOLDER (folder_store));
1982         } else if (TNY_IS_ACCOUNT (folder_store)) {
1983                 /* Use the folder store as an account: */
1984                 account = TNY_ACCOUNT (folder_store);
1985         }
1986  
1987         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1988                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1989                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1990                         
1991                         /* This IS a local account like a maildir account, which does not require 
1992                          * a connection. (original comment had a vague assumption in its language
1993                          * usage. There's no assuming needed, this IS what it IS: a local account), */
1994  
1995                         /* We promise to instantly perform the callback, so ... */
1996                         if (callback) {
1997                                 callback (FALSE, NULL, parent_window, account, user_data);
1998                         }
1999                         
2000                         return;
2001                 }
2002         }
2003  
2004         modest_platform_connect_and_perform (parent_window, account, callback, user_data);
2005  
2006         return;
2007 }