* Gathered the argument checking code to one single function in the D-Bus methods
[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, NULL, 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         gboolean user_requested;
1034         
1035         device = modest_runtime_get_device();
1036         device_online = tny_device_is_online (device);
1037
1038         /* Whether the connection is user requested or automatically
1039            requested, for example via D-Bus */
1040         user_requested = (parent_window) ? TRUE : FALSE;
1041
1042         /* If there is no account check only the device status */
1043         if (!account) {
1044                 if (device_online)
1045                         return TRUE;
1046                 else
1047                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1048                                                                NULL, user_requested);
1049         }
1050
1051         /* Return if the account is already connected */
1052         conn_status = tny_account_get_connection_status (account);
1053         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1054                 return TRUE;
1055
1056         /* Create the helper */
1057         data = g_slice_new0 (ConnectAndWaitData);
1058         data->mutex = g_mutex_new ();
1059         data->has_callback = FALSE;
1060
1061         /* Connect the device */
1062         if (!device_online) {
1063                 /* Track account connection status changes */
1064                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1065                                                   G_CALLBACK (on_connection_status_changed),
1066                                                   data);
1067                 /* Try to connect the device */
1068                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1069                                                                 NULL, user_requested);
1070
1071                 /* If the device connection failed then exit */
1072                 if (!device_online && data->handler)
1073                         goto frees;
1074         } else {
1075                 /* Force a reconnection of the account */
1076                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1077                                               on_tny_camel_account_set_online_cb, data);
1078         }
1079
1080         /* Wait until the callback is executed */
1081         g_mutex_lock (data->mutex);
1082         if (!data->has_callback) {
1083                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1084                 gdk_threads_leave ();
1085                 g_mutex_unlock (data->mutex);
1086                 g_main_loop_run (data->wait_loop);
1087                 g_mutex_lock (data->mutex);
1088                 gdk_threads_enter ();
1089         }
1090         g_mutex_unlock (data->mutex);
1091
1092  frees:
1093         if (data) {
1094                 if (g_signal_handler_is_connected (account, data->handler))
1095                         g_signal_handler_disconnect (account, data->handler);
1096                 g_mutex_free (data->mutex);
1097                 g_main_loop_unref (data->wait_loop);
1098                 g_slice_free (ConnectAndWaitData, data);
1099         }
1100
1101         conn_status = tny_account_get_connection_status (account);
1102         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1103 }
1104
1105 gboolean 
1106 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1107 {
1108         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1109                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1110                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1111                         /* This must be a maildir account, which does not require a connection: */
1112                         return TRUE;
1113                 }
1114         }
1115
1116         return modest_platform_connect_and_wait (parent_window, account);
1117 }
1118
1119 gboolean 
1120 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1121 {
1122         if (!folder_store)
1123                 return TRUE; /* Maybe it is something local. */
1124                 
1125         gboolean result = TRUE;
1126         if (TNY_IS_FOLDER (folder_store)) {
1127                 /* Get the folder's parent account: */
1128                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1129                 if (account != NULL) {
1130                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1131                         g_object_unref (account);
1132                 }
1133         } else if (TNY_IS_ACCOUNT (folder_store)) {
1134                 /* Use the folder store as an account: */
1135                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1136         }
1137
1138         return result;
1139 }
1140
1141 gboolean 
1142 modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1143 {
1144         TnyAccount *account = NULL;
1145         gboolean result = TRUE;
1146
1147         g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1148
1149         if (TNY_IS_FOLDER (folder_store)) {
1150                 /* Get the folder's parent account: */
1151                 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1152         } else if (TNY_IS_ACCOUNT (folder_store)) {
1153                 account = TNY_ACCOUNT(folder_store);
1154                 g_object_ref(account);
1155         }
1156
1157         if (account != NULL) {
1158                 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1159                         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1160                             !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1161                                 /* This must be a maildir account, which does
1162                                  * not require a connection: */
1163                                 result = FALSE;
1164                         }
1165                 }
1166                 g_object_unref (account);
1167         } else {
1168                 result = FALSE;
1169         }
1170
1171         return result;
1172 }
1173
1174 void
1175 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1176                                  ModestSortDialogType type)
1177 {
1178         GtkWidget *dialog = NULL;
1179
1180         /* Build dialog */
1181         dialog = hildon_sort_dialog_new (parent_window);
1182         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1183                                      GTK_WINDOW (dialog));
1184
1185         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1186                                         "applications_email_sort",
1187                                         modest_maemo_utils_get_osso_context());
1188
1189         /* Fill sort keys */
1190         switch (type) {
1191         case MODEST_SORT_HEADERS:
1192                 launch_sort_headers_dialog (parent_window, 
1193                                             HILDON_SORT_DIALOG(dialog));
1194                 break;
1195         }
1196         
1197         /* Free */
1198         on_destroy_dialog (GTK_DIALOG(dialog));
1199 }
1200
1201
1202 gboolean 
1203 modest_platform_set_update_interval (guint minutes)
1204 {
1205 #ifdef MODEST_HAVE_LIBALARM
1206         
1207         ModestConf *conf = modest_runtime_get_conf ();
1208         if (!conf)
1209                 return FALSE;
1210                 
1211         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1212
1213         /* Delete any existing alarm,
1214          * because we will replace it: */
1215         if (alarm_cookie) {
1216                 if (alarm_event_del(alarm_cookie) != 1)
1217                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1218                 alarm_cookie = 0;
1219                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1220         }
1221         
1222         /* 0 means no updates: */
1223         if (minutes == 0)
1224                 return TRUE;
1225         
1226      
1227         /* Register alarm: */
1228         
1229         /* Set the interval in alarm_event_t structure: */
1230         alarm_event_t *event = g_new0(alarm_event_t, 1);
1231         event->alarm_time = minutes * 60; /* seconds */
1232         
1233         /* Set recurrence every few minutes: */
1234         event->recurrence = minutes;
1235         event->recurrence_count = -1; /* Means infinite */
1236
1237         /* Specify what should happen when the alarm happens:
1238          * It should call this D-Bus method: */
1239          
1240         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1241         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1242         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1243         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1244
1245         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1246          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1247          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1248          * This is why we want to use the Alarm API instead of just g_timeout_add().
1249          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1250          */
1251         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1252         
1253         alarm_cookie = alarm_event_add (event);
1254
1255         /* now, free it */
1256         alarm_event_free (event);
1257         
1258         /* Store the alarm ID in GConf, so we can remove it later:
1259          * This is apparently valid between application instances. */
1260         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1261         
1262         if (!alarm_cookie) {
1263             /* Error */
1264             const alarm_error_t alarm_error = alarmd_get_error ();
1265             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1266             
1267             /* Give people some clue: */
1268             /* The alarm API should have a function for this: */
1269             if (alarm_error == ALARMD_ERROR_DBUS) {
1270                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1271             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1272                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1273             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1274                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1275             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1276                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1277             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1278                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1279             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1280                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1281             }
1282             
1283             return FALSE;
1284         }
1285 #endif /* MODEST_HAVE_LIBALARM */       
1286         return TRUE;
1287 }
1288
1289 void 
1290 modest_platform_on_new_headers_received (TnyList *header_list) 
1291 {
1292 #ifdef MODEST_HAVE_HILDON_NOTIFY
1293         HildonNotification *notification;
1294         TnyIterator *iter;
1295         GSList *notifications_list = NULL;
1296
1297         /* Get previous notifications ids */
1298         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1299                                                    MODEST_CONF_NOTIFICATION_IDS, 
1300                                                    MODEST_CONF_VALUE_INT, NULL);
1301
1302         iter = tny_list_create_iterator (header_list);
1303         while (!tny_iterator_is_done (iter)) {
1304                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1305                 const gchar *display_date;
1306                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1307                 TnyFolder *folder = tny_header_get_folder (header);
1308                 gboolean first_notification = TRUE;
1309                 gint notif_id;
1310
1311                 /* constant string, don't free */
1312                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1313
1314                 display_address = g_strdup(tny_header_get_from (header));
1315                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1316                 
1317                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1318                 notification = hildon_notification_new (summary,
1319                                                         tny_header_get_subject (header),
1320                                                         "qgn_list_messagin",
1321                                                         "email.arrive");
1322                 /* Create the message URL */
1323                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1324                                        tny_header_get_uid (header));
1325
1326                 hildon_notification_add_dbus_action(notification,
1327                                                     "default",
1328                                                     "Cancel",
1329                                                     MODEST_DBUS_SERVICE,
1330                                                     MODEST_DBUS_OBJECT,
1331                                                     MODEST_DBUS_IFACE,
1332                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1333                                                     G_TYPE_STRING, url,
1334                                                     -1);
1335
1336                 /* Play sound if the user wants. Show the LED
1337                    pattern. Show and play just one */
1338                 if (G_UNLIKELY (first_notification)) {
1339                         first_notification = FALSE;
1340                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1341                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1342                                                   NULL))  {
1343                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1344                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1345                         }
1346
1347                         /* Set the led pattern */
1348                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1349                                                             "dialog-type", 4);
1350                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1351                                                             "led-pattern",
1352                                                             "PatternCommunicationEmail");                       
1353                 }
1354
1355                 /* Notify. We need to do this in an idle because this function
1356                    could be called from a thread */
1357                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1358
1359                 /* Save id in the list */
1360                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1361                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1362                 /* We don't listen for the "closed" signal, because we
1363                    don't care about if the notification was removed or
1364                    not to store the list in gconf */
1365         
1366                 /* Free & carry on */
1367                 g_free (display_address);
1368                 g_free (summary);
1369                 g_free (url);
1370                 g_object_unref (folder);
1371                 g_object_unref (header);
1372                 tny_iterator_next (iter);
1373         }
1374         g_object_unref (iter);
1375
1376         /* Save the ids */
1377         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1378                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1379
1380         g_slist_free (notifications_list);
1381         
1382 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1383 }
1384
1385 void
1386 modest_platform_remove_new_mail_notifications (void) 
1387 {
1388 #ifdef MODEST_HAVE_HILDON_NOTIFY
1389         GSList *notif_list = NULL;
1390
1391         /* Get previous notifications ids */
1392         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1393                                            MODEST_CONF_NOTIFICATION_IDS, 
1394                                            MODEST_CONF_VALUE_INT, NULL);
1395
1396         while (notif_list) {
1397                 gint notif_id;
1398                 NotifyNotification *notif;
1399
1400                 /* Nasty HACK to remove the notifications, set the id
1401                    of the existing ones and then close them */
1402                 notif_id = GPOINTER_TO_INT(notif_list->data);
1403                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1404                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1405
1406                 /* Close the notification, note that some ids could be
1407                    already invalid, but we don't care because it does
1408                    not fail */
1409                 notify_notification_close(notif, NULL);
1410                 g_object_unref(notif);
1411
1412                 /* Delete the link, it's like going to the next */
1413                 notif_list = g_slist_delete_link (notif_list, notif_list);
1414         }
1415
1416         /* Save the ids */
1417         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1418                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1419
1420         g_slist_free (notif_list);
1421
1422 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1423 }
1424
1425
1426
1427 GtkWidget * 
1428 modest_platform_get_global_settings_dialog ()
1429 {
1430         return modest_maemo_global_settings_dialog_new ();
1431 }
1432
1433 void
1434 modest_platform_show_help (GtkWindow *parent_window, 
1435                            const gchar *help_id)
1436 {
1437         osso_return_t result;
1438         g_return_if_fail (help_id);
1439
1440         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1441                                    help_id, HILDON_HELP_SHOW_DIALOG);
1442         
1443         if (result != OSSO_OK) {
1444                 gchar *error_msg;
1445                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1446                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1447                                                 NULL,
1448                                                 error_msg);
1449                 g_free (error_msg);
1450         }
1451 }
1452
1453 void 
1454 modest_platform_show_search_messages (GtkWindow *parent_window)
1455 {
1456         osso_return_t result = OSSO_ERROR;
1457         
1458         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1459                                              "osso_global_search",
1460                                              "search_email", NULL, DBUS_TYPE_INVALID);
1461
1462         if (result != OSSO_OK) {
1463                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1464         }
1465 }
1466
1467 void 
1468 modest_platform_show_addressbook (GtkWindow *parent_window)
1469 {
1470         osso_return_t result = OSSO_ERROR;
1471         
1472         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1473                                              "osso_addressbook",
1474                                              "top_application", NULL, DBUS_TYPE_INVALID);
1475
1476         if (result != OSSO_OK) {
1477                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1478         }
1479 }
1480
1481 GtkWidget *
1482 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1483 {
1484         GtkWidget *widget = modest_folder_view_new (query);
1485
1486         /* Show one account by default */
1487         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1488                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1489
1490
1491         /* Restore settings */
1492         modest_widget_memory_restore (modest_runtime_get_conf(), 
1493                                       G_OBJECT (widget),
1494                                       MODEST_CONF_FOLDER_VIEW_KEY);
1495
1496         return widget;
1497 }
1498
1499 void 
1500 modest_platform_information_banner (GtkWidget *parent,
1501                                     const gchar *icon_name,
1502                                     const gchar *text)
1503 {
1504         hildon_banner_show_information (parent, icon_name, text);
1505 }
1506
1507 GtkWidget *
1508 modest_platform_animation_banner (GtkWidget *parent,
1509                                   const gchar *animation_name,
1510                                   const gchar *text)
1511 {
1512         GtkWidget *inf_note = NULL;
1513
1514         g_return_val_if_fail (text != NULL, NULL);
1515
1516         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1517
1518         return inf_note;
1519 }
1520
1521 typedef struct
1522 {
1523         GMainLoop* loop;
1524         TnyAccount *account;
1525         gboolean is_online;
1526         gint count_tries;
1527 } CheckAccountIdleData;
1528
1529 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1530
1531 static gboolean 
1532 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1533 {
1534         gboolean stop_trying = FALSE;
1535         g_return_val_if_fail (data && data->account, FALSE);
1536         
1537         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1538                 tny_account_get_connection_status (data->account));     
1539         
1540         if (data && data->account && 
1541                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1542                  * after which the account is likely to be usable, or never likely to be usable soon: */
1543                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1544         {
1545                 data->is_online = TRUE;
1546                 
1547                 stop_trying = TRUE;
1548         } else {
1549                 /* Give up if we have tried too many times: */
1550                 if (data->count_tries >= NUMBER_OF_TRIES) {
1551                         stop_trying = TRUE;
1552                 } else {
1553                         /* Wait for another timeout: */
1554                         ++(data->count_tries);
1555                 }
1556         }
1557         
1558         if (stop_trying) {
1559                 /* Allow the function that requested this idle callback to continue: */
1560                 if (data->loop)
1561                         g_main_loop_quit (data->loop);
1562                         
1563                 if (data->account)
1564                         g_object_unref (data->account);
1565                 
1566                 return FALSE; /* Don't call this again. */
1567         } else {
1568                 return TRUE; /* Call this timeout callback again. */
1569         }
1570 }
1571
1572 /* Return TRUE immediately if the account is already online,
1573  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1574  * soon as the account is online, or FALSE if the account does 
1575  * not become online in the NUMBER_OF_TRIES seconds.
1576  * This is useful when the D-Bus method was run immediately after 
1577  * the application was started (when using D-Bus activation), 
1578  * because the account usually takes a short time to go online.
1579  * The return value is maybe not very useful.
1580  */
1581 gboolean
1582 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1583 {
1584         g_return_val_if_fail (account, FALSE);
1585         
1586         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1587         
1588         if (!tny_device_is_online (modest_runtime_get_device())) {
1589                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1590                 return FALSE;
1591         }
1592         
1593         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1594          * so we avoid wait unnecessarily: */
1595         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1596                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1597                 return TRUE;            
1598         }
1599                 
1600         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1601                 __FUNCTION__, tny_account_get_connection_status (account));
1602         
1603         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1604          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1605          * we want to avoid. */
1606         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1607                 return TRUE;
1608                 
1609         /* This blocks on the result: */
1610         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1611         data->is_online = FALSE;
1612         data->account = account;
1613         g_object_ref (data->account);
1614         data->count_tries = 0;
1615                 
1616         GMainContext *context = NULL; /* g_main_context_new (); */
1617         data->loop = g_main_loop_new (context, FALSE /* not running */);
1618
1619         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1620
1621         /* This main loop will run until the idle handler has stopped it: */
1622         g_main_loop_run (data->loop);
1623
1624         g_main_loop_unref (data->loop);
1625         /* g_main_context_unref (context); */
1626
1627         g_slice_free (CheckAccountIdleData, data);
1628         
1629         return data->is_online; 
1630 }
1631
1632
1633
1634 static void
1635 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1636 {
1637         /* GTK_RESPONSE_HELP means we need to show the certificate */
1638         if (response_id == GTK_RESPONSE_HELP) {
1639                 GtkWidget *note;
1640                 gchar *msg;
1641                 
1642                 /* Do not close the dialog */
1643                 g_signal_stop_emission_by_name (dialog, "response");
1644
1645                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1646                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1647                 gtk_dialog_run (GTK_DIALOG(note));
1648                 gtk_widget_destroy (note);
1649         }
1650 }
1651
1652
1653 gboolean
1654 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1655                                                      const gchar *certificate)
1656 {
1657         GtkWidget *note;
1658         gint response;
1659         ModestWindow *main_win;
1660         
1661         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1662                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1663                            __FUNCTION__);
1664                 return FALSE;
1665         }
1666
1667         /* don't create it */
1668         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1669         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1670         
1671         
1672         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1673                                            server_name);
1674         
1675         note = hildon_note_new_confirmation_add_buttons  (
1676                 GTK_WINDOW(main_win),
1677                 question,
1678                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1679                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1680                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1681                 NULL, NULL);
1682         
1683         g_signal_connect (G_OBJECT(note), "response", 
1684                           G_CALLBACK(on_cert_dialog_response),
1685                           (gpointer) certificate);
1686         
1687         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1688                                      GTK_WINDOW (note));
1689         response = gtk_dialog_run(GTK_DIALOG(note));
1690
1691         on_destroy_dialog (GTK_DIALOG(note));
1692         g_free (question);
1693         
1694         return response == GTK_RESPONSE_OK;
1695 }
1696
1697 gboolean
1698 modest_platform_run_alert_dialog (const gchar* prompt, 
1699                                   gboolean is_question)
1700 {       
1701         ModestWindow *main_win; 
1702
1703         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1704                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1705                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1706                 return is_question ? FALSE : TRUE;
1707         }
1708
1709         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1710         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1711         
1712         gboolean retval = TRUE;
1713         if (is_question) {
1714                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1715                  * when it is a question.
1716                  * Obviously, we need tinymail to use more specific error codes instead,
1717                  * so we know what buttons to show. */
1718                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1719                                                                               prompt));
1720                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1721                                              GTK_WINDOW (dialog));
1722                 
1723                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1724                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1725                 
1726                 on_destroy_dialog (GTK_DIALOG(dialog));         
1727         } else {
1728                 /* Just show the error text and use the default response: */
1729                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1730                                                         prompt);
1731         }
1732         return retval;
1733 }
1734
1735 /***************/
1736 typedef struct {
1737         GtkWindow *parent_window;
1738         ModestConnectedPerformer callback;
1739         TnyAccount *account;
1740         gpointer user_data;
1741         gchar *iap;
1742         TnyDevice *device;
1743 } OnWentOnlineInfo;
1744  
1745 static void 
1746 on_went_online_info_free (OnWentOnlineInfo *info)
1747 {
1748         /* And if we cleanup, we DO cleanup  :-)  */
1749         
1750         if (info->device)
1751                 g_object_unref (info->device);
1752         if (info->iap)
1753                 g_free (info->iap);
1754         if (info->parent_window)
1755                 g_object_unref (info->parent_window);
1756         if (info->account)
1757                 g_object_unref (info->account);
1758         
1759         g_slice_free (OnWentOnlineInfo, info);
1760         
1761         /* We're done ... */
1762         
1763         return;
1764 }
1765  
1766 static void
1767 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1768 {
1769         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1770  
1771         /* Now it's really time to callback to the caller. If going online didn't succeed,
1772          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1773          * canceled will be set. Etcetera etcetera. */
1774         
1775         if (info->callback) {
1776                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1777         }
1778         
1779         /* This is our last call, we must cleanup here if we didn't yet do that */
1780         on_went_online_info_free (info);
1781         
1782         return;
1783 }
1784  
1785  
1786 static void
1787 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1788 {
1789         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1790         info->iap = g_strdup (iap_id);
1791         
1792         if (canceled || err || !info->account) {
1793         
1794                 /* If there's a problem or if there's no account (then that's it for us, we callback
1795                  * the caller's callback now. He'll have to handle err or canceled, of course.
1796                  * We are not really online, as the account is not really online here ... */    
1797                 
1798                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1799                  * this info. We don't cleanup err, Tinymail does that! */
1800                 
1801                 if (info->callback) {
1802                         
1803                         /* info->account can be NULL here, this means that the user did not
1804                          * provide a nice account instance. We'll assume that the user knows
1805                          * what he's doing and is happy with just the device going online. 
1806                          * 
1807                          * We can't do magic, we don't know what account the user wants to
1808                          * see going online. So just the device goes online, end of story */
1809                         
1810                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1811                 }
1812                 
1813         } else if (info->account) {
1814                 
1815                 /* If there's no problem and if we have an account, we'll put the account
1816                  * online too. When done, the callback of bringing the account online
1817                  * will callback the caller's callback. This is the most normal case. */
1818  
1819                 info->device = TNY_DEVICE (g_object_ref (device));
1820                 
1821                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1822                                               on_account_went_online, info);
1823                 
1824                 /* The on_account_went_online cb frees up the info, go look if you
1825                  * don't believe me! (so we return here) */
1826                 
1827                 return;
1828         }
1829         
1830         /* We cleanup if we are not bringing the account online too */
1831         on_went_online_info_free (info);
1832  
1833         return; 
1834 }
1835         
1836 void 
1837 modest_platform_connect_and_perform (GtkWindow *parent_window, 
1838                                      TnyAccount *account, 
1839                                      ModestConnectedPerformer callback, 
1840                                      gpointer user_data)
1841 {
1842         gboolean device_online;
1843         TnyDevice *device;
1844         TnyConnectionStatus conn_status;
1845         OnWentOnlineInfo *info;
1846         gboolean user_requested;
1847         
1848         device = modest_runtime_get_device();
1849         device_online = tny_device_is_online (device);
1850
1851         /* Whether the connection is user requested or automatically
1852            requested, for example via D-Bus */
1853         user_requested = (parent_window) ? TRUE : FALSE;
1854
1855         /* If there is no account check only the device status */
1856         if (!account) {
1857                 
1858                 if (device_online) {
1859  
1860                         /* We promise to instantly perform the callback, so ... */
1861                         if (callback) {
1862                                 callback (FALSE, NULL, parent_window, account, user_data);
1863                         }
1864                         
1865                 } else {
1866                         
1867                         info = g_slice_new0 (OnWentOnlineInfo);
1868                         
1869                         info->iap = NULL;
1870                         info->device = NULL;
1871                         info->account = NULL;
1872                 
1873                         if (parent_window)
1874                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1875                         else
1876                                 info->parent_window = NULL;
1877                         info->user_data = user_data;
1878                         info->callback = callback;
1879                 
1880                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1881                                                               user_requested, on_conic_device_went_online, 
1882                                                               info);
1883  
1884                         /* We'll cleanup in on_conic_device_went_online */
1885                 }
1886  
1887                 /* The other code has no more reason to run. This is all that we can do for the
1888                  * caller (he should have given us a nice and clean account instance!). We
1889                  * can't do magic, we don't know what account he intends to bring online. So
1890                  * we'll just bring the device online (and await his false bug report). */
1891                 
1892                 return;
1893         }
1894  
1895         
1896         /* Return if the account is already connected */
1897         
1898         conn_status = tny_account_get_connection_status (account);
1899         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1900  
1901                 /* We promise to instantly perform the callback, so ... */
1902                 if (callback) {
1903                         callback (FALSE, NULL, parent_window, account, user_data);
1904                 }
1905                 
1906                 return;
1907         }
1908         
1909         /* Else, we are in a state that requires that we go online before we
1910          * call the caller's callback. */
1911         
1912         info = g_slice_new0 (OnWentOnlineInfo);
1913         
1914         info->device = NULL;
1915         info->iap = NULL;
1916         info->account = TNY_ACCOUNT (g_object_ref (account));
1917         
1918         if (parent_window)
1919                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1920         else
1921                 info->parent_window = NULL;
1922         
1923         /* So we'll put the callback away for later ... */
1924         
1925         info->user_data = user_data;
1926         info->callback = callback;
1927         
1928         if (!device_online) {
1929  
1930                 /* If also the device is offline, then we connect both the device 
1931                  * and the account */
1932                 
1933                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1934                                                       user_requested, on_conic_device_went_online, 
1935                                                       info);
1936                 
1937         } else {
1938                 
1939                 /* If the device is online, we'll just connect the account */
1940                 
1941                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1942                                               on_account_went_online, info);
1943         }
1944  
1945         /* The info gets freed by on_account_went_online or on_conic_device_went_online
1946          * in both situations, go look if you don't believe me! */
1947         
1948         return;
1949 }
1950  
1951 void 
1952 modest_platform_connect_and_perform_if_network_account (GtkWindow *parent_window, 
1953                                                         TnyAccount *account,
1954                                                         ModestConnectedPerformer callback, 
1955                                                         gpointer user_data)
1956 {
1957         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1958                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1959                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1960                         
1961                         /* This IS a local account like a maildir account, which does not require 
1962                          * a connection. (original comment had a vague assumption in its language
1963                          * usage. There's no assuming needed, this IS what it IS: a local account), */
1964  
1965                         /* We promise to instantly perform the callback, so ... */
1966                         if (callback) {
1967                                 callback (FALSE, NULL, parent_window, account, user_data);
1968                         }
1969                         
1970                         return;
1971                 }
1972         }
1973  
1974         modest_platform_connect_and_perform (parent_window, account, callback, user_data);
1975  
1976         return;
1977 }
1978  
1979 void
1980 modest_platform_connect_and_perform_if_network_folderstore (GtkWindow *parent_window, 
1981                                                             TnyFolderStore *folder_store, 
1982                                                             ModestConnectedPerformer callback, 
1983                                                             gpointer user_data)
1984 {
1985         if (!folder_store) {
1986  
1987                 /* We promise to instantly perform the callback, so ... */
1988                 if (callback) {
1989                         callback (FALSE, NULL, parent_window, NULL, user_data);
1990                 }
1991                 return; 
1992                 
1993                 /* Original comment: Maybe it is something local. */
1994                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
1995                 
1996         }
1997         
1998         if (TNY_IS_FOLDER (folder_store)) {
1999                 /* Get the folder's parent account: */
2000                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
2001                 if (account != NULL) {
2002                         modest_platform_connect_and_perform_if_network_account (NULL, account, callback, user_data);
2003                         g_object_unref (account);
2004                 }
2005         } else if (TNY_IS_ACCOUNT (folder_store)) {
2006                 /* Use the folder store as an account: */
2007                 modest_platform_connect_and_perform_if_network_account (NULL, TNY_ACCOUNT (folder_store), callback, user_data);
2008         }
2009  
2010         return;
2011 }