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