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