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