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