Asynchronous retrieval of size in details dialog of msg view window
[modest] / src / hildon2 / modest-platform.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <config.h>
31 #include <glib/gi18n.h>
32
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) == 0) {
1538                 g_warning ("%s: header list is empty", __FUNCTION__);
1539                 return;
1540         }
1541
1542         if (!show_visual) {
1543                 modest_platform_push_email_notification ();
1544                 /* We do a return here to avoid indentation with an else */
1545                 return;
1546         }
1547
1548 #ifdef MODEST_HAVE_HILDON_NOTIFY
1549         HildonNotification *notification;
1550         TnyIterator *iter;
1551         GSList *notifications_list = NULL;
1552
1553         /* Get previous notifications ids */
1554         notifications_list = modest_conf_get_list (modest_runtime_get_conf (),
1555                                                    MODEST_CONF_NOTIFICATION_IDS,
1556                                                    MODEST_CONF_VALUE_INT, NULL);
1557
1558         iter = tny_list_create_iterator (header_list);
1559         while (!tny_iterator_is_done (iter)) {
1560                 gchar *url = NULL, *display_address = NULL;
1561                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1562                 TnyFolder *folder = tny_header_get_folder (header);
1563                 gboolean first_notification = TRUE;
1564                 gint notif_id;
1565                 gchar *str;
1566
1567                 display_address = tny_header_dup_from (header);
1568                 /* string is changed in-place */
1569                 modest_text_utils_get_display_address (display_address);
1570
1571                 str = tny_header_dup_subject (header);
1572                 notification = hildon_notification_new (display_address,
1573                                                         str,
1574                                                         "qgn_list_messagin",
1575                                                         MODEST_NOTIFICATION_CATEGORY);
1576                 g_free (str);
1577                 /* Create the message URL */
1578                 str = tny_header_dup_uid (header);
1579                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1580                                        str);
1581                 g_free (str);
1582
1583                 hildon_notification_add_dbus_action(notification,
1584                                                     "default",
1585                                                     "Cancel",
1586                                                     MODEST_DBUS_SERVICE,
1587                                                     MODEST_DBUS_OBJECT,
1588                                                     MODEST_DBUS_IFACE,
1589                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1590                                                     G_TYPE_STRING, url,
1591                                                     -1);
1592
1593                 /* Play sound if the user wants. Show the LED
1594                    pattern. Show and play just one */
1595                 if (G_UNLIKELY (first_notification)) {
1596                         TnyAccount *account;
1597
1598                         first_notification = FALSE;
1599
1600                         /* Set the led pattern */
1601                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1602                                                             "dialog-type", 4);
1603                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1604                                                             "led-pattern",
1605                                                             MODEST_NEW_MAIL_LIGHTING_PATTERN);
1606
1607                         /* Set the account of the headers */
1608                         account = tny_folder_get_account (folder);
1609                         if (account) {
1610                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1611                                                                     "email-account",
1612                                                                     tny_account_get_id (account));
1613                                 g_object_unref (account);
1614                         }
1615                 }
1616
1617                 /* Notify. We need to do this in an idle because this function
1618                    could be called from a thread */
1619                 if (!notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL)) {
1620                         g_warning ("Failed to send notification");
1621                 }
1622
1623                 /* Save id in the list */
1624                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1625                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1626                 /* We don't listen for the "closed" signal, because we
1627                    don't care about if the notification was removed or
1628                    not to store the list in gconf */
1629         
1630                 /* Free & carry on */
1631                 g_free (display_address);
1632                 g_free (url);
1633                 g_object_unref (folder);
1634                 g_object_unref (header);
1635                 tny_iterator_next (iter);
1636         }
1637         g_object_unref (iter);
1638
1639         /* Save the ids */
1640         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1641                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1642
1643         g_slist_free (notifications_list);
1644         
1645 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1646 }
1647
1648 void
1649 modest_platform_remove_new_mail_notifications (gboolean only_visuals) 
1650 {
1651         if (only_visuals) {
1652 #ifdef MODEST_HAVE_MCE
1653                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1654                                      MCE_SERVICE,
1655                                      MCE_REQUEST_PATH,
1656                                      MCE_REQUEST_IF,
1657                                      MCE_DEACTIVATE_LED_PATTERN,
1658                                      NULL,
1659                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1660                                      DBUS_TYPE_INVALID);
1661 #endif
1662                 return;
1663         }
1664
1665 #ifdef MODEST_HAVE_HILDON_NOTIFY
1666         GSList *notif_list = NULL;
1667
1668         /* Get previous notifications ids */
1669         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1670                                            MODEST_CONF_NOTIFICATION_IDS, 
1671                                            MODEST_CONF_VALUE_INT, NULL);
1672
1673         while (notif_list) {
1674                 gint notif_id;
1675                 NotifyNotification *notif;
1676
1677                 /* Nasty HACK to remove the notifications, set the id
1678                    of the existing ones and then close them */
1679                 notif_id = GPOINTER_TO_INT(notif_list->data);
1680                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1681                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1682
1683                 /* Close the notification, note that some ids could be
1684                    already invalid, but we don't care because it does
1685                    not fail */
1686                 notify_notification_close(notif, NULL);
1687                 g_object_unref(notif);
1688
1689                 /* Delete the link, it's like going to the next */
1690                 notif_list = g_slist_delete_link (notif_list, notif_list);
1691         }
1692
1693         /* Save the ids */
1694         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1695                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1696
1697         g_slist_free (notif_list);
1698
1699 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1700 }
1701
1702
1703
1704 GtkWidget * 
1705 modest_platform_get_global_settings_dialog ()
1706 {
1707         return modest_hildon2_global_settings_dialog_new ();
1708 }
1709
1710 void
1711 modest_platform_show_help (GtkWindow *parent_window, 
1712                            const gchar *help_id)
1713 {
1714         return;
1715 }
1716
1717 void 
1718 modest_platform_show_search_messages (GtkWindow *parent_window)
1719 {
1720         osso_return_t result = OSSO_ERROR;
1721         
1722         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1723                                              "osso_global_search",
1724                                              "search_email", NULL, DBUS_TYPE_INVALID);
1725
1726         if (result != OSSO_OK) {
1727                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1728         }
1729 }
1730
1731 void 
1732 modest_platform_show_addressbook (GtkWindow *parent_window)
1733 {
1734         osso_return_t result = OSSO_ERROR;
1735
1736         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1737                                              "osso_addressbook",
1738                                              "top_application", NULL, DBUS_TYPE_INVALID);
1739
1740         if (result != OSSO_OK) {
1741                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1742         }
1743 }
1744
1745 GtkWidget *
1746 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1747 {
1748         GtkWidget *widget = modest_folder_view_new (query);
1749
1750         /* Show one account by default */
1751         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1752                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1753
1754         /* Restore settings */
1755         modest_widget_memory_restore (modest_runtime_get_conf(), 
1756                                       G_OBJECT (widget),
1757                                       MODEST_CONF_FOLDER_VIEW_KEY);
1758
1759         return widget;
1760 }
1761
1762 void
1763 banner_finish (gpointer data, GObject *object)
1764 {
1765         ModestWindowMgr *mgr = (ModestWindowMgr *) data;
1766         modest_window_mgr_unregister_banner (mgr);
1767         g_object_unref (mgr);
1768 }
1769
1770 void 
1771 modest_platform_information_banner (GtkWidget *parent,
1772                                     const gchar *icon_name,
1773                                     const gchar *text)
1774 {
1775         GtkWidget *banner, *banner_parent = NULL;
1776         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1777
1778         if (modest_window_mgr_get_num_windows (mgr) == 0)
1779                 return;
1780
1781         if (parent && GTK_IS_WINDOW (parent)) {
1782                 /* If the window is the active one then show the
1783                    banner on top of this window */
1784                 if (gtk_window_is_active (GTK_WINDOW (parent)))
1785                         banner_parent = parent;
1786                 /* If the window is not the topmost but it's visible
1787                    (it's minimized for example) then show the banner
1788                    with no parent */ 
1789                 else if (GTK_WIDGET_VISIBLE (parent))
1790                         banner_parent = NULL;
1791                 /* If the window is hidden (like the main window when
1792                    running in the background) then do not show
1793                    anything */
1794                 else 
1795                         return;
1796         }
1797
1798
1799         banner = hildon_banner_show_information (banner_parent, icon_name, text);
1800
1801         modest_window_mgr_register_banner (mgr);
1802         g_object_ref (mgr);
1803         g_object_weak_ref ((GObject *) banner, banner_finish, mgr);
1804 }
1805
1806 void
1807 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1808                                                  const gchar *icon_name,
1809                                                  const gchar *text,
1810                                                  gint timeout)
1811 {
1812         GtkWidget *banner;
1813
1814         if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1815                 return;
1816
1817         banner = hildon_banner_show_information (parent, icon_name, text);
1818         hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1819 }
1820
1821 GtkWidget *
1822 modest_platform_animation_banner (GtkWidget *parent,
1823                                   const gchar *animation_name,
1824                                   const gchar *text)
1825 {
1826         GtkWidget *inf_note = NULL;
1827
1828         g_return_val_if_fail (text != NULL, NULL);
1829
1830         if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1831                 return NULL;
1832
1833         /* If the parent is not visible then do not show */
1834         if (parent && !GTK_WIDGET_VISIBLE (parent))
1835                 return NULL;
1836
1837         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1838
1839         return inf_note;
1840 }
1841
1842 typedef struct
1843 {
1844         GMainLoop* loop;
1845         TnyAccount *account;
1846         gboolean is_online;
1847         gint count_tries;
1848 } CheckAccountIdleData;
1849
1850 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1851
1852 static gboolean 
1853 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1854 {
1855         gboolean stop_trying = FALSE;
1856         g_return_val_if_fail (data && data->account, FALSE);
1857         
1858         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1859                 tny_account_get_connection_status (data->account));     
1860         
1861         if (data && data->account && 
1862                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1863                  * after which the account is likely to be usable, or never likely to be usable soon: */
1864                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1865         {
1866                 data->is_online = TRUE;
1867                 
1868                 stop_trying = TRUE;
1869         } else {
1870                 /* Give up if we have tried too many times: */
1871                 if (data->count_tries >= NUMBER_OF_TRIES) {
1872                         stop_trying = TRUE;
1873                 } else {
1874                         /* Wait for another timeout: */
1875                         ++(data->count_tries);
1876                 }
1877         }
1878         
1879         if (stop_trying) {
1880                 /* Allow the function that requested this idle callback to continue: */
1881                 if (data->loop)
1882                         g_main_loop_quit (data->loop);
1883                         
1884                 if (data->account)
1885                         g_object_unref (data->account);
1886                 
1887                 return FALSE; /* Don't call this again. */
1888         } else {
1889                 return TRUE; /* Call this timeout callback again. */
1890         }
1891 }
1892
1893 /* Return TRUE immediately if the account is already online,
1894  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1895  * soon as the account is online, or FALSE if the account does 
1896  * not become online in the NUMBER_OF_TRIES seconds.
1897  * This is useful when the D-Bus method was run immediately after 
1898  * the application was started (when using D-Bus activation), 
1899  * because the account usually takes a short time to go online.
1900  * The return value is maybe not very useful.
1901  */
1902 gboolean
1903 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1904 {
1905         gboolean is_online;
1906
1907         g_return_val_if_fail (account, FALSE);
1908
1909         if (!tny_device_is_online (modest_runtime_get_device())) {
1910                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1911                 return FALSE;
1912         }
1913
1914         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1915          * so we avoid wait unnecessarily: */
1916         if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1917                 return TRUE;
1918
1919         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1920          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1921          * we want to avoid. */
1922         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1923                 return TRUE;
1924                 
1925         /* This blocks on the result: */
1926         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1927         data->is_online = FALSE;
1928         data->account = account;
1929         g_object_ref (data->account);
1930         data->count_tries = 0;
1931                 
1932         GMainContext *context = NULL; /* g_main_context_new (); */
1933         data->loop = g_main_loop_new (context, FALSE /* not running */);
1934
1935         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1936
1937         /* This main loop will run until the idle handler has stopped it: */
1938         g_main_loop_run (data->loop);
1939
1940         g_main_loop_unref (data->loop);
1941         /* g_main_context_unref (context); */
1942
1943         is_online = data->is_online;
1944         g_slice_free (CheckAccountIdleData, data);
1945         
1946         return is_online;       
1947 }
1948
1949
1950
1951 static void
1952 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1953 {
1954         /* GTK_RESPONSE_HELP means we need to show the certificate */
1955         if (response_id == GTK_RESPONSE_APPLY) {
1956                 GtkWidget *note;
1957                 gchar *msg;
1958                 
1959                 /* Do not close the dialog */
1960                 g_signal_stop_emission_by_name (dialog, "response");
1961
1962                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1963                 note = hildon_note_new_information (NULL, msg);
1964                 gtk_dialog_run (GTK_DIALOG(note));
1965                 gtk_widget_destroy (note);
1966         }
1967 }
1968
1969
1970 gboolean
1971 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1972                                                      const gchar *certificate)
1973 {
1974         GtkWidget *note;
1975         gint response;
1976         ModestWindow *win;
1977         HildonWindowStack *stack;
1978
1979         stack = hildon_window_stack_get_default ();
1980         win = MODEST_WINDOW (hildon_window_stack_peek (stack));
1981
1982         if (!win) {
1983           g_warning ("%s: don't show dialogs if there's no window shown; assuming 'Cancel'",
1984                            __FUNCTION__);
1985                 return FALSE;
1986         }
1987
1988         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1989                                            server_name);
1990
1991         /* We use GTK_RESPONSE_APPLY because we want the button in the
1992            middle of OK and CANCEL the same as the browser does for
1993            example. With GTK_RESPONSE_HELP the view button is aligned
1994            to the left while the other two to the right */
1995         note = hildon_note_new_confirmation_add_buttons  (
1996                 NULL,
1997                 question,
1998                 _HL("wdgt_bd_yes"),     GTK_RESPONSE_OK,
1999                 _HL("wdgt_bd_view"),          GTK_RESPONSE_APPLY,   /* abusing this... */
2000                 _HL("wdgt_bd_no"), GTK_RESPONSE_CANCEL,
2001                 NULL, NULL);
2002
2003         g_signal_connect (G_OBJECT(note), "response", 
2004                           G_CALLBACK(on_cert_dialog_response),
2005                           (gpointer) certificate);
2006
2007         response = gtk_dialog_run(GTK_DIALOG(note));
2008
2009         on_destroy_dialog (note);
2010         g_free (question);
2011
2012         return response == GTK_RESPONSE_OK;
2013 }
2014
2015 gboolean
2016 modest_platform_run_alert_dialog (const gchar* prompt,
2017                                   gboolean is_question)
2018 {
2019         ModestWindow *top_win;
2020         HildonWindowStack *stack;
2021
2022         stack = hildon_window_stack_get_default ();
2023         top_win = MODEST_WINDOW (hildon_window_stack_peek (stack));
2024
2025         if (!top_win) {
2026                 g_warning ("%s: don't show dialogs if there's no window shown; assuming 'Cancel'",
2027                            __FUNCTION__);
2028                 return FALSE;
2029         }
2030
2031         gboolean retval = TRUE;
2032         if (is_question) {
2033                 /* The Tinymail documentation says that we should show Yes and No buttons,
2034                  * when it is a question.
2035                  * Obviously, we need tinymail to use more specific error codes instead,
2036                  * so we know what buttons to show. */
2037                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (top_win), 
2038                                                                               prompt));
2039                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
2040                                              GTK_WINDOW (dialog), GTK_WINDOW (top_win));
2041
2042                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
2043                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
2044
2045                 on_destroy_dialog (dialog);
2046         } else {
2047                 /* Just show the error text and use the default response: */
2048                 modest_platform_run_information_dialog (GTK_WINDOW (top_win), 
2049                                                         prompt, FALSE);
2050         }
2051         return retval;
2052 }
2053
2054 /***************/
2055 typedef struct {
2056         GtkWindow *parent_window;
2057         ModestConnectedPerformer callback;
2058         TnyAccount *account;
2059         gpointer user_data;
2060         gchar *iap;
2061         TnyDevice *device;
2062 } OnWentOnlineInfo;
2063  
2064 static void 
2065 on_went_online_info_free (OnWentOnlineInfo *info)
2066 {
2067         /* And if we cleanup, we DO cleanup  :-)  */
2068         
2069         if (info->device)
2070                 g_object_unref (info->device);
2071         if (info->iap)
2072                 g_free (info->iap);
2073         if (info->parent_window)
2074                 g_object_unref (info->parent_window);
2075         if (info->account)
2076                 g_object_unref (info->account);
2077         
2078         g_slice_free (OnWentOnlineInfo, info);
2079         
2080         /* We're done ... */
2081         
2082         return;
2083 }
2084  
2085 static void
2086 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
2087 {
2088         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
2089  
2090         /* Now it's really time to callback to the caller. If going online didn't succeed,
2091          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
2092          * canceled will be set. Etcetera etcetera. */
2093         
2094         if (info->callback) {
2095                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2096         }
2097         
2098         /* This is our last call, we must cleanup here if we didn't yet do that */
2099         on_went_online_info_free (info);
2100         
2101         return;
2102 }
2103  
2104  
2105 static void
2106 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
2107 {
2108         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
2109         info->iap = g_strdup (iap_id);
2110         
2111         if (canceled || err || !info->account) {
2112         
2113                 /* If there's a problem or if there's no account (then that's it for us, we callback
2114                  * the caller's callback now. He'll have to handle err or canceled, of course.
2115                  * We are not really online, as the account is not really online here ... */    
2116                 
2117                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
2118                  * this info. We don't cleanup err, Tinymail does that! */
2119                 
2120                 if (info->callback) {
2121                         
2122                         /* info->account can be NULL here, this means that the user did not
2123                          * provide a nice account instance. We'll assume that the user knows
2124                          * what he's doing and is happy with just the device going online. 
2125                          * 
2126                          * We can't do magic, we don't know what account the user wants to
2127                          * see going online. So just the device goes online, end of story */
2128                         
2129                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2130                 }
2131                 
2132         } else if (info->account) {
2133                 
2134                 /* If there's no problem and if we have an account, we'll put the account
2135                  * online too. When done, the callback of bringing the account online
2136                  * will callback the caller's callback. This is the most normal case. */
2137  
2138                 info->device = TNY_DEVICE (g_object_ref (device));
2139                 
2140                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
2141                                               on_account_went_online, info);
2142                 
2143                 /* The on_account_went_online cb frees up the info, go look if you
2144                  * don't believe me! (so we return here) */
2145                 
2146                 return;
2147         }
2148         
2149         /* We cleanup if we are not bringing the account online too */
2150         on_went_online_info_free (info);
2151  
2152         return; 
2153 }
2154         
2155 void 
2156 modest_platform_connect_and_perform (GtkWindow *parent_window, 
2157                                      gboolean force,
2158                                      TnyAccount *account, 
2159                                      ModestConnectedPerformer callback, 
2160                                      gpointer user_data)
2161 {
2162         gboolean device_online;
2163         TnyDevice *device;
2164         TnyConnectionStatus conn_status;
2165         OnWentOnlineInfo *info;
2166         
2167         device = modest_runtime_get_device();
2168         device_online = tny_device_is_online (device);
2169
2170         /* If there is no account check only the device status */
2171         if (!account) {
2172                 
2173                 if (device_online) {
2174  
2175                         /* We promise to instantly perform the callback, so ... */
2176                         if (callback) {
2177                                 callback (FALSE, NULL, parent_window, account, user_data);
2178                         }
2179                         
2180                 } else {
2181                         
2182                         info = g_slice_new0 (OnWentOnlineInfo);
2183                         
2184                         info->iap = NULL;
2185                         info->device = NULL;
2186                         info->account = NULL;
2187                 
2188                         if (parent_window)
2189                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2190                         else
2191                                 info->parent_window = NULL;
2192                         info->user_data = user_data;
2193                         info->callback = callback;
2194                 
2195                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2196                                                               force, on_conic_device_went_online, 
2197                                                               info);
2198  
2199                         /* We'll cleanup in on_conic_device_went_online */
2200                 }
2201  
2202                 /* The other code has no more reason to run. This is all that we can do for the
2203                  * caller (he should have given us a nice and clean account instance!). We
2204                  * can't do magic, we don't know what account he intends to bring online. So
2205                  * we'll just bring the device online (and await his false bug report). */
2206                 
2207                 return;
2208         }
2209  
2210         
2211         /* Return if the account is already connected */
2212         
2213         conn_status = tny_account_get_connection_status (account);
2214         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
2215  
2216                 /* We promise to instantly perform the callback, so ... */
2217                 if (callback) {
2218                         callback (FALSE, NULL, parent_window, account, user_data);
2219                 }
2220                 
2221                 return;
2222         }
2223         
2224         /* Else, we are in a state that requires that we go online before we
2225          * call the caller's callback. */
2226         
2227         info = g_slice_new0 (OnWentOnlineInfo);
2228         
2229         info->device = NULL;
2230         info->iap = NULL;
2231         info->account = TNY_ACCOUNT (g_object_ref (account));
2232         
2233         if (parent_window)
2234                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2235         else
2236                 info->parent_window = NULL;
2237         
2238         /* So we'll put the callback away for later ... */
2239         
2240         info->user_data = user_data;
2241         info->callback = callback;
2242         
2243         if (!device_online) {
2244  
2245                 /* If also the device is offline, then we connect both the device 
2246                  * and the account */
2247                 
2248                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2249                                                       force, on_conic_device_went_online, 
2250                                                       info);
2251                 
2252         } else {
2253                 
2254                 /* If the device is online, we'll just connect the account */
2255                 
2256                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
2257                                               on_account_went_online, info);
2258         }
2259  
2260         /* The info gets freed by on_account_went_online or on_conic_device_went_online
2261          * in both situations, go look if you don't believe me! */
2262         
2263         return;
2264 }
2265
2266 void
2267 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
2268                                                gboolean force,
2269                                                TnyFolderStore *folder_store, 
2270                                                ModestConnectedPerformer callback, 
2271                                                gpointer user_data)
2272 {
2273         TnyAccount *account = NULL;
2274
2275         if (!folder_store ||
2276             (TNY_IS_MERGE_FOLDER (folder_store) &&
2277              (tny_folder_get_folder_type (TNY_FOLDER(folder_store)) == TNY_FOLDER_TYPE_OUTBOX))) {
2278
2279                 /* We promise to instantly perform the callback, so ... */
2280                 if (callback) {
2281                         GError *error = NULL;
2282                         g_set_error (&error, TNY_ERROR_DOMAIN, TNY_SERVICE_ERROR_UNKNOWN,
2283                                      "Unable to move or not found folder");
2284                         callback (FALSE, error, parent_window, NULL, user_data);
2285                         g_error_free (error);
2286                 }
2287                 return;
2288
2289         } else if (TNY_IS_FOLDER (folder_store)) {
2290                 /* Get the folder's parent account: */
2291                 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2292         } else if (TNY_IS_ACCOUNT (folder_store)) {
2293                 /* Use the folder store as an account: */
2294                 account = TNY_ACCOUNT (g_object_ref (folder_store));
2295         }
2296
2297         if (account && (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE)) {
2298                 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) {
2299                         /* No need to connect a local account */
2300                         if (callback)
2301                                 callback (FALSE, NULL, parent_window, account, user_data);
2302
2303                         goto clean;
2304                 }
2305         }
2306         modest_platform_connect_and_perform (parent_window, force, account, callback, user_data);
2307
2308  clean:
2309         if (account)
2310                 g_object_unref (account);
2311 }
2312
2313 static void
2314 src_account_connect_performer (gboolean canceled,
2315                                GError *err,
2316                                GtkWindow *parent_window,
2317                                TnyAccount *src_account,
2318                                gpointer user_data)
2319 {
2320         DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data;
2321
2322         if (canceled || err) {
2323                 /* If there was any error call the user callback */
2324                 info->callback (canceled, err, parent_window, src_account, info->data);
2325         } else {
2326                 /* Connect the destination account */
2327                 modest_platform_connect_if_remote_and_perform (parent_window, TRUE, 
2328                                                                TNY_FOLDER_STORE (info->dst_account),
2329                                                                info->callback, info->data);
2330         }
2331
2332         /* Free the info object */
2333         g_object_unref (info->dst_account);
2334         g_slice_free (DoubleConnectionInfo, info);
2335 }
2336
2337
2338 void 
2339 modest_platform_double_connect_and_perform (GtkWindow *parent_window, 
2340                                             gboolean force,
2341                                             TnyFolderStore *folder_store,
2342                                             DoubleConnectionInfo *connect_info)
2343 {
2344         modest_platform_connect_if_remote_and_perform(parent_window, 
2345                                                       force,
2346                                                       folder_store, 
2347                                                       src_account_connect_performer, 
2348                                                       connect_info);
2349 }
2350
2351 GtkWidget *
2352 modest_platform_get_account_settings_wizard (void)
2353 {
2354         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2355
2356         return GTK_WIDGET (dialog);
2357 }
2358
2359 ModestConnectedVia
2360 modest_platform_get_current_connection (void)
2361 {
2362         TnyDevice *device = NULL;
2363         ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY;
2364         
2365         device = modest_runtime_get_device ();
2366
2367         if (!tny_device_is_online (device))
2368                 return MODEST_CONNECTED_VIA_ANY;
2369
2370 #ifdef MODEST_HAVE_CONIC
2371         /* Get iap id */
2372         const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
2373         if (iap_id) {
2374                 ConIcIap *iap = tny_maemo_conic_device_get_iap (
2375                         TNY_MAEMO_CONIC_DEVICE (device), iap_id);
2376                 const gchar *bearer_type = con_ic_iap_get_bearer_type (iap);
2377                 if (bearer_type) {
2378                         if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) ||
2379                             !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) ||
2380                             !strcmp (bearer_type, "WIMAX")) {
2381                                 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX;
2382                         } else {
2383                                 retval = MODEST_CONNECTED_VIA_ANY;
2384                         }
2385                 }       
2386                 g_object_unref (iap);
2387         }
2388 #else
2389         retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */  
2390 #endif /* MODEST_HAVE_CONIC */
2391         return retval;
2392 }
2393
2394
2395
2396 gboolean
2397 modest_platform_check_memory_low (ModestWindow *win,
2398                                   gboolean visuals)
2399 {
2400         gboolean lowmem;
2401         
2402         /* are we in low memory state? */
2403         lowmem = osso_mem_in_lowmem_state () ? TRUE : FALSE;
2404         
2405         if (win && lowmem && visuals)
2406                 modest_platform_run_information_dialog (
2407                         GTK_WINDOW(win),
2408                         _KR("memr_ib_operation_disabled"),
2409                         TRUE);
2410
2411         if (lowmem)
2412                 g_debug ("%s: low memory reached. disallowing some operations",
2413                          __FUNCTION__);
2414
2415         return lowmem;
2416 }
2417
2418 void 
2419 modest_platform_run_folder_details_dialog (GtkWindow *parent_window,
2420                                            TnyFolder *folder)
2421 {
2422         GtkWidget *dialog;
2423         
2424         /* Create dialog */
2425         dialog = modest_hildon2_details_dialog_new_with_folder (parent_window, folder);
2426
2427         /* Run dialog */
2428         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2429                                      GTK_WINDOW (dialog), 
2430                                      parent_window);
2431         gtk_widget_show_all (dialog);
2432
2433         g_signal_connect_swapped (dialog, "response", 
2434                                   G_CALLBACK (gtk_widget_destroy),
2435                                   dialog);
2436 }
2437
2438 typedef struct _HeaderDetailsGetSizeInfo {
2439         GtkWidget *dialog;
2440         TnyMimePart *part;
2441         guint total;
2442 } HeaderDetailsGetSizeInfo;
2443
2444 static void 
2445 header_details_dialog_destroy (gpointer userdata,
2446                                GObject *object)
2447 {
2448         HeaderDetailsGetSizeInfo *info = (HeaderDetailsGetSizeInfo *) userdata;
2449
2450         info->dialog = NULL;
2451 }
2452
2453 static gboolean
2454 idle_get_mime_part_size_cb (gpointer userdata)
2455 {
2456         HeaderDetailsGetSizeInfo *info = (HeaderDetailsGetSizeInfo *) userdata;
2457         gdk_threads_enter ();
2458
2459         if (info->dialog && GTK_WIDGET_VISIBLE (info->dialog)) {
2460                 modest_details_dialog_set_message_size (MODEST_DETAILS_DIALOG (info->dialog),
2461                                                         info->total);
2462         }
2463
2464         if (info->dialog) {
2465                 g_object_weak_unref (G_OBJECT (info->dialog), header_details_dialog_destroy, info);
2466                 info->dialog = NULL;
2467         }
2468         g_object_unref (info->part);
2469         g_slice_free (HeaderDetailsGetSizeInfo, info);
2470
2471         gdk_threads_leave ();
2472
2473         return FALSE;
2474 }
2475
2476 static gpointer
2477 get_mime_part_size_thread (gpointer thr_user_data)
2478 {
2479         HeaderDetailsGetSizeInfo *info = (HeaderDetailsGetSizeInfo *) thr_user_data;
2480         gssize result = 0;
2481         TnyStream *count_stream;
2482
2483         count_stream = modest_count_stream_new ();
2484         result = tny_mime_part_decode_to_stream (info->part, count_stream, NULL);
2485         info->total = modest_count_stream_get_count(MODEST_COUNT_STREAM (count_stream));
2486         if (info->total == 0) {
2487                 modest_count_stream_reset_count(MODEST_COUNT_STREAM (count_stream));
2488                 result = tny_mime_part_write_to_stream (info->part, count_stream, NULL);
2489                 info->total = modest_count_stream_get_count(MODEST_COUNT_STREAM (count_stream));
2490         }
2491         
2492         /* if there was an error, don't set the size (this is pretty uncommon) */
2493         if (result < 0) {
2494                 g_warning ("%s: error while writing mime part to stream\n", __FUNCTION__);
2495         }
2496         g_idle_add (idle_get_mime_part_size_cb, info);
2497
2498         return NULL;
2499 }
2500
2501 void
2502 modest_platform_run_header_details_dialog (GtkWindow *parent_window,
2503                                            TnyHeader *header,
2504                                            gboolean async_get_size,
2505                                            TnyMsg *msg)
2506 {
2507         GtkWidget *dialog;
2508
2509         /* Create dialog */
2510         dialog = modest_hildon2_details_dialog_new_with_header (parent_window, header, !async_get_size);
2511
2512         if (async_get_size && msg && TNY_IS_MSG (msg)) {
2513                 HeaderDetailsGetSizeInfo *info;
2514                 info = g_slice_new (HeaderDetailsGetSizeInfo);
2515                 info->dialog = dialog;
2516                 info->total = 0;
2517                 info->part = TNY_MIME_PART (g_object_ref (msg));
2518
2519                 g_object_weak_ref (G_OBJECT (dialog), header_details_dialog_destroy, info);
2520                 g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
2521         }
2522
2523         /* Run dialog */
2524         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2525                                      GTK_WINDOW (dialog),
2526                                      parent_window);
2527         gtk_widget_show_all (dialog);
2528
2529         g_signal_connect_swapped (dialog, "response", 
2530                                   G_CALLBACK (gtk_widget_destroy),
2531                                   dialog);
2532 }
2533
2534 osso_context_t *
2535 modest_platform_get_osso_context (void)
2536 {
2537         return modest_maemo_utils_get_osso_context ();
2538 }
2539
2540 static void
2541 _modest_platform_play_email_tone (void)
2542 {
2543         gchar *mail_tone;
2544         gint mail_volume_int;
2545         int ret;
2546         ca_context *ca_con = NULL;
2547         ca_proplist *pl = NULL;
2548
2549 #ifdef MODEST_USE_PROFILE
2550         gchar *active_profile;
2551         gchar *mail_volume;
2552
2553         active_profile = profile_get_profile ();
2554         mail_tone = profile_get_value (active_profile, PROFILE_MAIL_TONE);
2555         mail_volume = profile_get_value (active_profile, PROFILE_MAIL_VOLUME);
2556         mail_volume_int = profile_parse_int (mail_volume);
2557         g_free (mail_volume);
2558         g_free (active_profile);
2559 #else
2560         mail_tone = MAIL_TONE;
2561         mail_volume_int = 100;
2562 #endif
2563
2564         if (mail_tone && !strstr (mail_tone, "/")) {
2565                 gchar *tmp;
2566
2567                 tmp = g_strconcat ("/usr/share/sounds", mail_tone, NULL);
2568                 g_free (mail_tone);
2569                 mail_tone = tmp;
2570         }
2571
2572         if (mail_volume_int > 0) {
2573
2574                 if ((ret = ca_context_create(&ca_con)) != CA_SUCCESS) {
2575                         g_warning("ca_context_create: %s\n", ca_strerror(ret));
2576                         return;
2577                 }
2578
2579                 if ((ret = ca_context_open(ca_con)) != CA_SUCCESS) {
2580                         g_warning("ca_context_open: %s\n", ca_strerror(ret));
2581                         ca_context_destroy(ca_con);
2582                         return;
2583                 }
2584
2585                 ca_proplist_create(&pl);
2586                 ca_proplist_sets(pl, CA_PROP_MEDIA_FILENAME, mail_tone);
2587                 ca_proplist_setf(pl, CA_PROP_CANBERRA_VOLUME, "%f", (gfloat) mail_volume_int);
2588
2589                 ret = ca_context_play_full(ca_con, 0, pl, NULL, NULL);
2590                 g_debug("ca_context_play_full (vol %f): %s\n", (gfloat) mail_volume_int, ca_strerror(ret));
2591
2592                 ca_proplist_destroy(pl);
2593                 ca_context_destroy(ca_con);
2594         }
2595
2596         g_free (mail_tone);
2597 }
2598
2599 #define MOVE_TO_DIALOG_FOLDER_VIEW "folder-view"
2600 #define MOVE_TO_DIALOG_BACK_BUTTON "back-button"
2601 #define MOVE_TO_DIALOG_ACTION_BUTTON "action-button"
2602 #define MOVE_TO_DIALOG_SHOWING_FOLDERS "showing-folders"
2603 #define MOVE_TO_DIALOG_PANNABLE "pannable"
2604 #define MOVE_TO_FOLDER_SEPARATOR "/"
2605
2606 static void
2607 move_to_dialog_set_selected_folder_store (GtkWidget *dialog, 
2608                                           TnyFolderStore *folder_store)
2609 {
2610         GtkWidget *action_button;
2611         GtkWidget *image = NULL;
2612         TnyAccount *account;
2613         gchar *account_name = NULL;
2614
2615         action_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2616
2617         /* Get account name */
2618         if (TNY_IS_FOLDER (folder_store))
2619                 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2620         else
2621                 account = g_object_ref (folder_store);
2622
2623         if (modest_tny_account_is_virtual_local_folders (account))
2624                 account_name = modest_conf_get_string (modest_runtime_get_conf(),
2625                                                        MODEST_CONF_DEVICE_NAME, NULL);
2626
2627         if (!account_name)
2628                 account_name = g_strdup (tny_account_get_name (account));
2629
2630         g_object_unref (account);
2631
2632         /* Set title of button: account or folder name */
2633         if (TNY_IS_FOLDER (folder_store)) {
2634                 hildon_button_set_title (HILDON_BUTTON (action_button), 
2635                                          tny_folder_get_name (TNY_FOLDER (folder_store)));
2636         } else {
2637                 hildon_button_set_title (HILDON_BUTTON (action_button), account_name);
2638         }
2639
2640         /* Set value of button, folder full name */
2641         if (TNY_IS_CAMEL_FOLDER (folder_store)) {
2642                 gchar *full_name = g_strconcat (account_name, MOVE_TO_FOLDER_SEPARATOR,
2643                                                 tny_camel_folder_get_full_name (TNY_CAMEL_FOLDER (folder_store)),
2644                                                 NULL);
2645                 hildon_button_set_value (HILDON_BUTTON (action_button), full_name);
2646                 g_free (full_name);
2647         }
2648         g_free (account_name);
2649
2650         /* Set image for the button */
2651         image = get_image_for_folder_store (folder_store, MODEST_ICON_SIZE_BIG);
2652         if (image)
2653                 hildon_button_set_image (HILDON_BUTTON (action_button), image);
2654 }
2655
2656 static void
2657 move_to_dialog_show_accounts (GtkWidget *dialog)
2658 {
2659         GtkWidget *back_button;
2660         GtkWidget *folder_view;
2661         GtkWidget *pannable;
2662         GtkWidget *action_button;
2663
2664         back_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON));
2665         action_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2666         folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2667         pannable = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE));
2668
2669         gtk_widget_set_sensitive (back_button, FALSE);
2670         gtk_widget_set_sensitive (action_button, FALSE);
2671
2672         /* Need to set this here, otherwise callbacks called because
2673            of filtering won't perform correctly */
2674         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS, GINT_TO_POINTER (FALSE));
2675
2676         /* Reset action button */
2677         hildon_button_set_title (HILDON_BUTTON (action_button), NULL);
2678         hildon_button_set_value (HILDON_BUTTON (action_button), NULL);
2679         hildon_button_set_image (HILDON_BUTTON (action_button), NULL);
2680
2681         modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW (folder_view), NULL);
2682         modest_folder_view_show_non_move_folders (MODEST_FOLDER_VIEW (folder_view), TRUE);
2683         modest_folder_view_set_style (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
2684         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2685                                          MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2686         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2687                                        MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2688         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view), 
2689                                          MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS);
2690         modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view), 
2691                                        MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS);
2692         hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (pannable), 0, 0);
2693 }
2694
2695 static void
2696 move_to_dialog_show_folders (GtkWidget *dialog, TnyFolderStore *folder_store)
2697 {
2698         GtkWidget *back_button;
2699         GtkWidget *folder_view;
2700         TnyAccount *account;
2701         const gchar *account_id;
2702         GtkWidget *pannable;
2703         GtkWidget *action_button;
2704
2705         back_button =
2706                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON));
2707         action_button =
2708                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2709         folder_view =
2710                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2711         pannable =
2712                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE));
2713
2714         gtk_widget_set_sensitive (back_button, TRUE);
2715         gtk_widget_set_sensitive (action_button, TRUE);
2716
2717         /* Need to set this here, otherwise callbacks called because
2718            of filtering won't perform correctly */
2719         g_object_set_data (G_OBJECT (dialog),
2720                            MOVE_TO_DIALOG_SHOWING_FOLDERS,
2721                            GINT_TO_POINTER (TRUE));
2722
2723         account = TNY_ACCOUNT (folder_store);
2724         if (modest_tny_account_is_virtual_local_folders (account)) {
2725                 account_id = tny_account_get_id (account);
2726                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2727                                                MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2728         } else if (modest_tny_account_is_memory_card_account (account)) {
2729                 account_id = tny_account_get_id (account);
2730                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2731                                                MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2732         } else {
2733                 account_id = tny_account_get_id (account);
2734                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2735                                                MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2736                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2737                                                MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2738         }
2739
2740         move_to_dialog_set_selected_folder_store (dialog, folder_store);
2741         modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW (folder_view),
2742                                                                      account_id);
2743
2744         modest_folder_view_show_non_move_folders (MODEST_FOLDER_VIEW (folder_view), FALSE);
2745         modest_folder_view_set_style (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2746         modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS);
2747         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS);
2748         hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (pannable), 0, 0);
2749 }
2750
2751 static void
2752 on_move_to_dialog_back_clicked (GtkButton *button,
2753                                 gpointer userdata)
2754 {
2755         GtkWidget *dialog = (GtkWidget *) userdata;
2756
2757         /* Back to show accounts */
2758         move_to_dialog_show_accounts (dialog);
2759 }
2760
2761 static void
2762 on_move_to_dialog_row_activated (GtkTreeView       *tree_view,
2763                                     GtkTreePath       *path,
2764                                     GtkTreeViewColumn *column,
2765                                     gpointer           user_data)
2766 {
2767         TnyFolderStore *selected = NULL;
2768         GtkWidget *dialog;
2769         GtkWidget *folder_view;
2770         gboolean showing_folders;
2771
2772         dialog = (GtkWidget *) user_data;
2773         showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), 
2774                                                               MOVE_TO_DIALOG_SHOWING_FOLDERS));
2775
2776         folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), 
2777                                                      MOVE_TO_DIALOG_FOLDER_VIEW));
2778
2779         selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2780         if (!selected)
2781                 return;
2782
2783         if (!showing_folders) {
2784                 gboolean valid = TRUE;
2785
2786                 if (TNY_IS_ACCOUNT (selected) &&
2787                     modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (selected))) {
2788                         ModestProtocolType protocol_type;
2789
2790                         protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (selected));
2791                         valid  = !modest_protocol_registry_protocol_type_has_tag 
2792                                 (modest_runtime_get_protocol_registry (),
2793                                  protocol_type,
2794                                  MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2795                 }
2796                 if (valid)
2797                         move_to_dialog_show_folders (dialog, selected);
2798         } else {
2799                 move_to_dialog_set_selected_folder_store (dialog, selected);
2800         }
2801 }
2802
2803 static void
2804 on_move_to_dialog_selection_changed (GtkTreeSelection *selection,
2805                                      gpointer          user_data)
2806 {
2807         gboolean showing_folders;
2808         GtkWidget *dialog;
2809
2810         dialog = (GtkWidget *) user_data;
2811         showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS));
2812         if (showing_folders) {
2813                 TnyFolderStore *selected;
2814                 GtkWidget *folder_view;
2815
2816                 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2817                 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2818
2819                 if (selected) {
2820                         move_to_dialog_set_selected_folder_store (dialog, selected);
2821                         g_object_unref (selected);
2822                 }
2823         }
2824 }
2825
2826 static void
2827 on_move_to_dialog_action_clicked (GtkButton *selection,
2828                                   gpointer   user_data)
2829 {
2830         TnyFolderStore *selected;
2831         GtkWidget *dialog;
2832         GtkWidget *folder_view;
2833         gboolean showing_folders;
2834
2835         dialog = (GtkWidget *) user_data;
2836         showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS));
2837         if (showing_folders) {
2838                 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2839                 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2840
2841                 if (selected)
2842                         gtk_dialog_response  (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
2843         }
2844 }
2845
2846 GtkWidget *
2847 modest_platform_create_move_to_dialog (GtkWindow *parent_window,
2848                                        GtkWidget **folder_view)
2849 {
2850         GtkWidget *dialog, *folder_view_container;
2851         GtkWidget *align;
2852         GtkWidget *buttons_hbox;
2853         GtkWidget *back_button;
2854         GdkPixbuf *back_pixbuf;
2855         GtkWidget *top_vbox;
2856         GtkWidget *action_button;
2857         GtkTreeSelection *selection;
2858
2859         /* Create dialog. We cannot use a touch selector because we
2860            need to use here the folder view widget directly */
2861         dialog = gtk_dialog_new_with_buttons (_("mcen_ti_moveto_folders_title"),
2862                                               GTK_WINDOW (parent_window),
2863                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR |
2864                                               GTK_DIALOG_DESTROY_WITH_PARENT,
2865                                               _HL("wdgt_bd_new"), MODEST_GTK_RESPONSE_NEW_FOLDER,
2866                                               NULL);
2867
2868         align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
2869         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_NONE);
2870         top_vbox = gtk_vbox_new (FALSE, MODEST_MARGIN_HALF);
2871
2872         /* Create folder view */
2873         *folder_view = modest_platform_create_folder_view (NULL);
2874
2875         modest_folder_view_set_cell_style (MODEST_FOLDER_VIEW (*folder_view),
2876                                            MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT);
2877         modest_folder_view_show_message_count (MODEST_FOLDER_VIEW (*folder_view),
2878                                                FALSE);
2879         tny_account_store_view_set_account_store (TNY_ACCOUNT_STORE_VIEW (*folder_view),
2880                                                   (TnyAccountStore *) modest_runtime_get_account_store ());
2881
2882         buttons_hbox = gtk_hbox_new (FALSE, MODEST_MARGIN_HALF);
2883         back_button = gtk_button_new ();
2884         back_pixbuf = modest_platform_get_icon (_FM("filemanager_folder_up"), MODEST_ICON_SIZE_BIG);
2885         if (back_pixbuf) {
2886                 gtk_button_set_image (GTK_BUTTON (back_button), gtk_image_new_from_pixbuf (back_pixbuf));
2887                 g_object_unref (back_pixbuf);
2888         }
2889
2890         action_button = hildon_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
2891                                            HILDON_BUTTON_ARRANGEMENT_VERTICAL);
2892         gtk_button_set_alignment (GTK_BUTTON (action_button), 0.0, 0.5);
2893
2894         gtk_box_pack_start (GTK_BOX (buttons_hbox), back_button, FALSE, FALSE, 0);
2895         gtk_box_pack_start (GTK_BOX (buttons_hbox), action_button, TRUE, TRUE, 0);
2896         gtk_widget_set_sensitive (GTK_WIDGET (back_button), FALSE);
2897         gtk_widget_set_sensitive (GTK_WIDGET (action_button), FALSE);
2898         gtk_box_pack_start (GTK_BOX (top_vbox), buttons_hbox, FALSE, FALSE, 0);
2899
2900         /* Create pannable and add it to the dialog */
2901         folder_view_container = hildon_pannable_area_new ();
2902         gtk_container_add (GTK_CONTAINER (folder_view_container), *folder_view);
2903         gtk_box_pack_start (GTK_BOX (top_vbox), folder_view_container, TRUE, TRUE, 0);
2904
2905         gtk_container_add (GTK_CONTAINER (align), top_vbox);
2906         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), align, TRUE, TRUE, 0);
2907
2908         gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 300);
2909
2910         gtk_widget_show (GTK_DIALOG (dialog)->vbox);
2911         gtk_widget_show (folder_view_container);
2912         gtk_widget_show (align);
2913         gtk_widget_show (top_vbox);
2914         gtk_widget_show (*folder_view);
2915         gtk_widget_show_all (back_button);
2916         gtk_widget_show (action_button);
2917         gtk_widget_show (buttons_hbox);
2918         gtk_widget_show (dialog);
2919
2920         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW, *folder_view);
2921         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON, back_button);
2922         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON, action_button);
2923         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE, folder_view_container);
2924
2925         /* Simulate the behaviour of a HildonPickerDialog by emitting
2926            a response when a folder is selected */
2927         g_signal_connect (*folder_view, "row-activated",
2928                           G_CALLBACK (on_move_to_dialog_row_activated),
2929                           dialog);
2930
2931         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (*folder_view));
2932         g_signal_connect (selection, "changed",
2933                           G_CALLBACK (on_move_to_dialog_selection_changed),
2934                           dialog);
2935
2936         g_signal_connect (action_button, "clicked",
2937                           G_CALLBACK (on_move_to_dialog_action_clicked),
2938                           dialog);
2939
2940         g_signal_connect (back_button, "clicked",
2941                           G_CALLBACK (on_move_to_dialog_back_clicked),
2942                           dialog);
2943
2944         move_to_dialog_show_accounts (dialog);
2945
2946         return dialog;
2947 }
2948
2949 TnyList *
2950 modest_platform_get_list_to_move (ModestWindow *window)
2951 {
2952         TnyList *list = NULL;
2953
2954         if (MODEST_IS_HEADER_WINDOW (window)) {
2955                 ModestHeaderView *header_view;
2956
2957                 header_view = modest_header_window_get_header_view (MODEST_HEADER_WINDOW (window));
2958                 list = modest_header_view_get_selected_headers (header_view);
2959         } else if (MODEST_IS_FOLDER_WINDOW (window)) {
2960                 ModestFolderView *folder_view;
2961                 TnyFolderStore *selected_folder;
2962
2963                 list = TNY_LIST (tny_simple_list_new ());
2964                 folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (window));
2965                 selected_folder = modest_folder_view_get_selected (folder_view);
2966                 if (selected_folder) {
2967                         tny_list_prepend (list, G_OBJECT (selected_folder));
2968                         g_object_unref (selected_folder);
2969                 }
2970                 return list;
2971         } else if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
2972                 TnyHeader *header;
2973
2974                 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
2975                 if (header) {
2976                         list = TNY_LIST (tny_simple_list_new ());
2977                         tny_list_prepend (list, G_OBJECT (header));
2978                         g_object_unref (header);
2979                 }
2980         } else {
2981                 g_return_val_if_reached (NULL);
2982         }
2983
2984         return list;
2985 }