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