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