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