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