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