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