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