* Merged revisions 4870 & 5005 from trunk, both revisions have patches for well...
[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
864         /* Some locales like pt_BR need this to get the full window
865            title shown */
866         gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1);
867
868         /* Create the hbox */
869         hbox = gtk_hbox_new (FALSE, 12);
870         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
871         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
872
873         /* Add hbox to dialog */
874         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
875                             hbox, FALSE, FALSE, 0);
876         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
877                                      GTK_WINDOW (dialog));
878         gtk_widget_show_all (GTK_WIDGET(dialog));
879                 
880         result = gtk_dialog_run (GTK_DIALOG(dialog));
881         if (result == GTK_RESPONSE_ACCEPT)
882                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
883
884         gtk_widget_destroy (dialog);
885
886         while (gtk_events_pending ())
887                 gtk_main_iteration ();
888
889         return result;
890 }
891
892 gint
893 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
894                                        TnyFolderStore *parent_folder,
895                                        gchar *suggested_name,
896                                        gchar **folder_name)
897 {
898         gchar *real_suggested_name = NULL, *tmp = NULL;
899         gint result;
900
901         if(suggested_name == NULL)
902         {
903                 const gchar *default_name = _("mcen_ia_default_folder_name");
904                 unsigned int i;
905                 gchar num_str[3];
906
907                 for(i = 0; i < 100; ++ i) {
908                         gboolean exists = FALSE;
909
910                         sprintf(num_str, "%.2u", i);
911
912                         if (i == 0)
913                                 real_suggested_name = g_strdup (default_name);
914                         else
915                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
916                                                                        num_str);
917                         exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
918                                                                             real_suggested_name,
919                                                                             TRUE);
920
921                         if (!exists)
922                                 break;
923
924                         g_free (real_suggested_name);
925                 }
926
927                 /* Didn't find a free number */
928                 if (i == 100)
929                         real_suggested_name = g_strdup (default_name);
930         } else {
931                 real_suggested_name = suggested_name;
932         }
933
934         tmp = g_strconcat (_("mcen_fi_new_folder_name"), ":", NULL);
935         result = modest_platform_run_folder_name_dialog (parent_window, 
936                                                          parent_folder,
937                                                          _("mcen_ti_new_folder"),
938                                                          tmp,
939                                                          real_suggested_name,
940                                                          folder_name);
941         g_free (tmp);
942
943         if (suggested_name == NULL)
944                 g_free(real_suggested_name);
945
946         return result;
947 }
948
949 gint
950 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
951                                           TnyFolderStore *parent_folder,
952                                           const gchar *suggested_name,
953                                           gchar **folder_name)
954 {
955         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
956
957         return modest_platform_run_folder_name_dialog (parent_window, 
958                                                        parent_folder,
959                                                        _HL("ckdg_ti_rename_folder"),
960                                                        _HL("ckdg_fi_rename_name"),
961                                                        suggested_name,
962                                                        folder_name);
963 }
964
965
966
967 static void
968 on_destroy_dialog (GtkDialog *dialog)
969 {
970         gtk_widget_destroy (GTK_WIDGET(dialog));
971         if (gtk_events_pending ())
972                 gtk_main_iteration ();
973 }
974
975 gint
976 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
977                                          const gchar *message)
978 {
979         GtkWidget *dialog;
980         gint response;
981         
982         dialog = hildon_note_new_confirmation (parent_window, message);
983         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
984                                      GTK_WINDOW (dialog));
985
986         response = gtk_dialog_run (GTK_DIALOG (dialog));
987
988         on_destroy_dialog (GTK_DIALOG(dialog));
989         
990         while (gtk_events_pending ())
991                 gtk_main_iteration ();
992
993         return response;
994 }
995
996 gint
997 modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window,
998                                                       const gchar *message,
999                                                       const gchar *button_accept,
1000                                                       const gchar *button_cancel)
1001 {
1002         GtkWidget *dialog;
1003         gint response;
1004         
1005         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1006                                                            button_accept, GTK_RESPONSE_ACCEPT,
1007                                                            button_cancel, GTK_RESPONSE_CANCEL,
1008                                                            NULL);
1009         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1010                                      GTK_WINDOW (dialog));
1011
1012         response = gtk_dialog_run (GTK_DIALOG (dialog));
1013
1014         on_destroy_dialog (GTK_DIALOG(dialog));
1015         
1016         while (gtk_events_pending ())
1017                 gtk_main_iteration ();
1018
1019         return response;
1020 }
1021         
1022 gint
1023 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
1024                                    const gchar *message)
1025 {
1026         GtkWidget *dialog;
1027         gint response;
1028         
1029         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1030                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
1031                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
1032                                                            NULL);
1033         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
1034         response = gtk_dialog_run (GTK_DIALOG (dialog));
1035         
1036         on_destroy_dialog (GTK_DIALOG(dialog));
1037
1038         while (gtk_events_pending ())
1039                 gtk_main_iteration ();
1040
1041         return response;
1042 }
1043
1044
1045
1046 void
1047 modest_platform_run_information_dialog (GtkWindow *parent_window,
1048                                         const gchar *message,
1049                                         gboolean block)
1050 {
1051         GtkWidget *note;
1052         
1053         note = hildon_note_new_information (parent_window, message);
1054         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1055                                      GTK_WINDOW (note));
1056         
1057         if (block) {
1058                 gtk_dialog_run (GTK_DIALOG (note));
1059         
1060                 on_destroy_dialog (GTK_DIALOG (note));
1061
1062                 while (gtk_events_pending ())
1063                         gtk_main_iteration ();
1064         } else {
1065                 g_signal_connect_swapped (note,
1066                                           "response", 
1067                                           G_CALLBACK (on_destroy_dialog),
1068                                           note);
1069
1070                 gtk_widget_show_all (note);
1071         }
1072 }
1073
1074 typedef struct _ConnectAndWaitData {
1075         GMutex *mutex;
1076         GMainLoop *wait_loop;
1077         gboolean has_callback;
1078         gulong handler;
1079 } ConnectAndWaitData;
1080
1081
1082 static void
1083 quit_wait_loop (TnyAccount *account,
1084                 ConnectAndWaitData *data) 
1085 {
1086         /* Set the has_callback to TRUE (means that the callback was
1087            executed and wake up every code waiting for cond to be
1088            TRUE */
1089         g_mutex_lock (data->mutex);
1090         data->has_callback = TRUE;
1091         if (data->wait_loop)
1092                 g_main_loop_quit (data->wait_loop);
1093         g_mutex_unlock (data->mutex);
1094 }
1095
1096 static void
1097 on_connection_status_changed (TnyAccount *account, 
1098                               TnyConnectionStatus status,
1099                               gpointer user_data)
1100 {
1101         TnyConnectionStatus conn_status;
1102         ConnectAndWaitData *data;
1103                         
1104         /* Ignore if reconnecting or disconnected */
1105         conn_status = tny_account_get_connection_status (account);
1106         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1107             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1108                 return;
1109
1110         /* Remove the handler */
1111         data = (ConnectAndWaitData *) user_data;
1112         g_signal_handler_disconnect (account, data->handler);
1113
1114         /* Quit from wait loop */
1115         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1116 }
1117
1118 static void
1119 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1120                                     gboolean canceled, 
1121                                     GError *err, 
1122                                     gpointer user_data)
1123 {
1124         /* Quit from wait loop */
1125         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1126 }
1127
1128 gboolean 
1129 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1130                                   TnyAccount *account)
1131 {
1132         ConnectAndWaitData *data = NULL;
1133         gboolean device_online;
1134         TnyDevice *device;
1135         TnyConnectionStatus conn_status;
1136         gboolean user_requested;
1137         
1138         device = modest_runtime_get_device();
1139         device_online = tny_device_is_online (device);
1140
1141         /* Whether the connection is user requested or automatically
1142            requested, for example via D-Bus */
1143         user_requested = (parent_window) ? TRUE : FALSE;
1144
1145         /* If there is no account check only the device status */
1146         if (!account) {
1147                 if (device_online)
1148                         return TRUE;
1149                 else
1150                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1151                                                                NULL, user_requested);
1152         }
1153
1154         /* Return if the account is already connected */
1155         conn_status = tny_account_get_connection_status (account);
1156         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1157                 return TRUE;
1158
1159         /* Create the helper */
1160         data = g_slice_new0 (ConnectAndWaitData);
1161         data->mutex = g_mutex_new ();
1162         data->has_callback = FALSE;
1163
1164         /* Connect the device */
1165         if (!device_online) {
1166                 /* Track account connection status changes */
1167                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1168                                                   G_CALLBACK (on_connection_status_changed),
1169                                                   data);
1170                 /* Try to connect the device */
1171                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1172                                                                 NULL, user_requested);
1173
1174                 /* If the device connection failed then exit */
1175                 if (!device_online && data->handler)
1176                         goto frees;
1177         } else {
1178                 /* Force a reconnection of the account */
1179                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1180                                               on_tny_camel_account_set_online_cb, data);
1181         }
1182
1183         /* Wait until the callback is executed */
1184         g_mutex_lock (data->mutex);
1185         if (!data->has_callback) {
1186                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1187                 gdk_threads_leave ();
1188                 g_mutex_unlock (data->mutex);
1189                 g_main_loop_run (data->wait_loop);
1190                 g_mutex_lock (data->mutex);
1191                 gdk_threads_enter ();
1192         }
1193         g_mutex_unlock (data->mutex);
1194
1195  frees:
1196         if (data) {
1197                 if (g_signal_handler_is_connected (account, data->handler))
1198                         g_signal_handler_disconnect (account, data->handler);
1199                 g_mutex_free (data->mutex);
1200                 g_main_loop_unref (data->wait_loop);
1201                 g_slice_free (ConnectAndWaitData, data);
1202         }
1203
1204         conn_status = tny_account_get_connection_status (account);
1205         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1206 }
1207
1208 gboolean 
1209 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1210 {
1211         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1212                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1213                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1214                         /* This must be a maildir account, which does not require a connection: */
1215                         return TRUE;
1216                 }
1217         }
1218
1219         return modest_platform_connect_and_wait (parent_window, account);
1220 }
1221
1222 gboolean 
1223 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1224 {
1225         if (!folder_store)
1226                 return TRUE; /* Maybe it is something local. */
1227                 
1228         gboolean result = TRUE;
1229         if (TNY_IS_FOLDER (folder_store)) {
1230                 /* Get the folder's parent account: */
1231                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1232                 if (account != NULL) {
1233                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1234                         g_object_unref (account);
1235                 }
1236         } else if (TNY_IS_ACCOUNT (folder_store)) {
1237                 /* Use the folder store as an account: */
1238                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1239         }
1240
1241         return result;
1242 }
1243
1244 void
1245 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1246                                  ModestSortDialogType type)
1247 {
1248         GtkWidget *dialog = NULL;
1249
1250         /* Build dialog */
1251         dialog = hildon_sort_dialog_new (parent_window);
1252         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1253                                      GTK_WINDOW (dialog));
1254
1255         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1256                                         "applications_email_sort",
1257                                         modest_maemo_utils_get_osso_context());
1258
1259         /* Fill sort keys */
1260         switch (type) {
1261         case MODEST_SORT_HEADERS:
1262                 launch_sort_headers_dialog (parent_window, 
1263                                             HILDON_SORT_DIALOG(dialog));
1264                 break;
1265         }
1266         
1267         /* Free */
1268         on_destroy_dialog (GTK_DIALOG(dialog));
1269 }
1270
1271
1272 gboolean 
1273 modest_platform_set_update_interval (guint minutes)
1274 {
1275 #ifdef MODEST_HAVE_LIBALARM
1276         
1277         ModestConf *conf = modest_runtime_get_conf ();
1278         if (!conf)
1279                 return FALSE;
1280                 
1281         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1282
1283         /* Delete any existing alarm,
1284          * because we will replace it: */
1285         if (alarm_cookie) {
1286                 if (alarm_event_del(alarm_cookie) != 1)
1287                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1288                 alarm_cookie = 0;
1289                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1290         }
1291         
1292         /* 0 means no updates: */
1293         if (minutes == 0)
1294                 return TRUE;
1295         
1296      
1297         /* Register alarm: */
1298         
1299         /* Set the interval in alarm_event_t structure: */
1300         alarm_event_t *event = g_new0(alarm_event_t, 1);
1301         event->alarm_time = minutes * 60; /* seconds */
1302         
1303         /* Set recurrence every few minutes: */
1304         event->recurrence = minutes;
1305         event->recurrence_count = -1; /* Means infinite */
1306
1307         /* Specify what should happen when the alarm happens:
1308          * It should call this D-Bus method: */
1309          
1310         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1311         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1312         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1313         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1314
1315         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1316          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1317          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1318          * This is why we want to use the Alarm API instead of just g_timeout_add().
1319          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1320          * ALARM_EVENT_CONNECTED will prevent the alarm from being called in case that the device is offline
1321          */
1322         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION | ALARM_EVENT_CONNECTED;
1323         
1324         alarm_cookie = alarm_event_add (event);
1325
1326         /* now, free it */
1327         alarm_event_free (event);
1328         
1329         /* Store the alarm ID in GConf, so we can remove it later:
1330          * This is apparently valid between application instances. */
1331         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1332         
1333         if (!alarm_cookie) {
1334             /* Error */
1335             const alarm_error_t alarm_error = alarmd_get_error ();
1336             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1337             
1338             /* Give people some clue: */
1339             /* The alarm API should have a function for this: */
1340             if (alarm_error == ALARMD_ERROR_DBUS) {
1341                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1342             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1343                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1344             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1345                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1346             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1347                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1348             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1349                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1350             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1351                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1352             }
1353             
1354             return FALSE;
1355         }
1356 #endif /* MODEST_HAVE_LIBALARM */       
1357         return TRUE;
1358 }
1359
1360 void
1361 modest_platform_push_email_notification(void)
1362 {
1363         gboolean play_sound;
1364         ModestWindow *main_window;
1365         gboolean screen_on = TRUE, app_in_foreground;
1366
1367         /* Check whether or not we should play a sound */
1368         play_sound = modest_conf_get_bool (modest_runtime_get_conf (),
1369                                            MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1370                                            NULL);
1371
1372         /* Get the screen status */
1373         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1374         if (main_window)
1375                 screen_on = modest_main_window_screen_is_on (MODEST_MAIN_WINDOW (main_window));
1376
1377         /* Get the window status */
1378         app_in_foreground = hildon_program_get_is_topmost (hildon_program_get_instance ());
1379
1380         /* If the screen is on and the app is in the
1381            foreground we don't show anything */
1382         if (!(screen_on && app_in_foreground)) {
1383                 /* Play a sound */
1384                 if (play_sound)
1385                         hildon_play_system_sound (MODEST_NEW_MAIL_SOUND_FILE);
1386
1387                 /* Activate LED. This must be deactivated by
1388                    modest_platform_remove_new_mail_notifications */
1389 #ifdef MODEST_HAVE_MCE
1390                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1391                                      MCE_SERVICE,
1392                                      MCE_REQUEST_PATH,
1393                                      MCE_REQUEST_IF,
1394                                      MCE_ACTIVATE_LED_PATTERN,
1395                                      NULL,
1396                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1397                                      DBUS_TYPE_INVALID);
1398 #endif
1399         }
1400 }
1401
1402 void 
1403 modest_platform_on_new_headers_received (TnyList *header_list,
1404                                          gboolean show_visual)
1405 {
1406         g_return_if_fail (TNY_IS_LIST(header_list));
1407
1408         if (tny_list_get_length(header_list) == 0) {
1409                 g_warning ("%s: header list is empty", __FUNCTION__);
1410                 return;
1411         }
1412         
1413         if (!show_visual) {
1414                 modest_platform_push_email_notification ();
1415                 /* We do a return here to avoid indentation with an else */
1416                 return;
1417         }
1418
1419 #ifdef MODEST_HAVE_HILDON_NOTIFY
1420         gboolean play_sound;
1421
1422         /* Check whether or not we should play a sound */
1423         play_sound = modest_conf_get_bool (modest_runtime_get_conf (),
1424                                            MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1425                                            NULL);
1426
1427         HildonNotification *notification;
1428         TnyIterator *iter;
1429         GSList *notifications_list = NULL;
1430
1431         /* Get previous notifications ids */
1432         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1433                                                    MODEST_CONF_NOTIFICATION_IDS, 
1434                                                    MODEST_CONF_VALUE_INT, NULL);
1435
1436         iter = tny_list_create_iterator (header_list);
1437         while (!tny_iterator_is_done (iter)) {
1438                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1439                 const gchar *display_date;
1440                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1441                 TnyFolder *folder = tny_header_get_folder (header);
1442                 gboolean first_notification = TRUE;
1443                 gint notif_id;
1444                 gchar *str;
1445
1446                 /* constant string, don't free */
1447                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1448
1449                 display_address = tny_header_dup_from (header);
1450                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1451                 
1452                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1453                 str = tny_header_dup_subject (header);
1454                 notification = hildon_notification_new (summary,
1455                                                         str,
1456                                                         "qgn_list_messagin",
1457                                                         "email.arrive");
1458                 g_free (str);
1459                 /* Create the message URL */
1460                 str = tny_header_dup_uid (header);
1461                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1462                                        str);
1463                 g_free (str);
1464
1465                 hildon_notification_add_dbus_action(notification,
1466                                                     "default",
1467                                                     "Cancel",
1468                                                     MODEST_DBUS_SERVICE,
1469                                                     MODEST_DBUS_OBJECT,
1470                                                     MODEST_DBUS_IFACE,
1471                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1472                                                     G_TYPE_STRING, url,
1473                                                     -1);
1474
1475                 /* Play sound if the user wants. Show the LED
1476                    pattern. Show and play just one */
1477                 if (G_UNLIKELY (first_notification)) {
1478                         first_notification = FALSE;
1479                         if (play_sound)  {
1480                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1481                                                                     "sound-file", MODEST_NEW_MAIL_SOUND_FILE);
1482                         }
1483
1484                         /* Set the led pattern */
1485                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1486                                                             "dialog-type", 4);
1487                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1488                                                             "led-pattern",
1489                                                             MODEST_NEW_MAIL_LIGHTING_PATTERN);                  
1490                 }
1491
1492                 /* Notify. We need to do this in an idle because this function
1493                    could be called from a thread */
1494                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1495
1496                 /* Save id in the list */
1497                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1498                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1499                 /* We don't listen for the "closed" signal, because we
1500                    don't care about if the notification was removed or
1501                    not to store the list in gconf */
1502         
1503                 /* Free & carry on */
1504                 g_free (display_address);
1505                 g_free (summary);
1506                 g_free (url);
1507                 g_object_unref (folder);
1508                 g_object_unref (header);
1509                 tny_iterator_next (iter);
1510         }
1511         g_object_unref (iter);
1512
1513         /* Save the ids */
1514         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1515                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1516
1517         g_slist_free (notifications_list);
1518         
1519 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1520 }
1521
1522 void
1523 modest_platform_remove_new_mail_notifications (gboolean only_visuals) 
1524 {
1525         if (only_visuals) {
1526 #ifdef MODEST_HAVE_MCE
1527                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1528                                      MCE_SERVICE,
1529                                      MCE_REQUEST_PATH,
1530                                      MCE_REQUEST_IF,
1531                                      MCE_DEACTIVATE_LED_PATTERN,
1532                                      NULL,
1533                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1534                                      DBUS_TYPE_INVALID);
1535 #endif
1536                 return;
1537         }
1538
1539 #ifdef MODEST_HAVE_HILDON_NOTIFY
1540         GSList *notif_list = NULL;
1541
1542         /* Get previous notifications ids */
1543         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1544                                            MODEST_CONF_NOTIFICATION_IDS, 
1545                                            MODEST_CONF_VALUE_INT, NULL);
1546
1547         while (notif_list) {
1548                 gint notif_id;
1549                 NotifyNotification *notif;
1550
1551                 /* Nasty HACK to remove the notifications, set the id
1552                    of the existing ones and then close them */
1553                 notif_id = GPOINTER_TO_INT(notif_list->data);
1554                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1555                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1556
1557                 /* Close the notification, note that some ids could be
1558                    already invalid, but we don't care because it does
1559                    not fail */
1560                 notify_notification_close(notif, NULL);
1561                 g_object_unref(notif);
1562
1563                 /* Delete the link, it's like going to the next */
1564                 notif_list = g_slist_delete_link (notif_list, notif_list);
1565         }
1566
1567         /* Save the ids */
1568         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1569                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1570
1571         g_slist_free (notif_list);
1572
1573 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1574 }
1575
1576
1577
1578 GtkWidget * 
1579 modest_platform_get_global_settings_dialog ()
1580 {
1581         return modest_maemo_global_settings_dialog_new ();
1582 }
1583
1584 void
1585 modest_platform_show_help (GtkWindow *parent_window, 
1586                            const gchar *help_id)
1587 {
1588         osso_return_t result;
1589         g_return_if_fail (help_id);
1590
1591         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1592                                    help_id, HILDON_HELP_SHOW_DIALOG);
1593         
1594         if (result != OSSO_OK) {
1595                 gchar *error_msg;
1596                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1597                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1598                                                 NULL,
1599                                                 error_msg);
1600                 g_free (error_msg);
1601         }
1602 }
1603
1604 void 
1605 modest_platform_show_search_messages (GtkWindow *parent_window)
1606 {
1607         osso_return_t result = OSSO_ERROR;
1608         
1609         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1610                                              "osso_global_search",
1611                                              "search_email", NULL, DBUS_TYPE_INVALID);
1612
1613         if (result != OSSO_OK) {
1614                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1615         }
1616 }
1617
1618 void 
1619 modest_platform_show_addressbook (GtkWindow *parent_window)
1620 {
1621         osso_return_t result = OSSO_ERROR;
1622         
1623         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1624                                              "osso_addressbook",
1625                                              "top_application", NULL, DBUS_TYPE_INVALID);
1626
1627         if (result != OSSO_OK) {
1628                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1629         }
1630 }
1631
1632 GtkWidget *
1633 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1634 {
1635         GtkWidget *widget = modest_folder_view_new (query);
1636
1637         /* Show one account by default */
1638         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1639                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1640
1641         /* Restore settings */
1642         modest_widget_memory_restore (modest_runtime_get_conf(), 
1643                                       G_OBJECT (widget),
1644                                       MODEST_CONF_FOLDER_VIEW_KEY);
1645
1646         return widget;
1647 }
1648
1649 void
1650 banner_finish (gpointer data, GObject *object)
1651 {
1652         ModestWindowMgr *mgr = (ModestWindowMgr *) data;
1653         modest_window_mgr_unregister_banner (mgr);
1654         g_object_unref (mgr);
1655 }
1656
1657 void 
1658 modest_platform_information_banner (GtkWidget *parent,
1659                                     const gchar *icon_name,
1660                                     const gchar *text)
1661 {
1662         GtkWidget *banner, *banner_parent = NULL;
1663         ModestWindowMgr *mgr;
1664
1665         mgr = modest_runtime_get_window_mgr ();
1666         if (parent && GTK_IS_WINDOW (parent)) {
1667                 /* If the window is the active one then show the
1668                    banner on top of this window */
1669                 if (gtk_window_is_active (GTK_WINDOW (parent)))
1670                         banner_parent = parent;
1671                 /* If the window is not the topmost but it's visible
1672                    (it's minimized for example) then show the banner
1673                    with no parent */ 
1674                 else if (GTK_WIDGET_VISIBLE (parent))
1675                         banner_parent = NULL;
1676                 /* If the window is hidden (like the main window when
1677                    running in the background) then do not show
1678                    anything */
1679                 else 
1680                         return;
1681         }
1682
1683
1684         banner = hildon_banner_show_information (banner_parent, icon_name, text);
1685
1686         modest_window_mgr_register_banner (mgr);
1687         g_object_ref (mgr);
1688         g_object_weak_ref ((GObject *) banner, banner_finish, mgr);
1689 }
1690
1691 void
1692 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1693                                                  const gchar *icon_name,
1694                                                  const gchar *text,
1695                                                  gint timeout)
1696 {
1697         GtkWidget *banner;
1698         banner = hildon_banner_show_information (parent, icon_name, text);
1699         hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1700 }
1701
1702 GtkWidget *
1703 modest_platform_animation_banner (GtkWidget *parent,
1704                                   const gchar *animation_name,
1705                                   const gchar *text)
1706 {
1707         GtkWidget *inf_note = NULL;
1708
1709         g_return_val_if_fail (text != NULL, NULL);
1710
1711         /* If the parent is not visible then do not show */
1712         if (parent && !GTK_WIDGET_VISIBLE (parent))
1713                 return NULL;
1714
1715         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1716
1717         return inf_note;
1718 }
1719
1720 typedef struct
1721 {
1722         GMainLoop* loop;
1723         TnyAccount *account;
1724         gboolean is_online;
1725         gint count_tries;
1726 } CheckAccountIdleData;
1727
1728 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1729
1730 static gboolean 
1731 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1732 {
1733         gboolean stop_trying = FALSE;
1734         g_return_val_if_fail (data && data->account, FALSE);
1735         
1736         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1737                 tny_account_get_connection_status (data->account));     
1738         
1739         if (data && data->account && 
1740                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1741                  * after which the account is likely to be usable, or never likely to be usable soon: */
1742                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1743         {
1744                 data->is_online = TRUE;
1745                 
1746                 stop_trying = TRUE;
1747         } else {
1748                 /* Give up if we have tried too many times: */
1749                 if (data->count_tries >= NUMBER_OF_TRIES) {
1750                         stop_trying = TRUE;
1751                 } else {
1752                         /* Wait for another timeout: */
1753                         ++(data->count_tries);
1754                 }
1755         }
1756         
1757         if (stop_trying) {
1758                 /* Allow the function that requested this idle callback to continue: */
1759                 if (data->loop)
1760                         g_main_loop_quit (data->loop);
1761                         
1762                 if (data->account)
1763                         g_object_unref (data->account);
1764                 
1765                 return FALSE; /* Don't call this again. */
1766         } else {
1767                 return TRUE; /* Call this timeout callback again. */
1768         }
1769 }
1770
1771 /* Return TRUE immediately if the account is already online,
1772  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1773  * soon as the account is online, or FALSE if the account does 
1774  * not become online in the NUMBER_OF_TRIES seconds.
1775  * This is useful when the D-Bus method was run immediately after 
1776  * the application was started (when using D-Bus activation), 
1777  * because the account usually takes a short time to go online.
1778  * The return value is maybe not very useful.
1779  */
1780 gboolean
1781 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1782 {
1783         g_return_val_if_fail (account, FALSE);
1784         
1785         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1786         
1787         if (!tny_device_is_online (modest_runtime_get_device())) {
1788                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1789                 return FALSE;
1790         }
1791         
1792         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1793          * so we avoid wait unnecessarily: */
1794         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1795                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1796                 return TRUE;            
1797         }
1798                 
1799         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1800                 __FUNCTION__, tny_account_get_connection_status (account));
1801         
1802         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1803          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1804          * we want to avoid. */
1805         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1806                 return TRUE;
1807                 
1808         /* This blocks on the result: */
1809         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1810         data->is_online = FALSE;
1811         data->account = account;
1812         g_object_ref (data->account);
1813         data->count_tries = 0;
1814                 
1815         GMainContext *context = NULL; /* g_main_context_new (); */
1816         data->loop = g_main_loop_new (context, FALSE /* not running */);
1817
1818         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1819
1820         /* This main loop will run until the idle handler has stopped it: */
1821         g_main_loop_run (data->loop);
1822
1823         g_main_loop_unref (data->loop);
1824         /* g_main_context_unref (context); */
1825
1826         g_slice_free (CheckAccountIdleData, data);
1827         
1828         return data->is_online; 
1829 }
1830
1831
1832
1833 static void
1834 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1835 {
1836         /* GTK_RESPONSE_HELP means we need to show the certificate */
1837         if (response_id == GTK_RESPONSE_APPLY) {
1838                 GtkWidget *note;
1839                 gchar *msg;
1840                 
1841                 /* Do not close the dialog */
1842                 g_signal_stop_emission_by_name (dialog, "response");
1843
1844                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1845                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1846                 gtk_dialog_run (GTK_DIALOG(note));
1847                 gtk_widget_destroy (note);
1848         }
1849 }
1850
1851
1852 gboolean
1853 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1854                                                      const gchar *certificate)
1855 {
1856         GtkWidget *note;
1857         gint response;
1858         ModestWindow *main_win;
1859         
1860         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1861                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1862                            __FUNCTION__);
1863                 return FALSE;
1864         }
1865
1866         /* don't create it */
1867         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1868         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1869         
1870         
1871         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1872                                            server_name);
1873         
1874         /* We use GTK_RESPONSE_APPLY because we want the button in the
1875            middle of OK and CANCEL the same as the browser does for
1876            example. With GTK_RESPONSE_HELP the view button is aligned
1877            to the left while the other two to the right */
1878         note = hildon_note_new_confirmation_add_buttons  (
1879                 GTK_WINDOW(main_win),
1880                 question,
1881                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1882                 _("mcen_bd_view"),          GTK_RESPONSE_APPLY,   /* abusing this... */
1883                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1884                 NULL, NULL);
1885         
1886         g_signal_connect (G_OBJECT(note), "response", 
1887                           G_CALLBACK(on_cert_dialog_response),
1888                           (gpointer) certificate);
1889         
1890         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1891                                      GTK_WINDOW (note));
1892         response = gtk_dialog_run(GTK_DIALOG(note));
1893
1894         on_destroy_dialog (GTK_DIALOG(note));
1895         g_free (question);
1896         
1897         return response == GTK_RESPONSE_OK;
1898 }
1899
1900 gboolean
1901 modest_platform_run_alert_dialog (const gchar* prompt, 
1902                                   gboolean is_question)
1903 {       
1904         ModestWindow *main_win; 
1905
1906         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1907                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1908                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1909                 return is_question ? FALSE : TRUE;
1910         }
1911
1912         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1913         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1914         
1915         gboolean retval = TRUE;
1916         if (is_question) {
1917                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1918                  * when it is a question.
1919                  * Obviously, we need tinymail to use more specific error codes instead,
1920                  * so we know what buttons to show. */
1921                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1922                                                                               prompt));
1923                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1924                                              GTK_WINDOW (dialog));
1925                 
1926                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1927                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1928                 
1929                 on_destroy_dialog (GTK_DIALOG(dialog));         
1930         } else {
1931                 /* Just show the error text and use the default response: */
1932                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1933                                                         prompt, FALSE);
1934         }
1935         return retval;
1936 }
1937
1938 /***************/
1939 typedef struct {
1940         GtkWindow *parent_window;
1941         ModestConnectedPerformer callback;
1942         TnyAccount *account;
1943         gpointer user_data;
1944         gchar *iap;
1945         TnyDevice *device;
1946 } OnWentOnlineInfo;
1947  
1948 static void 
1949 on_went_online_info_free (OnWentOnlineInfo *info)
1950 {
1951         /* And if we cleanup, we DO cleanup  :-)  */
1952         
1953         if (info->device)
1954                 g_object_unref (info->device);
1955         if (info->iap)
1956                 g_free (info->iap);
1957         if (info->parent_window)
1958                 g_object_unref (info->parent_window);
1959         if (info->account)
1960                 g_object_unref (info->account);
1961         
1962         g_slice_free (OnWentOnlineInfo, info);
1963         
1964         /* We're done ... */
1965         
1966         return;
1967 }
1968  
1969 static void
1970 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1971 {
1972         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1973  
1974         /* Now it's really time to callback to the caller. If going online didn't succeed,
1975          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1976          * canceled will be set. Etcetera etcetera. */
1977
1978         if (tny_account_get_connection_status (TNY_ACCOUNT (account)) == TNY_CONNECTION_STATUS_CONNECTED)
1979                 g_debug ("------------ %s %s CONECTADA", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT (account)));
1980         else
1981                 g_debug ("------------ %s %s NO conectada", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT (account)));
1982         
1983         if (info->callback) {
1984                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1985         }
1986         
1987         /* This is our last call, we must cleanup here if we didn't yet do that */
1988         on_went_online_info_free (info);
1989         
1990         return;
1991 }
1992  
1993  
1994 static void
1995 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1996 {
1997         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1998         info->iap = g_strdup (iap_id);
1999         
2000         if (canceled || err || !info->account) {
2001         
2002                 /* If there's a problem or if there's no account (then that's it for us, we callback
2003                  * the caller's callback now. He'll have to handle err or canceled, of course.
2004                  * We are not really online, as the account is not really online here ... */    
2005                 
2006                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
2007                  * this info. We don't cleanup err, Tinymail does that! */
2008                 
2009                 if (info->callback) {
2010                         
2011                         /* info->account can be NULL here, this means that the user did not
2012                          * provide a nice account instance. We'll assume that the user knows
2013                          * what he's doing and is happy with just the device going online. 
2014                          * 
2015                          * We can't do magic, we don't know what account the user wants to
2016                          * see going online. So just the device goes online, end of story */
2017                         
2018                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2019                 }
2020                 
2021         } else if (info->account) {
2022                 
2023                 /* If there's no problem and if we have an account, we'll put the account
2024                  * online too. When done, the callback of bringing the account online
2025                  * will callback the caller's callback. This is the most normal case. */
2026  
2027                 info->device = TNY_DEVICE (g_object_ref (device));
2028                 
2029                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
2030                                               on_account_went_online, info);
2031                 
2032                 /* The on_account_went_online cb frees up the info, go look if you
2033                  * don't believe me! (so we return here) */
2034                 
2035                 return;
2036         }
2037         
2038         /* We cleanup if we are not bringing the account online too */
2039         on_went_online_info_free (info);
2040  
2041         return; 
2042 }
2043         
2044 void 
2045 modest_platform_connect_and_perform (GtkWindow *parent_window, 
2046                                      gboolean force,
2047                                      TnyAccount *account, 
2048                                      ModestConnectedPerformer callback, 
2049                                      gpointer user_data)
2050 {
2051         gboolean device_online;
2052         TnyDevice *device;
2053         TnyConnectionStatus conn_status;
2054         OnWentOnlineInfo *info;
2055         
2056         device = modest_runtime_get_device();
2057         device_online = tny_device_is_online (device);
2058
2059         /* If there is no account check only the device status */
2060         if (!account) {
2061                 
2062                 if (device_online) {
2063  
2064                         /* We promise to instantly perform the callback, so ... */
2065                         if (callback) {
2066                                 callback (FALSE, NULL, parent_window, account, user_data);
2067                         }
2068                         
2069                 } else {
2070                         
2071                         info = g_slice_new0 (OnWentOnlineInfo);
2072                         
2073                         info->iap = NULL;
2074                         info->device = NULL;
2075                         info->account = NULL;
2076                 
2077                         if (parent_window)
2078                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2079                         else
2080                                 info->parent_window = NULL;
2081                         info->user_data = user_data;
2082                         info->callback = callback;
2083                 
2084                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2085                                                               force, on_conic_device_went_online, 
2086                                                               info);
2087  
2088                         /* We'll cleanup in on_conic_device_went_online */
2089                 }
2090  
2091                 /* The other code has no more reason to run. This is all that we can do for the
2092                  * caller (he should have given us a nice and clean account instance!). We
2093                  * can't do magic, we don't know what account he intends to bring online. So
2094                  * we'll just bring the device online (and await his false bug report). */
2095                 
2096                 return;
2097         }
2098  
2099         
2100         /* Return if the account is already connected */
2101         
2102         conn_status = tny_account_get_connection_status (account);
2103         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
2104  
2105                 /* We promise to instantly perform the callback, so ... */
2106                 if (callback) {
2107                         callback (FALSE, NULL, parent_window, account, user_data);
2108                 }
2109                 
2110                 return;
2111         }
2112         
2113         /* Else, we are in a state that requires that we go online before we
2114          * call the caller's callback. */
2115         
2116         info = g_slice_new0 (OnWentOnlineInfo);
2117         
2118         info->device = NULL;
2119         info->iap = NULL;
2120         info->account = TNY_ACCOUNT (g_object_ref (account));
2121         
2122         if (parent_window)
2123                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2124         else
2125                 info->parent_window = NULL;
2126         
2127         /* So we'll put the callback away for later ... */
2128         
2129         info->user_data = user_data;
2130         info->callback = callback;
2131         
2132         if (!device_online) {
2133  
2134                 /* If also the device is offline, then we connect both the device 
2135                  * and the account */
2136                 
2137                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2138                                                       force, on_conic_device_went_online, 
2139                                                       info);
2140                 
2141         } else {
2142                 
2143                 /* If the device is online, we'll just connect the account */
2144                 
2145                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
2146                                               on_account_went_online, info);
2147         }
2148  
2149         /* The info gets freed by on_account_went_online or on_conic_device_went_online
2150          * in both situations, go look if you don't believe me! */
2151         
2152         return;
2153 }
2154
2155 void
2156 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
2157                                                gboolean force,
2158                                                TnyFolderStore *folder_store, 
2159                                                ModestConnectedPerformer callback, 
2160                                                gpointer user_data)
2161 {
2162         TnyAccount *account = NULL;
2163         
2164         if (!folder_store) {
2165                 /* We promise to instantly perform the callback, so ... */
2166                 if (callback) {
2167                         callback (FALSE, NULL, parent_window, NULL, user_data);
2168                 }
2169                 return; 
2170                 
2171                 /* Original comment: Maybe it is something local. */
2172                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
2173                 
2174         } else if (TNY_IS_FOLDER (folder_store)) {
2175                 /* Get the folder's parent account: */
2176                 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2177         } else if (TNY_IS_ACCOUNT (folder_store)) {
2178                 /* Use the folder store as an account: */
2179                 account = TNY_ACCOUNT (g_object_ref (folder_store));
2180         }
2181  
2182         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2183                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2184                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2185  
2186                         /* No need to connect a local account */
2187                         if (callback)
2188                                 callback (FALSE, NULL, parent_window, account, user_data);
2189
2190                         goto clean;
2191                 }
2192         }
2193         modest_platform_connect_and_perform (parent_window, force, account, callback, user_data);
2194  
2195  clean:
2196         if (account)
2197                 g_object_unref (account);
2198 }
2199
2200 static void
2201 src_account_connect_performer (gboolean canceled, 
2202                                GError *err,
2203                                GtkWindow *parent_window, 
2204                                TnyAccount *src_account, 
2205                                gpointer user_data)
2206 {
2207         DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data;
2208
2209         if (canceled || err) {
2210                 /* If there was any error call the user callback */
2211                 info->callback (canceled, err, parent_window, src_account, info->data);
2212         } else {
2213                 /* Connect the destination account */
2214                 modest_platform_connect_if_remote_and_perform (parent_window, TRUE, 
2215                                                                TNY_FOLDER_STORE (info->dst_account),
2216                                                                info->callback, info->data);
2217         }
2218
2219         /* Free the info object */
2220         g_object_unref (info->dst_account);
2221         g_slice_free (DoubleConnectionInfo, info);
2222 }
2223
2224
2225 void 
2226 modest_platform_double_connect_and_perform (GtkWindow *parent_window, 
2227                                             gboolean force,
2228                                             TnyFolderStore *folder_store,
2229                                             DoubleConnectionInfo *connect_info)
2230 {
2231         modest_platform_connect_if_remote_and_perform(parent_window, 
2232                                                       force,
2233                                                       folder_store, 
2234                                                       src_account_connect_performer, 
2235                                                       connect_info);
2236 }
2237
2238 GtkWidget *
2239 modest_platform_get_account_settings_dialog (ModestAccountSettings *settings)
2240 {
2241         ModestAccountSettingsDialog *dialog = modest_account_settings_dialog_new ();
2242
2243         modest_account_settings_dialog_set_account (dialog, settings);
2244         return GTK_WIDGET (dialog);
2245 }
2246
2247 GtkWidget *
2248 modest_platform_get_account_settings_wizard (void)
2249 {
2250         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2251
2252         return GTK_WIDGET (dialog);
2253 }
2254
2255 ModestConnectedVia
2256 modest_platform_get_current_connection (void)
2257 {
2258         TnyDevice *device = NULL;
2259         ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY;
2260         
2261         device = modest_runtime_get_device ();
2262
2263         if (!tny_device_is_online (device))
2264                 return MODEST_CONNECTED_VIA_ANY;
2265
2266 #ifdef MODEST_HAVE_CONIC
2267         /* Get iap id */
2268         const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
2269         if (iap_id) {
2270                 ConIcIap *iap = tny_maemo_conic_device_get_iap (
2271                         TNY_MAEMO_CONIC_DEVICE (device), iap_id);
2272                 const gchar *bearer_type = con_ic_iap_get_bearer_type (iap);
2273                 if (bearer_type) {
2274                         if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) ||
2275                             !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) ||
2276                             !strcmp (bearer_type, "WIMAX")) {
2277                                 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX;
2278                         } else {
2279                                 retval = MODEST_CONNECTED_VIA_ANY;
2280                         }
2281                 }       
2282                 g_object_unref (iap);
2283         }
2284 #else
2285         retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */  
2286 #endif /* MODEST_HAVE_CONIC */
2287         return retval;
2288 }
2289
2290
2291
2292 gboolean
2293 modest_platform_check_memory_low (ModestWindow *win,
2294                                   gboolean visuals)
2295 {
2296         gboolean lowmem;
2297         
2298         /* are we in low memory state? */
2299         lowmem = osso_mem_in_lowmem_state () ? TRUE : FALSE;
2300         
2301         if (win && lowmem && visuals)
2302                 modest_platform_run_information_dialog (
2303                         GTK_WINDOW(win),
2304                         dgettext("ke-recv","memr_ib_operation_disabled"),
2305                         TRUE);
2306
2307         if (lowmem)
2308                 g_debug ("%s: low memory reached. disallowing some operations",
2309                          __FUNCTION__);
2310
2311         return lowmem;
2312 }