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