e8fc6a8fa6d801af8639dddfb8da2edf73c69947
[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 <dbus_api/modest-dbus-callbacks.h>
43 #include <modest-osso-autosave-callbacks.h>
44 #include <libosso.h>
45 #include <tny-maemo-conic-device.h>
46 #include <tny-camel-folder.h>
47 #include <tny-simple-list.h>
48 #include <tny-merge-folder.h>
49 #include <tny-error.h>
50 #include <tny-folder.h>
51 #include <tny-account-store-view.h>
52 #include <gtk/gtkicontheme.h>
53 #include <gtk/gtkmenuitem.h>
54 #include <gtk/gtkmain.h>
55 #include <modest-text-utils.h>
56 #include "modest-tny-folder.h"
57 #include "modest-tny-account.h"
58 #include <string.h>
59 #include <libgnomevfs/gnome-vfs-mime-utils.h>
60 #include <modest-account-settings-dialog.h>
61 #include <modest-easysetup-wizard-dialog.h>
62 #include "modest-hildon2-sort-dialog.h"
63 #include <hildon/hildon.h>
64 #include <osso-mem.h>
65 #include "hildon2/modest-hildon2-details-dialog.h"
66 #include "hildon2/modest-hildon2-window-mgr.h"
67 #ifdef MODEST_USE_PROFILE
68 #include <keys_nokia.h>
69 #include <libprofile.h>
70 #endif
71 #include <canberra.h>
72 #include <modest-datetime-formatter.h>
73 #include "modest-header-window.h"
74 #include <modest-folder-window.h>
75 #include <modest-account-mgr.h>
76 #include <modest-account-mgr-helpers.h>
77 #include <modest-ui-constants.h>
78 #include <modest-selector-picker.h>
79 #include <modest-icon-names.h>
80
81 #ifdef MODEST_HAVE_MCE
82 #include <mce/dbus-names.h>
83 #endif /*MODEST_HAVE_MCE*/
84
85 #ifdef MODEST_HAVE_ABOOK
86 #include <libosso-abook/osso-abook.h>
87 #endif /*MODEST_HAVE_ABOOK*/
88
89 #ifdef MODEST_HAVE_LIBALARM
90 #include <alarmd/libalarm.h> /* For alarm_event_add(), etc. */
91 #endif /*MODEST_HAVE_LIBALARM*/
92
93
94 #define HILDON_OSSO_URI_ACTION "uri-action"
95 #define URI_ACTION_COPY "copy:"
96 #define MODEST_NOTIFICATION_CATEGORY "email-message"
97 #define MODEST_NEW_MAIL_LIGHTING_PATTERN "PatternCommunicationEmail"
98 #ifdef MODEST_USE_PROFILE
99 #define PROFILE_MAIL_TONE PROFILEKEY_EMAIL_ALERT_TONE
100 #define PROFILE_MAIL_VOLUME PROFILEKEY_EMAIL_ALERT_VOLUME
101 #else
102 #define MAIL_TONE "message-new-email"
103 #endif
104
105 #define COMMON_FOLDER_DIALOG_ENTRY "entry"
106 #define COMMON_FOLDER_DIALOG_ACCOUNT_PICKER "account-picker"
107 #define FOLDER_PICKER_CURRENT_FOLDER "current-folder"
108 #define MODEST_ALARMD_APPID PACKAGE_NAME
109
110
111 static void _modest_platform_play_email_tone (void);
112
113
114 static void     
115 on_modest_conf_update_interval_changed (ModestConf* self, 
116                                         const gchar *key, 
117                                         ModestConfEvent event,
118                                         ModestConfNotificationId id, 
119                                         gpointer user_data)
120 {
121         g_return_if_fail (key);
122         
123         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
124                 const guint update_interval_minutes = 
125                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
126                 modest_platform_set_update_interval (update_interval_minutes);
127         }
128 }
129
130
131
132 static gboolean
133 check_required_files (void)
134 {
135         FILE *mcc_file = modest_maemo_open_mcc_mapping_file (NULL);
136         if (!mcc_file) {
137                 g_printerr ("modest: check for mcc file failed\n");
138                 return FALSE;
139         } else 
140                 fclose (mcc_file);
141         
142         if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
143             access(MODEST_FALLBACK_PROVIDER_DATA_FILE, R_OK) != 0) {
144                 g_printerr ("modest: cannot find providers data\n");
145                 return FALSE;
146         }
147         
148         return TRUE;
149 }
150
151
152 /* the gpointer here is the osso_context. */
153 gboolean
154 modest_platform_init (int argc, char *argv[])
155 {
156         osso_context_t *osso_context;
157         
158         osso_hw_state_t hw_state = { 0 };
159         DBusConnection *con;    
160         GSList *acc_names;
161         
162         if (!check_required_files ()) {
163                 g_printerr ("modest: missing required files\n");
164                 return FALSE;
165         }
166         
167         osso_context =  osso_initialize(PACKAGE,PACKAGE_VERSION,
168                                         FALSE, NULL);   
169         if (!osso_context) {
170                 g_printerr ("modest: failed to acquire osso context\n");
171                 return FALSE;
172         }
173         modest_maemo_utils_set_osso_context (osso_context);
174
175         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
176                 g_printerr ("modest: could not get dbus connection\n");
177                 return FALSE;
178         }
179
180         /* Add a D-Bus handler to be used when the main osso-rpc 
181          * D-Bus handler has not handled something.
182          * We use this for D-Bus methods that need to use more complex types 
183          * than osso-rpc supports. 
184          */
185         if (!dbus_connection_add_filter (con,
186                                          modest_dbus_req_filter,
187                                          NULL,
188                                          NULL)) {
189
190                 g_printerr ("modest: Could not add D-Bus filter\n");
191                 return FALSE;
192         }
193
194         /* Register our simple D-Bus callbacks, via the osso API: */
195         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
196                                MODEST_DBUS_SERVICE, 
197                                MODEST_DBUS_OBJECT, 
198                                MODEST_DBUS_IFACE,
199                                modest_dbus_req_handler, NULL /* user_data */);
200         if (result != OSSO_OK) {
201                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
202                 return FALSE;
203         }
204
205         /* Register hardware event dbus callback: */
206         hw_state.shutdown_ind = TRUE;
207         osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
208
209         /* Register osso auto-save callbacks: */
210         result = osso_application_set_autosave_cb (osso_context, 
211                 modest_on_osso_application_autosave, NULL /* user_data */);
212         if (result != OSSO_OK) {
213                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
214                 return FALSE;
215         }
216         
217
218         /* Make sure that the update interval is changed whenever its gconf key 
219          * is changed */
220         /* CAUTION: we're not using here the
221            modest_conf_listen_to_namespace because we know that there
222            are other parts of Modest listening for this namespace, so
223            we'll receive the notifications anyway. We basically do not
224            use it because there is no easy way to do the
225            modest_conf_forget_namespace */
226         ModestConf *conf = modest_runtime_get_conf ();
227         g_signal_connect (G_OBJECT(conf),
228                           "key_changed",
229                           G_CALLBACK (on_modest_conf_update_interval_changed), 
230                           NULL);
231
232         /* only force the setting of the default interval, if there are actually
233          * any accounts */
234         acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
235         if (acc_names) {
236                 /* Get the initial update interval from gconf: */
237                 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
238                                                        MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
239                 modest_account_mgr_free_account_names (acc_names);
240         }
241
242         
243 #ifdef MODEST_HAVE_ABOOK
244         /* initialize the addressbook */
245         if (!osso_abook_init (&argc, &argv, osso_context)) {
246                 g_printerr ("modest: failed to initialized addressbook\n");
247                 return FALSE;
248         }
249 #endif /*MODEST_HAVE_ABOOK*/
250
251         return TRUE;
252 }
253
254 gboolean
255 modest_platform_uninit (void)
256 {
257         osso_context_t *osso_context =
258                 modest_maemo_utils_get_osso_context ();
259         if (osso_context)
260                 osso_deinitialize (osso_context);
261
262         return TRUE;
263 }
264
265
266
267
268 TnyDevice*
269 modest_platform_get_new_device (void)
270 {
271         return TNY_DEVICE (tny_maemo_conic_device_new ());
272 }
273
274 gchar*
275 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
276                                     gchar **effective_mime_type)
277 {
278         GString *mime_str = NULL;
279         gchar *icon_name  = NULL;
280         gchar **icons, **cursor;
281         
282         if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) 
283                 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
284         else {
285                 mime_str = g_string_new (mime_type);
286                 g_string_ascii_down (mime_str);
287         }
288         
289         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
290         
291         for (cursor = icons; cursor; ++cursor) {
292                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
293                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
294                         icon_name = g_strdup ("qgn_list_messagin");
295                         break;
296                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
297                         icon_name = g_strdup (*cursor);
298                         break;
299                 }
300         }
301         g_strfreev (icons);
302
303         if (effective_mime_type)
304                 *effective_mime_type = g_string_free (mime_str, FALSE);
305         else
306                 g_string_free (mime_str, TRUE);
307         
308         return icon_name;
309 }
310
311
312 static gboolean
313 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
314 {
315         GError *err = NULL;
316         gboolean result;
317
318         g_return_val_if_fail (uri, FALSE);
319         
320         result = hildon_uri_open (uri, action, &err);
321         if (!result) {
322                 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
323                             uri, action,  err && err->message ? err->message : "unknown error");
324                 if (err)
325                         g_error_free (err);
326         }
327         return result;
328 }
329
330
331
332 gboolean 
333 modest_platform_activate_uri (const gchar *uri)
334 {
335         HildonURIAction *action;
336         gboolean result = FALSE;
337         GSList *actions, *iter = NULL;
338         
339         g_return_val_if_fail (uri, FALSE);
340         if (!uri)
341                 return FALSE;
342
343         /* don't try to activate file: uri's -- they might confuse the user,
344          * and/or might have security implications */
345         if (!g_str_has_prefix (uri, "file:")) {
346                 
347                 actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
348                 
349                 for (iter = actions; iter; iter = g_slist_next (iter)) {
350                         action = (HildonURIAction*) iter->data;
351                         if (action && strcmp (hildon_uri_action_get_service (action),
352                                               "com.nokia.modest") == 0) {
353                                 result = checked_hildon_uri_open (uri, action);
354                                 break;
355                         }
356                 }
357                 
358                 /* if we could not open it with email, try something else */
359                 if (!result)
360                         result = checked_hildon_uri_open (uri, NULL);   
361         } 
362         
363         if (!result) {
364                 ModestWindow *parent =
365                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
366                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
367                                                 _("mcen_ib_unsupported_link"));
368                 g_warning ("%s: cannot open uri '%s'", __FUNCTION__,uri);
369         } 
370         
371         return result;
372 }
373
374 gboolean 
375 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
376 {
377         gint result = 0;
378         DBusConnection *con;
379         gchar *uri_path = NULL;
380         
381         uri_path = gnome_vfs_get_uri_from_local_path (path);    
382         con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
383         
384         if (mime_type)
385                 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
386         if (result != 1)
387                 result = hildon_mime_open_file (con, uri_path);
388         if (result != 1)
389                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"), FALSE);
390         
391         return result != 1;
392 }
393
394 typedef struct  {
395         GSList *actions;
396         gchar  *uri;
397 } ModestPlatformPopupInfo;
398
399 static gboolean
400 delete_uri_popup (GtkWidget *menu,
401                   GdkEvent *event,
402                   gpointer userdata)
403 {
404         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
405
406         g_free (popup_info->uri);
407         hildon_uri_free_actions (popup_info->actions);
408
409         return FALSE;
410 }
411
412 static void
413 activate_uri_popup_item (GtkMenuItem *menu_item,
414                          gpointer userdata)
415 {
416         GSList *node;
417         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
418         const gchar* action_name;
419
420         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
421         if (!action_name) {
422                 g_printerr ("modest: no action name defined\n");
423                 return;
424         }
425
426         /* special handling for the copy menu item -- copy the uri to the clipboard */
427         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
428         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
429                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
430                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
431
432                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
433                         action_name += strlen ("mailto:");
434                 
435                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
436                 modest_platform_information_banner (NULL, NULL, _CS("ecoc_ib_edwin_copied"));
437                 return; /* we're done */
438         }
439         
440         /* now, the real uri-actions... */
441         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
442                 HildonURIAction *action = (HildonURIAction *) node->data;
443                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
444                         if (!checked_hildon_uri_open (popup_info->uri, action)) {
445                                 ModestWindow *parent =
446                                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
447                                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
448                                                                 _("mcen_ib_unsupported_link"));
449                         }
450                         break;
451                 }
452         }
453 }
454
455 gboolean 
456 modest_platform_show_uri_popup (const gchar *uri)
457 {
458         GSList *actions_list;
459
460         if (uri == NULL)
461                 return FALSE;
462         
463         actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
464         if (actions_list) {
465
466                 GtkWidget *menu = gtk_menu_new ();
467                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
468
469                 /* don't add actions for file: uri's -- they might confuse the user,
470                  * and/or might have security implications
471                  * we still allow to copy the url though
472                  */
473                 if (!g_str_has_prefix (uri, "file:")) {                 
474                 
475                         GSList *node;                   
476                         popup_info->actions = actions_list;
477                         popup_info->uri = g_strdup (uri);
478
479                         for (node = actions_list; node != NULL; node = g_slist_next (node)) {
480                                 GtkWidget *menu_item;
481                                 const gchar *action_name;
482                                 const gchar *translation_domain;
483                                 HildonURIAction *action = (HildonURIAction *) node->data;
484                                 action_name = hildon_uri_action_get_name (action);
485                                 translation_domain = hildon_uri_action_get_translation_domain (action);
486                                 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
487                                 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
488                                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
489                                                   popup_info);
490
491                                 if (hildon_uri_is_default_action (action, NULL)) {
492                                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
493                                 } else {
494                                         gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
495                                 }
496                                 gtk_widget_show (menu_item);
497                         }
498                 }
499
500
501                 /* and what to do when the link is deleted */
502                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
503                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
504
505         } else {
506                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
507         }
508
509         return TRUE;
510 }
511
512
513 GdkPixbuf*
514 modest_platform_get_icon (const gchar *name, guint icon_size)
515 {
516         GError *err = NULL;
517         GdkPixbuf* pixbuf = NULL;
518         GtkIconTheme *current_theme = NULL;
519
520         g_return_val_if_fail (name, NULL);
521
522         /* strlen == 0 is not really an error; it just
523          * means the icon is not available
524          */
525         if (!name || strlen(name) == 0)
526                 return NULL;
527
528         current_theme = gtk_icon_theme_get_default ();
529         pixbuf = gtk_icon_theme_load_icon (current_theme, name, icon_size,
530                                            GTK_ICON_LOOKUP_NO_SVG,
531                                            &err);
532         if (!pixbuf) {
533                 g_printerr ("modest: error loading theme icon '%s': %s\n",
534                             name, err->message);
535                 g_error_free (err);
536         } 
537         return pixbuf;
538 }
539
540 const gchar*
541 modest_platform_get_app_name (void)
542 {
543         return _("mcen_ap_name");
544 }
545
546 static void
547 entry_insert_text (GtkEditable *editable,
548                    const gchar *text,
549                    gint         length,
550                    gint        *position,
551                    gpointer     data)
552 {
553         gchar *chars;
554         gint chars_length;
555
556         chars = gtk_editable_get_chars (editable, 0, -1);
557         chars_length = g_utf8_strlen (chars, -1);
558         g_free (chars);
559
560         /* Show WID-INF036 */
561         if (chars_length >= 20) {
562                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
563                                                  _CS("ckdg_ib_maximum_characters_reached"));
564         } else {
565                 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
566                         /* Show an error */
567                         gchar *tmp, *msg;
568
569                         tmp = g_strndup (folder_name_forbidden_chars,
570                                          FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
571                         msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
572                         hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)),
573                                                          NULL, msg);
574                         g_free (msg);
575                         g_free (tmp);
576                 } else {
577                         if (length >= 20) {
578                                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
579                                                                  _CS("ckdg_ib_maximum_characters_reached"));
580                         }
581                         /* Write the text in the entry if it's valid */
582                         g_signal_handlers_block_by_func (editable,
583                                                          (gpointer) entry_insert_text, data);
584                         gtk_editable_insert_text (editable, text, length, position);
585                         g_signal_handlers_unblock_by_func (editable,
586                                                            (gpointer) entry_insert_text, data);
587                 }
588         }
589         /* Do not allow further processing */
590         g_signal_stop_emission_by_name (editable, "insert_text");
591 }
592
593 static void
594 entry_changed (GtkEditable *editable,
595                gpointer     user_data)
596 {
597         gchar *chars;
598         GtkWidget *ok_button;
599         GList *buttons;
600
601         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
602         ok_button = GTK_WIDGET (buttons->data);
603
604         chars = gtk_editable_get_chars (editable, 0, -1);
605         g_return_if_fail (chars != NULL);
606
607
608         if (g_utf8_strlen (chars,-1) >= 20) {
609                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
610                                                  _CS("ckdg_ib_maximum_characters_reached"));
611         }
612         gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
613
614         /* Free */
615         g_list_free (buttons);
616         g_free (chars);
617 }
618
619
620
621 static void
622 on_response (GtkDialog *dialog,
623              gint response,
624              gpointer user_data)
625 {
626         GtkWidget *entry, *picker;
627         TnyFolderStore *parent;
628         const gchar *new_name;
629         gboolean exists;
630
631         if (response != GTK_RESPONSE_ACCEPT)
632                 return;
633         
634         /* Get entry */
635         entry = g_object_get_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ENTRY);
636         picker = g_object_get_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ACCOUNT_PICKER);
637         
638         parent = TNY_FOLDER_STORE (user_data);
639         new_name = gtk_entry_get_text (GTK_ENTRY (entry));
640         exists = FALSE;
641         
642         if (picker != NULL) {
643
644                 parent = g_object_get_data (G_OBJECT (picker), FOLDER_PICKER_CURRENT_FOLDER);
645         }
646
647         /* Look for another folder with the same name */
648         if (modest_tny_folder_has_subfolder_with_name (parent, 
649                                                        new_name,
650                                                        TRUE)) {
651                 exists = TRUE;
652         }
653         
654         if (!exists) {
655                 if (TNY_IS_ACCOUNT (parent) &&
656                     modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
657                     modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
658                                                                          new_name)) {
659                         exists = TRUE;
660                 }
661         }
662         
663         if (exists) {
664                 
665                 /* Show an error */
666                 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), 
667                                                 NULL, _CS("ckdg_ib_folder_already_exists"));
668                 /* Select the text */
669                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
670                 gtk_widget_grab_focus (entry);
671                 /* Do not close the dialog */
672                 g_signal_stop_emission_by_name (dialog, "response");
673         }
674
675 }
676
677 typedef struct _FolderChooserData {
678         TnyFolderStore *store;
679         GtkWidget *dialog;
680 } FolderChooserData;
681
682 static void
683 folder_chooser_activated (ModestFolderView *folder_view,
684                           TnyFolderStore *folder,
685                           FolderChooserData *userdata)
686 {
687         userdata->store = folder;
688         gtk_dialog_response (GTK_DIALOG (userdata->dialog), GTK_RESPONSE_OK);
689 }
690
691 static TnyFolderStore *
692 folder_chooser_dialog_run (ModestFolderView *original)
693 {
694         GtkWidget *folder_view;
695         FolderChooserData userdata = {NULL, NULL};
696         GtkWidget *pannable;
697         const gchar *visible_id = NULL;
698
699         userdata.dialog = hildon_dialog_new ();
700         pannable = hildon_pannable_area_new ();
701         folder_view = modest_platform_create_folder_view (NULL);
702
703         gtk_window_set_title (GTK_WINDOW (userdata.dialog), _FM("ckdg_ti_change_folder"));
704
705         modest_folder_view_copy_model (MODEST_FOLDER_VIEW (original), 
706                                        MODEST_FOLDER_VIEW (folder_view));
707
708         visible_id = 
709                 modest_folder_view_get_account_id_of_visible_server_account (MODEST_FOLDER_VIEW(original));
710         modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW(folder_view),
711                                                                      visible_id);
712
713         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (userdata.dialog)->vbox), pannable);
714         gtk_container_add (GTK_CONTAINER (pannable), folder_view);
715         gtk_widget_set_size_request (pannable, -1, 320);
716
717         gtk_widget_show (folder_view);
718         gtk_widget_show (pannable);
719         gtk_widget_show (userdata.dialog);
720         g_signal_connect (G_OBJECT (folder_view), "folder-activated", 
721                           G_CALLBACK (folder_chooser_activated), 
722                           (gpointer) &userdata);
723
724         gtk_dialog_run (GTK_DIALOG (userdata.dialog));
725         gtk_widget_destroy (userdata.dialog);
726
727         return userdata.store;
728 }
729
730 static gchar *
731 folder_store_get_display_name (TnyFolderStore *store)
732 {
733         if (TNY_IS_ACCOUNT (store)) {
734                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (store)))
735                         return modest_conf_get_string (modest_runtime_get_conf(),
736                                                        MODEST_CONF_DEVICE_NAME, NULL);
737                 else
738                         return g_strdup (tny_account_get_name (TNY_ACCOUNT (store)));
739         } else {
740                 gchar *fname;
741                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
742
743                 fname = g_strdup (tny_folder_get_name (TNY_FOLDER (store)));
744                 type = tny_folder_get_folder_type (TNY_FOLDER (store));
745                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (store)) ||
746                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (store))) {
747                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (store));
748                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
749                                 g_free (fname);
750                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
751                         }
752                 } else {
753                         /* Sometimes an special folder is reported by the server as
754                            NORMAL, like some versions of Dovecot */
755                         if (type == TNY_FOLDER_TYPE_NORMAL ||
756                             type == TNY_FOLDER_TYPE_UNKNOWN) {
757                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (store));
758                         }
759                 }
760
761                 if (type == TNY_FOLDER_TYPE_INBOX) {
762                         g_free (fname);
763                         fname = g_strdup (_("mcen_me_folder_inbox"));
764                 }
765                 return fname;
766         }
767 }
768
769 GtkWidget *
770 get_image_for_folder_store (TnyFolderStore *store,
771                             gint size)
772 {
773         GdkPixbuf *pixbuf;
774         const gchar *icon_name = NULL;
775         GtkWidget *image = NULL;
776
777         if (TNY_IS_ACCOUNT (store)) {
778                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (store)))
779                         icon_name = MODEST_FOLDER_ICON_LOCAL_FOLDERS;
780                 else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (store)))
781                         icon_name = MODEST_FOLDER_ICON_MMC;
782                 else
783                         icon_name = MODEST_FOLDER_ICON_ACCOUNT;
784         } else {
785                 TnyFolderType type = modest_tny_folder_guess_folder_type (TNY_FOLDER (store));
786                 if (modest_tny_folder_is_remote_folder (TNY_FOLDER (store))) {
787                         switch (type) {
788                         case TNY_FOLDER_TYPE_INBOX:
789                                 icon_name = MODEST_FOLDER_ICON_INBOX;
790                                 break;
791                         default:
792                                 icon_name = MODEST_FOLDER_ICON_ACCOUNT;
793                         }
794                 } else if (modest_tny_folder_is_local_folder (TNY_FOLDER (store))) {
795                         switch (type) {
796                         case TNY_FOLDER_TYPE_OUTBOX:
797                                 icon_name = MODEST_FOLDER_ICON_OUTBOX;
798                                 break;
799                         case TNY_FOLDER_TYPE_DRAFTS:
800                                 icon_name = MODEST_FOLDER_ICON_DRAFTS;
801                                 break;
802                         case TNY_FOLDER_TYPE_SENT:
803                                 icon_name = MODEST_FOLDER_ICON_SENT;
804                                 break;
805                         default:
806                                 icon_name = MODEST_FOLDER_ICON_NORMAL;
807                         }
808                 } else if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (store))) {
809                         icon_name = MODEST_FOLDER_ICON_MMC_FOLDER;
810                 }
811         }
812
813         /* Set icon */
814         pixbuf = modest_platform_get_icon (icon_name, size);
815
816         if (pixbuf) {
817                 image = gtk_image_new_from_pixbuf (pixbuf);
818                 g_object_unref (pixbuf);
819         }
820
821         return image;
822 }
823
824 static void
825 folder_picker_set_store (GtkButton *button, TnyFolderStore *store)
826 {
827         gchar *name;
828
829         if (store == NULL) {
830                 g_object_set_data (G_OBJECT (button), FOLDER_PICKER_CURRENT_FOLDER, NULL);
831         } else {
832                 GtkWidget *image;
833
834                 g_object_ref (store);
835                 g_object_set_data_full (G_OBJECT (button), FOLDER_PICKER_CURRENT_FOLDER, 
836                                         store, (GDestroyNotify) g_object_unref);
837                 name = folder_store_get_display_name (store);
838                 hildon_button_set_value (HILDON_BUTTON (button), name);
839                 g_free (name);
840
841                 /* Select icon */
842                 image = get_image_for_folder_store (store, MODEST_ICON_SIZE_SMALL);
843                 if (image)
844                         hildon_button_set_image (HILDON_BUTTON (button), image);
845         }
846 }
847
848 /* Always returns DUPs so you must free the returned value */
849 static gchar *
850 get_next_folder_name (const gchar *suggested_name, 
851                       TnyFolderStore *suggested_folder)
852 {
853         const gchar *default_name = _FM("ckdg_va_new_folder_name_stub");
854         unsigned int i;
855         gchar *real_suggested_name;
856
857         if (suggested_name !=NULL) {
858                 return g_strdup (suggested_name);
859         }
860
861         for(i = 0; i < 100; ++ i) {
862                 gboolean exists = FALSE;
863
864                 if (i == 0)
865                         real_suggested_name = g_strdup (default_name);
866                 else
867                         real_suggested_name = g_strdup_printf ("%s(%d)",
868                                                                _FM("ckdg_va_new_folder_name_stub"),
869                                                                i);
870                 exists = modest_tny_folder_has_subfolder_with_name (suggested_folder,
871                                                                     real_suggested_name,
872                                                                     TRUE);
873
874                 if (!exists)
875                         break;
876
877                 g_free (real_suggested_name);
878         }
879
880         /* Didn't find a free number */
881         if (i == 100)
882                 real_suggested_name = g_strdup (default_name);
883
884         return real_suggested_name;
885 }
886
887 typedef struct {
888         ModestFolderView *folder_view;
889         GtkEntry *entry;
890 } FolderPickerHelper;
891
892 static void
893 folder_picker_clicked (GtkButton *button,
894                        FolderPickerHelper *helper)
895 {
896         TnyFolderStore *store;
897
898         store = folder_chooser_dialog_run (helper->folder_view);
899         if (store) {
900                 const gchar *current_name;
901                 gboolean exists;
902
903                 folder_picker_set_store (GTK_BUTTON (button), store);
904
905                 /* Update the name of the folder */
906                 current_name = gtk_entry_get_text (helper->entry);
907                 exists = modest_tny_folder_has_subfolder_with_name (store,
908                                                                     current_name,
909                                                                     TRUE);
910                 if (exists) {
911                         gchar *new_name = get_next_folder_name (NULL, store);
912                         gtk_entry_set_text (helper->entry, new_name);
913                         g_free (new_name);
914                 }
915         }
916 }
917
918 static GtkWidget *
919 folder_picker_new (TnyFolderStore *suggested, FolderPickerHelper *helper)
920 {
921         GtkWidget *button;
922
923         button = hildon_button_new (MODEST_EDITABLE_SIZE,
924                                     HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
925
926         hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 1.0);
927
928         if (suggested) {
929                 folder_picker_set_store (GTK_BUTTON (button), suggested);
930         }
931
932         g_signal_connect (G_OBJECT (button), "clicked",
933                           G_CALLBACK (folder_picker_clicked),
934                           helper);
935
936         return button;
937 }
938
939
940 static gint
941 modest_platform_run_folder_common_dialog (GtkWindow *parent_window,
942                                           TnyFolderStore *suggested_parent,
943                                           const gchar *dialog_title,
944                                           const gchar *label_text,
945                                           const gchar *suggested_name,
946                                           gboolean show_name,
947                                           gboolean show_parent,
948                                           gchar **folder_name,
949                                           TnyFolderStore **parent)
950 {
951         GtkWidget *accept_btn = NULL; 
952         GtkWidget *dialog, *entry = NULL, *label_entry = NULL,  *label_location = NULL, *hbox;
953         GtkWidget *account_picker = NULL;
954         GList *buttons = NULL;
955         gint result;
956         GtkSizeGroup *sizegroup;
957         ModestFolderView *folder_view;
958         ModestWindow *folder_window;
959         ModestHildon2WindowMgr *window_mgr;
960         FolderPickerHelper *helper = NULL;
961         GtkWidget *top_vbox, *top_align;
962
963         window_mgr = (ModestHildon2WindowMgr *) modest_runtime_get_window_mgr ();
964         folder_window = modest_hildon2_window_mgr_get_folder_window (window_mgr);
965         g_return_val_if_fail (MODEST_IS_FOLDER_WINDOW (folder_window), GTK_RESPONSE_NONE);
966         
967         folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (folder_window));
968         
969         top_vbox = gtk_vbox_new (FALSE, 0);
970         top_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
971         gtk_alignment_set_padding (GTK_ALIGNMENT (top_align), 0, 0, MODEST_MARGIN_DOUBLE, 0);
972         
973         /* Ask the user for the folder name */
974         dialog = gtk_dialog_new_with_buttons (dialog_title,
975                                               parent_window,
976                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
977                                               _FM("ckdg_bd_new_folder_dialog_ok"),
978                                               GTK_RESPONSE_ACCEPT,
979                                               NULL);
980
981         /* Add accept button (with unsensitive handler) */
982         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
983         accept_btn = GTK_WIDGET (buttons->data);
984
985         sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
986
987         if (show_name) {
988                 label_entry = gtk_label_new (label_text);
989                 entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
990                 gtk_entry_set_max_length (GTK_ENTRY (entry), 20);
991
992                 gtk_misc_set_alignment (GTK_MISC (label_entry), 0.0, 0.5);
993                 gtk_size_group_add_widget (sizegroup, label_entry);
994                 
995                 if (suggested_name)
996                   gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
997                 else
998                         gtk_entry_set_text (GTK_ENTRY (entry), _FM("ckdg_va_new_folder_name_stub"));
999                 gtk_entry_set_width_chars (GTK_ENTRY (entry),
1000                                            MAX (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1),
1001                                                 g_utf8_strlen (_FM("ckdg_va_new_folder_name_stub"), -1)));
1002                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
1003         }
1004         
1005         if (show_parent) {
1006           
1007                 label_location = gtk_label_new (_FM("ckdg_fi_new_folder_location"));
1008
1009                 gtk_misc_set_alignment (GTK_MISC (label_location), 0.0, 0.5);
1010                 gtk_size_group_add_widget (sizegroup, label_location);
1011
1012                 helper = g_slice_new0 (FolderPickerHelper);
1013                 helper->folder_view = folder_view;
1014                 helper->entry = (GtkEntry *) entry;
1015
1016                 account_picker = folder_picker_new (suggested_parent, helper);
1017         }
1018
1019         g_object_unref (sizegroup);
1020         
1021         /* Connect to the response method to avoid closing the dialog
1022            when an invalid name is selected*/
1023         g_signal_connect (dialog,
1024                           "response",
1025                           G_CALLBACK (on_response),
1026                           suggested_parent);
1027         
1028         if (show_name) {
1029                 /* Track entry changes */
1030                 g_signal_connect (entry,
1031                                   "insert-text",
1032                                   G_CALLBACK (entry_insert_text),
1033                                   dialog);
1034                 g_signal_connect (entry,
1035                                   "changed",
1036                                   G_CALLBACK (entry_changed),
1037                                   dialog);
1038         }
1039         
1040         
1041         /* Some locales like pt_BR need this to get the full window
1042            title shown */
1043         gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1);
1044         
1045         /* Create the hbox */
1046         if (show_name) {
1047                 hbox = gtk_hbox_new (FALSE, 12);
1048                 gtk_box_pack_start (GTK_BOX (hbox), label_entry, FALSE, FALSE, 0);
1049                 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1050                 
1051                 /* Add hbox to dialog */
1052                 gtk_box_pack_start (GTK_BOX (top_vbox), 
1053                                     hbox, FALSE, FALSE, 0);
1054                 g_object_set_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ENTRY, entry);
1055         }
1056
1057         if (show_parent) {
1058                 hbox = gtk_hbox_new (FALSE, 12);
1059                 gtk_box_pack_start (GTK_BOX (hbox), label_location, FALSE, FALSE, 0);
1060                 gtk_box_pack_start (GTK_BOX (hbox), account_picker, TRUE, TRUE, 0);
1061
1062                 /* Add hbox to dialog */
1063                 gtk_box_pack_start (GTK_BOX (top_vbox), 
1064                                     hbox, FALSE, FALSE, 0);
1065                 g_object_set_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ACCOUNT_PICKER, account_picker);
1066         }
1067         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1068                                      GTK_WINDOW (dialog), parent_window);
1069
1070         gtk_container_add (GTK_CONTAINER (top_align), top_vbox);
1071         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), top_align, TRUE, TRUE, 0);
1072
1073         gtk_widget_show_all (GTK_WIDGET(dialog));
1074
1075         result = gtk_dialog_run (GTK_DIALOG(dialog));
1076         if (result == GTK_RESPONSE_ACCEPT) {
1077                 if (show_name)
1078                         *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
1079                 if (show_parent) {
1080                         *parent = g_object_get_data (G_OBJECT (account_picker), FOLDER_PICKER_CURRENT_FOLDER);
1081                         if (*parent)
1082                                 g_object_ref (*parent);
1083                 }
1084         }
1085
1086         gtk_widget_destroy (dialog);
1087
1088         if (helper)
1089                 g_slice_free (FolderPickerHelper, helper);
1090
1091         while (gtk_events_pending ())
1092                 gtk_main_iteration ();
1093
1094         return result;
1095 }
1096
1097 gint
1098 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
1099                                        TnyFolderStore *suggested_folder,
1100                                        gchar *suggested_name,
1101                                        gchar **folder_name,
1102                                        TnyFolderStore **parent_folder)
1103 {
1104         gchar *real_suggested_name = NULL;
1105         gint result;
1106         ModestTnyAccountStore *acc_store;
1107         TnyAccount *account;
1108
1109         real_suggested_name = get_next_folder_name ((const gchar *) suggested_name,
1110                                                     suggested_folder);
1111
1112         /* In hildon 2.2 we always suggest the archive folder as parent */
1113         acc_store = modest_runtime_get_account_store ();
1114         account = modest_tny_account_store_get_mmc_folders_account (acc_store);
1115         if (account) {
1116                 suggested_folder = (TnyFolderStore *)
1117                         modest_tny_account_get_special_folder (account,
1118                                                                TNY_FOLDER_TYPE_ARCHIVE);
1119                 g_object_unref (account);
1120                 account = NULL;
1121         }
1122
1123         /* If there is not archive folder then fallback to local folders account */
1124         if (!suggested_folder)
1125                 suggested_folder = (TnyFolderStore *)
1126                         modest_tny_account_store_get_local_folders_account (acc_store);
1127
1128         result = modest_platform_run_folder_common_dialog (parent_window,
1129                                                            suggested_folder,
1130                                                            _HL("ckdg_ti_new_folder"),
1131                                                            _FM("ckdg_fi_new_folder_name"),
1132                                                            real_suggested_name,
1133                                                            TRUE,
1134                                                            TRUE,
1135                                                            folder_name,
1136                                                            parent_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 void
2439 modest_platform_run_header_details_dialog (GtkWindow *parent_window,
2440                                            TnyHeader *header)
2441 {
2442         GtkWidget *dialog;
2443
2444         /* Create dialog */
2445         dialog = modest_hildon2_details_dialog_new_with_header (parent_window, header);
2446
2447         /* Run dialog */
2448         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2449                                      GTK_WINDOW (dialog),
2450                                      parent_window);
2451         gtk_widget_show_all (dialog);
2452
2453         g_signal_connect_swapped (dialog, "response", 
2454                                   G_CALLBACK (gtk_widget_destroy),
2455                                   dialog);
2456 }
2457
2458 osso_context_t *
2459 modest_platform_get_osso_context (void)
2460 {
2461         return modest_maemo_utils_get_osso_context ();
2462 }
2463
2464 static void
2465 _modest_platform_play_email_tone (void)
2466 {
2467         gchar *mail_tone;
2468         gint mail_volume_int;
2469         int ret;
2470         ca_context *ca_con = NULL;
2471         ca_proplist *pl = NULL;
2472
2473 #ifdef MODEST_USE_PROFILE
2474         gchar *active_profile;
2475         gchar *mail_volume;
2476
2477         active_profile = profile_get_profile ();
2478         mail_tone = profile_get_value (active_profile, PROFILE_MAIL_TONE);
2479         mail_volume = profile_get_value (active_profile, PROFILE_MAIL_VOLUME);
2480         mail_volume_int = profile_parse_int (mail_volume);
2481         g_free (mail_volume);
2482         g_free (active_profile);
2483 #else
2484         mail_tone = MAIL_TONE;
2485         mail_volume_int = 100;
2486 #endif
2487
2488         if (mail_tone && !strstr (mail_tone, "/")) {
2489                 gchar *tmp;
2490
2491                 tmp = g_strconcat ("/usr/share/sounds", mail_tone, NULL);
2492                 g_free (mail_tone);
2493                 mail_tone = tmp;
2494         }
2495
2496         if (mail_volume_int > 0) {
2497
2498                 if ((ret = ca_context_create(&ca_con)) != CA_SUCCESS) {
2499                         g_warning("ca_context_create: %s\n", ca_strerror(ret));
2500                         return;
2501                 }
2502
2503                 if ((ret = ca_context_open(ca_con)) != CA_SUCCESS) {
2504                         g_warning("ca_context_open: %s\n", ca_strerror(ret));
2505                         ca_context_destroy(ca_con);
2506                         return;
2507                 }
2508
2509                 ca_proplist_create(&pl);
2510                 ca_proplist_sets(pl, CA_PROP_MEDIA_FILENAME, mail_tone);
2511                 ca_proplist_setf(pl, CA_PROP_CANBERRA_VOLUME, "%f", (gfloat) mail_volume_int);
2512
2513                 ret = ca_context_play_full(ca_con, 0, pl, NULL, NULL);
2514                 g_debug("ca_context_play_full (vol %f): %s\n", (gfloat) mail_volume_int, ca_strerror(ret));
2515
2516                 ca_proplist_destroy(pl);
2517                 ca_context_destroy(ca_con);
2518         }
2519
2520         g_free (mail_tone);
2521 }
2522
2523 #define MOVE_TO_DIALOG_FOLDER_VIEW "folder-view"
2524 #define MOVE_TO_DIALOG_BACK_BUTTON "back-button"
2525 #define MOVE_TO_DIALOG_ACTION_BUTTON "action-button"
2526 #define MOVE_TO_DIALOG_SHOWING_FOLDERS "showing-folders"
2527 #define MOVE_TO_DIALOG_PANNABLE "pannable"
2528 #define MOVE_TO_FOLDER_SEPARATOR "/"
2529
2530 static void
2531 move_to_dialog_set_selected_folder_store (GtkWidget *dialog, 
2532                                           TnyFolderStore *folder_store)
2533 {
2534         GtkWidget *action_button;
2535         GtkWidget *image = NULL;
2536         TnyAccount *account;
2537         gchar *account_name = NULL;
2538
2539         action_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2540
2541         /* Get account name */
2542         if (TNY_IS_FOLDER (folder_store))
2543                 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2544         else
2545                 account = g_object_ref (folder_store);
2546
2547         if (modest_tny_account_is_virtual_local_folders (account))
2548                 account_name = modest_conf_get_string (modest_runtime_get_conf(),
2549                                                        MODEST_CONF_DEVICE_NAME, NULL);
2550
2551         if (!account_name)
2552                 account_name = g_strdup (tny_account_get_name (account));
2553
2554         g_object_unref (account);
2555
2556         /* Set title of button: account or folder name */
2557         if (TNY_IS_FOLDER (folder_store)) {
2558                 hildon_button_set_title (HILDON_BUTTON (action_button), 
2559                                          tny_folder_get_name (TNY_FOLDER (folder_store)));
2560         } else {
2561                 hildon_button_set_title (HILDON_BUTTON (action_button), account_name);
2562         }
2563
2564         /* Set value of button, folder full name */
2565         if (TNY_IS_CAMEL_FOLDER (folder_store)) {
2566                 gchar *full_name = g_strconcat (account_name, MOVE_TO_FOLDER_SEPARATOR,
2567                                                 tny_camel_folder_get_full_name (TNY_CAMEL_FOLDER (folder_store)),
2568                                                 NULL);
2569                 hildon_button_set_value (HILDON_BUTTON (action_button), full_name);
2570                 g_free (full_name);
2571         }
2572         g_free (account_name);
2573
2574         /* Set image for the button */
2575         image = get_image_for_folder_store (folder_store, MODEST_ICON_SIZE_BIG);
2576         if (image)
2577                 hildon_button_set_image (HILDON_BUTTON (action_button), image);
2578 }
2579
2580 static void
2581 move_to_dialog_show_accounts (GtkWidget *dialog)
2582 {
2583         GtkWidget *back_button;
2584         GtkWidget *folder_view;
2585         GtkWidget *pannable;
2586         GtkWidget *action_button;
2587
2588         back_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON));
2589         action_button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2590         folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2591         pannable = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE));
2592
2593         gtk_widget_set_sensitive (back_button, FALSE);
2594         gtk_widget_set_sensitive (action_button, FALSE);
2595
2596         /* Need to set this here, otherwise callbacks called because
2597            of filtering won't perform correctly */
2598         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS, GINT_TO_POINTER (FALSE));
2599
2600         /* Reset action button */
2601         hildon_button_set_title (HILDON_BUTTON (action_button), NULL);
2602         hildon_button_set_value (HILDON_BUTTON (action_button), NULL);
2603         hildon_button_set_image (HILDON_BUTTON (action_button), NULL);
2604
2605         modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW (folder_view), NULL);
2606         modest_folder_view_show_non_move_folders (MODEST_FOLDER_VIEW (folder_view), TRUE);
2607         modest_folder_view_set_style (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
2608         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2609                                          MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2610         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view),
2611                                        MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2612         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view), 
2613                                          MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS);
2614         modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view), 
2615                                        MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS);
2616         hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (pannable), 0, 0);
2617 }
2618
2619 static void
2620 move_to_dialog_show_folders (GtkWidget *dialog, TnyFolderStore *folder_store)
2621 {
2622         GtkWidget *back_button;
2623         GtkWidget *folder_view;
2624         TnyAccount *account;
2625         const gchar *account_id;
2626         GtkWidget *pannable;
2627         GtkWidget *action_button;
2628
2629         back_button =
2630                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON));
2631         action_button =
2632                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON));
2633         folder_view =
2634                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2635         pannable =
2636                 GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE));
2637
2638         gtk_widget_set_sensitive (back_button, TRUE);
2639         gtk_widget_set_sensitive (action_button, TRUE);
2640
2641         /* Need to set this here, otherwise callbacks called because
2642            of filtering won't perform correctly */
2643         g_object_set_data (G_OBJECT (dialog),
2644                            MOVE_TO_DIALOG_SHOWING_FOLDERS,
2645                            GINT_TO_POINTER (TRUE));
2646
2647         account = TNY_ACCOUNT (folder_store);
2648         if (modest_tny_account_is_virtual_local_folders (account)) {
2649                 account_id = tny_account_get_id (account);
2650                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2651                                                MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2652         } else if (modest_tny_account_is_memory_card_account (account)) {
2653                 account_id = tny_account_get_id (account);
2654                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2655                                                MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2656         } else {
2657                 account_id = tny_account_get_id (account);
2658                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2659                                                MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS);
2660                 modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view),
2661                                                MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS);
2662         }
2663
2664         move_to_dialog_set_selected_folder_store (dialog, folder_store);
2665         modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW (folder_view),
2666                                                                      account_id);
2667
2668         modest_folder_view_show_non_move_folders (MODEST_FOLDER_VIEW (folder_view), FALSE);
2669         modest_folder_view_set_style (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2670         modest_folder_view_set_filter (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS);
2671         modest_folder_view_unset_filter (MODEST_FOLDER_VIEW (folder_view), MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS);
2672         hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (pannable), 0, 0);
2673 }
2674
2675 static void
2676 on_move_to_dialog_back_clicked (GtkButton *button,
2677                                 gpointer userdata)
2678 {
2679         GtkWidget *dialog = (GtkWidget *) userdata;
2680
2681         /* Back to show accounts */
2682         move_to_dialog_show_accounts (dialog);
2683 }
2684
2685 static void
2686 on_move_to_dialog_row_activated (GtkTreeView       *tree_view,
2687                                     GtkTreePath       *path,
2688                                     GtkTreeViewColumn *column,
2689                                     gpointer           user_data)
2690 {
2691         TnyFolderStore *selected = NULL;
2692         GtkWidget *dialog;
2693         GtkWidget *folder_view;
2694         gboolean showing_folders;
2695
2696         dialog = (GtkWidget *) user_data;
2697         showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), 
2698                                                               MOVE_TO_DIALOG_SHOWING_FOLDERS));
2699
2700         folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), 
2701                                                      MOVE_TO_DIALOG_FOLDER_VIEW));
2702
2703         selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2704         if (!selected)
2705                 return;
2706
2707         if (!showing_folders) {
2708                 gboolean valid = TRUE;
2709
2710                 if (TNY_IS_ACCOUNT (selected) &&
2711                     modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (selected))) {
2712                         ModestProtocolType protocol_type;
2713
2714                         protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (selected));
2715                         valid  = !modest_protocol_registry_protocol_type_has_tag 
2716                                 (modest_runtime_get_protocol_registry (),
2717                                  protocol_type,
2718                                  MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2719                 }
2720                 if (valid)
2721                         move_to_dialog_show_folders (dialog, selected);
2722         } else {
2723                 move_to_dialog_set_selected_folder_store (dialog, selected);
2724         }
2725 }
2726
2727 static void
2728 on_move_to_dialog_selection_changed (GtkTreeSelection *selection,
2729                                      gpointer          user_data)
2730 {
2731         gboolean showing_folders;
2732         GtkWidget *dialog;
2733
2734         dialog = (GtkWidget *) user_data;
2735         showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS));
2736         if (showing_folders) {
2737                 TnyFolderStore *selected;
2738                 GtkWidget *folder_view;
2739
2740                 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2741                 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2742
2743                 if (selected) {
2744                         move_to_dialog_set_selected_folder_store (dialog, selected);
2745                         g_object_unref (selected);
2746                 }
2747         }
2748 }
2749
2750 static void
2751 on_move_to_dialog_action_clicked (GtkButton *selection,
2752                                   gpointer   user_data)
2753 {
2754         TnyFolderStore *selected;
2755         GtkWidget *dialog;
2756         GtkWidget *folder_view;
2757         gboolean showing_folders;
2758
2759         dialog = (GtkWidget *) user_data;
2760         showing_folders = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_SHOWING_FOLDERS));
2761         if (showing_folders) {
2762                 folder_view = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW));
2763                 selected = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
2764
2765                 if (selected)
2766                         gtk_dialog_response  (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
2767         }
2768 }
2769
2770 GtkWidget *
2771 modest_platform_create_move_to_dialog (GtkWindow *parent_window,
2772                                        GtkWidget **folder_view)
2773 {
2774         GtkWidget *dialog, *folder_view_container;
2775         GtkWidget *align;
2776         GtkWidget *buttons_hbox;
2777         GtkWidget *back_button;
2778         GdkPixbuf *back_pixbuf;
2779         GtkWidget *top_vbox;
2780         GtkWidget *action_button;
2781         GtkTreeSelection *selection;
2782
2783         /* Create dialog. We cannot use a touch selector because we
2784            need to use here the folder view widget directly */
2785         dialog = gtk_dialog_new_with_buttons (_("mcen_ti_moveto_folders_title"),
2786                                               GTK_WINDOW (parent_window),
2787                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR |
2788                                               GTK_DIALOG_DESTROY_WITH_PARENT,
2789                                               _HL("wdgt_bd_new"), MODEST_GTK_RESPONSE_NEW_FOLDER,
2790                                               NULL);
2791
2792         align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
2793         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, MODEST_MARGIN_DOUBLE, MODEST_MARGIN_NONE);
2794         top_vbox = gtk_vbox_new (FALSE, MODEST_MARGIN_HALF);
2795
2796         /* Create folder view */
2797         *folder_view = modest_platform_create_folder_view (NULL);
2798
2799         modest_folder_view_set_cell_style (MODEST_FOLDER_VIEW (*folder_view),
2800                                            MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT);
2801         modest_folder_view_show_message_count (MODEST_FOLDER_VIEW (*folder_view),
2802                                                FALSE);
2803         tny_account_store_view_set_account_store (TNY_ACCOUNT_STORE_VIEW (*folder_view),
2804                                                   (TnyAccountStore *) modest_runtime_get_account_store ());
2805
2806         buttons_hbox = gtk_hbox_new (FALSE, MODEST_MARGIN_HALF);
2807         back_button = gtk_button_new ();
2808         back_pixbuf = modest_platform_get_icon (_FM("filemanager_folder_up"), MODEST_ICON_SIZE_BIG);
2809         if (back_pixbuf) {
2810                 gtk_button_set_image (GTK_BUTTON (back_button), gtk_image_new_from_pixbuf (back_pixbuf));
2811                 g_object_unref (back_pixbuf);
2812         }
2813
2814         action_button = hildon_button_new (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
2815                                            HILDON_BUTTON_ARRANGEMENT_VERTICAL);
2816         gtk_button_set_alignment (GTK_BUTTON (action_button), 0.0, 0.5);
2817
2818         gtk_box_pack_start (GTK_BOX (buttons_hbox), back_button, FALSE, FALSE, 0);
2819         gtk_box_pack_start (GTK_BOX (buttons_hbox), action_button, TRUE, TRUE, 0);
2820         gtk_widget_set_sensitive (GTK_WIDGET (back_button), FALSE);
2821         gtk_widget_set_sensitive (GTK_WIDGET (action_button), FALSE);
2822         gtk_box_pack_start (GTK_BOX (top_vbox), buttons_hbox, FALSE, FALSE, 0);
2823
2824         /* Create pannable and add it to the dialog */
2825         folder_view_container = hildon_pannable_area_new ();
2826         gtk_container_add (GTK_CONTAINER (folder_view_container), *folder_view);
2827         gtk_box_pack_start (GTK_BOX (top_vbox), folder_view_container, TRUE, TRUE, 0);
2828
2829         gtk_container_add (GTK_CONTAINER (align), top_vbox);
2830         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), align, TRUE, TRUE, 0);
2831
2832         gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 300);
2833
2834         gtk_widget_show (GTK_DIALOG (dialog)->vbox);
2835         gtk_widget_show (folder_view_container);
2836         gtk_widget_show (align);
2837         gtk_widget_show (top_vbox);
2838         gtk_widget_show (*folder_view);
2839         gtk_widget_show_all (back_button);
2840         gtk_widget_show (action_button);
2841         gtk_widget_show (buttons_hbox);
2842         gtk_widget_show (dialog);
2843
2844         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_FOLDER_VIEW, *folder_view);
2845         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_BACK_BUTTON, back_button);
2846         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_ACTION_BUTTON, action_button);
2847         g_object_set_data (G_OBJECT (dialog), MOVE_TO_DIALOG_PANNABLE, folder_view_container);
2848
2849         /* Simulate the behaviour of a HildonPickerDialog by emitting
2850            a response when a folder is selected */
2851         g_signal_connect (*folder_view, "row-activated",
2852                           G_CALLBACK (on_move_to_dialog_row_activated),
2853                           dialog);
2854
2855         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (*folder_view));
2856         g_signal_connect (selection, "changed",
2857                           G_CALLBACK (on_move_to_dialog_selection_changed),
2858                           dialog);
2859
2860         g_signal_connect (action_button, "clicked",
2861                           G_CALLBACK (on_move_to_dialog_action_clicked),
2862                           dialog);
2863
2864         g_signal_connect (back_button, "clicked",
2865                           G_CALLBACK (on_move_to_dialog_back_clicked),
2866                           dialog);
2867
2868         move_to_dialog_show_accounts (dialog);
2869
2870         return dialog;
2871 }
2872
2873 TnyList *
2874 modest_platform_get_list_to_move (ModestWindow *window)
2875 {
2876         TnyList *list = NULL;
2877
2878         if (MODEST_IS_HEADER_WINDOW (window)) {
2879                 ModestHeaderView *header_view;
2880
2881                 header_view = modest_header_window_get_header_view (MODEST_HEADER_WINDOW (window));
2882                 list = modest_header_view_get_selected_headers (header_view);
2883         } else if (MODEST_IS_FOLDER_WINDOW (window)) {
2884                 ModestFolderView *folder_view;
2885                 TnyFolderStore *selected_folder;
2886
2887                 list = TNY_LIST (tny_simple_list_new ());
2888                 folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (window));
2889                 selected_folder = modest_folder_view_get_selected (folder_view);
2890                 if (selected_folder) {
2891                         tny_list_prepend (list, G_OBJECT (selected_folder));
2892                         g_object_unref (selected_folder);
2893                 }
2894                 return list;
2895         } else if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
2896                 TnyHeader *header;
2897
2898                 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
2899                 if (header) {
2900                         list = TNY_LIST (tny_simple_list_new ());
2901                         tny_list_prepend (list, G_OBJECT (header));
2902                         g_object_unref (header);
2903                 }
2904         } else {
2905                 g_return_val_if_reached (NULL);
2906         }
2907
2908         return list;
2909 }