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