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