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