* Added a new parametter to modest_platform_show_information_banner that...
[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 <easysetup/modest-easysetup-wizard-dialog.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"), FALSE);
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                                    MAX (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1),
842                                         g_utf8_strlen (_("mcen_ia_default_folder_name"), -1)));
843         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
844
845         /* Connect to the response method to avoid closing the dialog
846            when an invalid name is selected*/
847         g_signal_connect (dialog,
848                           "response",
849                           G_CALLBACK (on_response),
850                           parent);
851
852         /* Track entry changes */
853         g_signal_connect (entry,
854                           "insert-text",
855                           G_CALLBACK (entry_insert_text),
856                           dialog);
857         g_signal_connect (entry,
858                           "changed",
859                           G_CALLBACK (entry_changed),
860                           dialog);
861
862         /* Create the hbox */
863         hbox = gtk_hbox_new (FALSE, 12);
864         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
865         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
866
867         /* Add hbox to dialog */
868         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
869                             hbox, FALSE, FALSE, 0);
870
871         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
872         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
873
874         /* Some locales like pt_BR need this to get the full window
875            title shown */
876         gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1);
877                 
878         result = gtk_dialog_run (GTK_DIALOG(dialog));
879         if (result == GTK_RESPONSE_ACCEPT)
880                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
881
882         gtk_widget_destroy (dialog);
883
884         while (gtk_events_pending ())
885                 gtk_main_iteration ();
886
887         return result;
888 }
889
890 gint
891 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
892                                        TnyFolderStore *parent_folder,
893                                        gchar *suggested_name,
894                                        gchar **folder_name)
895 {
896         gchar *real_suggested_name = NULL, *tmp = NULL;
897         gint result;
898
899         if(suggested_name == NULL)
900         {
901                 const gchar *default_name = _("mcen_ia_default_folder_name");
902                 unsigned int i;
903                 gchar num_str[3];
904
905                 for(i = 0; i < 100; ++ i) {
906                         gboolean exists = FALSE;
907
908                         sprintf(num_str, "%.2u", i);
909
910                         if (i == 0)
911                                 real_suggested_name = g_strdup (default_name);
912                         else
913                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
914                                                                        num_str);
915                         exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
916                                                                             real_suggested_name,
917                                                                             TRUE);
918
919                         if (!exists)
920                                 break;
921
922                         g_free (real_suggested_name);
923                 }
924
925                 /* Didn't find a free number */
926                 if (i == 100)
927                         real_suggested_name = g_strdup (default_name);
928         } else {
929                 real_suggested_name = suggested_name;
930         }
931
932         tmp = g_strconcat (_("mcen_fi_new_folder_name"), ":", NULL);
933         result = modest_platform_run_folder_name_dialog (parent_window, 
934                                                          parent_folder,
935                                                          _("mcen_ti_new_folder"),
936                                                          tmp,
937                                                          real_suggested_name,
938                                                          folder_name);
939         g_free (tmp);
940
941         if (suggested_name == NULL)
942                 g_free(real_suggested_name);
943
944         return result;
945 }
946
947 gint
948 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
949                                           TnyFolderStore *parent_folder,
950                                           const gchar *suggested_name,
951                                           gchar **folder_name)
952 {
953         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
954
955         return modest_platform_run_folder_name_dialog (parent_window, 
956                                                        parent_folder,
957                                                        _HL("ckdg_ti_rename_folder"),
958                                                        _HL("ckdg_fi_rename_name"),
959                                                        suggested_name,
960                                                        folder_name);
961 }
962
963
964
965 static void
966 on_destroy_dialog (GtkDialog *dialog)
967 {
968         gtk_widget_destroy (GTK_WIDGET(dialog));
969         if (gtk_events_pending ())
970                 gtk_main_iteration ();
971 }
972
973 gint
974 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
975                                          const gchar *message)
976 {
977         GtkWidget *dialog;
978         gint response;
979         
980         dialog = hildon_note_new_confirmation (parent_window, message);
981         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
982                                      GTK_WINDOW (dialog));
983
984         response = gtk_dialog_run (GTK_DIALOG (dialog));
985
986         on_destroy_dialog (GTK_DIALOG(dialog));
987         
988         while (gtk_events_pending ())
989                 gtk_main_iteration ();
990
991         return response;
992 }
993
994 gint
995 modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window,
996                                                       const gchar *message,
997                                                       const gchar *button_accept,
998                                                       const gchar *button_cancel)
999 {
1000         GtkWidget *dialog;
1001         gint response;
1002         
1003         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1004                                                            button_accept, GTK_RESPONSE_ACCEPT,
1005                                                            button_cancel, GTK_RESPONSE_CANCEL,
1006                                                            NULL);
1007         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1008                                      GTK_WINDOW (dialog));
1009
1010         response = gtk_dialog_run (GTK_DIALOG (dialog));
1011
1012         on_destroy_dialog (GTK_DIALOG(dialog));
1013         
1014         while (gtk_events_pending ())
1015                 gtk_main_iteration ();
1016
1017         return response;
1018 }
1019         
1020 gint
1021 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
1022                                    const gchar *message)
1023 {
1024         GtkWidget *dialog;
1025         gint response;
1026         
1027         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1028                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
1029                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
1030                                                            NULL);
1031         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
1032         response = gtk_dialog_run (GTK_DIALOG (dialog));
1033         
1034         on_destroy_dialog (GTK_DIALOG(dialog));
1035
1036         while (gtk_events_pending ())
1037                 gtk_main_iteration ();
1038
1039         return response;
1040 }
1041
1042
1043
1044 void
1045 modest_platform_run_information_dialog (GtkWindow *parent_window,
1046                                         const gchar *message,
1047                                         gboolean block)
1048 {
1049         GtkWidget *note;
1050         
1051         note = hildon_note_new_information (parent_window, message);
1052         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1053                                      GTK_WINDOW (note));
1054         
1055         if (block) {
1056                 gtk_dialog_run (GTK_DIALOG (note));
1057         
1058                 on_destroy_dialog (GTK_DIALOG (note));
1059
1060                 while (gtk_events_pending ())
1061                         gtk_main_iteration ();
1062         } else {
1063                 g_signal_connect_swapped (note,
1064                                           "response", 
1065                                           G_CALLBACK (on_destroy_dialog),
1066                                           note);
1067
1068                 gtk_widget_show_all (note);
1069         }
1070 }
1071
1072 typedef struct _ConnectAndWaitData {
1073         GMutex *mutex;
1074         GMainLoop *wait_loop;
1075         gboolean has_callback;
1076         gulong handler;
1077 } ConnectAndWaitData;
1078
1079
1080 static void
1081 quit_wait_loop (TnyAccount *account,
1082                 ConnectAndWaitData *data) 
1083 {
1084         /* Set the has_callback to TRUE (means that the callback was
1085            executed and wake up every code waiting for cond to be
1086            TRUE */
1087         g_mutex_lock (data->mutex);
1088         data->has_callback = TRUE;
1089         if (data->wait_loop)
1090                 g_main_loop_quit (data->wait_loop);
1091         g_mutex_unlock (data->mutex);
1092 }
1093
1094 static void
1095 on_connection_status_changed (TnyAccount *account, 
1096                               TnyConnectionStatus status,
1097                               gpointer user_data)
1098 {
1099         TnyConnectionStatus conn_status;
1100         ConnectAndWaitData *data;
1101                         
1102         /* Ignore if reconnecting or disconnected */
1103         conn_status = tny_account_get_connection_status (account);
1104         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1105             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1106                 return;
1107
1108         /* Remove the handler */
1109         data = (ConnectAndWaitData *) user_data;
1110         g_signal_handler_disconnect (account, data->handler);
1111
1112         /* Quit from wait loop */
1113         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1114 }
1115
1116 static void
1117 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1118                                     gboolean canceled, 
1119                                     GError *err, 
1120                                     gpointer user_data)
1121 {
1122         /* Quit from wait loop */
1123         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1124 }
1125
1126 gboolean 
1127 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1128                                   TnyAccount *account)
1129 {
1130         ConnectAndWaitData *data = NULL;
1131         gboolean device_online;
1132         TnyDevice *device;
1133         TnyConnectionStatus conn_status;
1134         gboolean user_requested;
1135         
1136         device = modest_runtime_get_device();
1137         device_online = tny_device_is_online (device);
1138
1139         /* Whether the connection is user requested or automatically
1140            requested, for example via D-Bus */
1141         user_requested = (parent_window) ? TRUE : FALSE;
1142
1143         /* If there is no account check only the device status */
1144         if (!account) {
1145                 if (device_online)
1146                         return TRUE;
1147                 else
1148                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1149                                                                NULL, user_requested);
1150         }
1151
1152         /* Return if the account is already connected */
1153         conn_status = tny_account_get_connection_status (account);
1154         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1155                 return TRUE;
1156
1157         /* Create the helper */
1158         data = g_slice_new0 (ConnectAndWaitData);
1159         data->mutex = g_mutex_new ();
1160         data->has_callback = FALSE;
1161
1162         /* Connect the device */
1163         if (!device_online) {
1164                 /* Track account connection status changes */
1165                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1166                                                   G_CALLBACK (on_connection_status_changed),
1167                                                   data);
1168                 /* Try to connect the device */
1169                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1170                                                                 NULL, user_requested);
1171
1172                 /* If the device connection failed then exit */
1173                 if (!device_online && data->handler)
1174                         goto frees;
1175         } else {
1176                 /* Force a reconnection of the account */
1177                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1178                                               on_tny_camel_account_set_online_cb, data);
1179         }
1180
1181         /* Wait until the callback is executed */
1182         g_mutex_lock (data->mutex);
1183         if (!data->has_callback) {
1184                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1185                 gdk_threads_leave ();
1186                 g_mutex_unlock (data->mutex);
1187                 g_main_loop_run (data->wait_loop);
1188                 g_mutex_lock (data->mutex);
1189                 gdk_threads_enter ();
1190         }
1191         g_mutex_unlock (data->mutex);
1192
1193  frees:
1194         if (data) {
1195                 if (g_signal_handler_is_connected (account, data->handler))
1196                         g_signal_handler_disconnect (account, data->handler);
1197                 g_mutex_free (data->mutex);
1198                 g_main_loop_unref (data->wait_loop);
1199                 g_slice_free (ConnectAndWaitData, data);
1200         }
1201
1202         conn_status = tny_account_get_connection_status (account);
1203         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1204 }
1205
1206 gboolean 
1207 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1208 {
1209         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1210                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1211                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1212                         /* This must be a maildir account, which does not require a connection: */
1213                         return TRUE;
1214                 }
1215         }
1216
1217         return modest_platform_connect_and_wait (parent_window, account);
1218 }
1219
1220 gboolean 
1221 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1222 {
1223         if (!folder_store)
1224                 return TRUE; /* Maybe it is something local. */
1225                 
1226         gboolean result = TRUE;
1227         if (TNY_IS_FOLDER (folder_store)) {
1228                 /* Get the folder's parent account: */
1229                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1230                 if (account != NULL) {
1231                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1232                         g_object_unref (account);
1233                 }
1234         } else if (TNY_IS_ACCOUNT (folder_store)) {
1235                 /* Use the folder store as an account: */
1236                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1237         }
1238
1239         return result;
1240 }
1241
1242 void
1243 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1244                                  ModestSortDialogType type)
1245 {
1246         GtkWidget *dialog = NULL;
1247
1248         /* Build dialog */
1249         dialog = hildon_sort_dialog_new (parent_window);
1250         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1251                                      GTK_WINDOW (dialog));
1252
1253         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1254                                         "applications_email_sort",
1255                                         modest_maemo_utils_get_osso_context());
1256
1257         /* Fill sort keys */
1258         switch (type) {
1259         case MODEST_SORT_HEADERS:
1260                 launch_sort_headers_dialog (parent_window, 
1261                                             HILDON_SORT_DIALOG(dialog));
1262                 break;
1263         }
1264         
1265         /* Free */
1266         on_destroy_dialog (GTK_DIALOG(dialog));
1267 }
1268
1269
1270 gboolean 
1271 modest_platform_set_update_interval (guint minutes)
1272 {
1273 #ifdef MODEST_HAVE_LIBALARM
1274         
1275         ModestConf *conf = modest_runtime_get_conf ();
1276         if (!conf)
1277                 return FALSE;
1278                 
1279         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1280
1281         /* Delete any existing alarm,
1282          * because we will replace it: */
1283         if (alarm_cookie) {
1284                 if (alarm_event_del(alarm_cookie) != 1)
1285                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1286                 alarm_cookie = 0;
1287                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1288         }
1289         
1290         /* 0 means no updates: */
1291         if (minutes == 0)
1292                 return TRUE;
1293         
1294      
1295         /* Register alarm: */
1296         
1297         /* Set the interval in alarm_event_t structure: */
1298         alarm_event_t *event = g_new0(alarm_event_t, 1);
1299         event->alarm_time = minutes * 60; /* seconds */
1300         
1301         /* Set recurrence every few minutes: */
1302         event->recurrence = minutes;
1303         event->recurrence_count = -1; /* Means infinite */
1304
1305         /* Specify what should happen when the alarm happens:
1306          * It should call this D-Bus method: */
1307          
1308         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1309         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1310         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1311         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1312
1313         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1314          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1315          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1316          * This is why we want to use the Alarm API instead of just g_timeout_add().
1317          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1318          * ALARM_EVENT_CONNECTED will prevent the alarm from being called in case that the device is offline
1319          */
1320         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION | ALARM_EVENT_CONNECTED;
1321         
1322         alarm_cookie = alarm_event_add (event);
1323
1324         /* now, free it */
1325         alarm_event_free (event);
1326         
1327         /* Store the alarm ID in GConf, so we can remove it later:
1328          * This is apparently valid between application instances. */
1329         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1330         
1331         if (!alarm_cookie) {
1332             /* Error */
1333             const alarm_error_t alarm_error = alarmd_get_error ();
1334             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1335             
1336             /* Give people some clue: */
1337             /* The alarm API should have a function for this: */
1338             if (alarm_error == ALARMD_ERROR_DBUS) {
1339                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1340             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1341                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1342             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1343                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1344             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1345                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1346             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1347                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1348             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1349                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1350             }
1351             
1352             return FALSE;
1353         }
1354 #endif /* MODEST_HAVE_LIBALARM */       
1355         return TRUE;
1356 }
1357
1358 void
1359 modest_platform_push_email_notification(void)
1360 {
1361         gboolean play_sound;
1362         ModestWindow *main_window;
1363         gboolean screen_on = TRUE, app_in_foreground;
1364
1365         /* Check whether or not we should play a sound */
1366         play_sound = modest_conf_get_bool (modest_runtime_get_conf (),
1367                                            MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1368                                            NULL);
1369
1370         /* Get the screen status */
1371         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1372         if (main_window)
1373                 screen_on = modest_main_window_screen_is_on (MODEST_MAIN_WINDOW (main_window));
1374
1375         /* Get the window status */
1376         app_in_foreground = hildon_program_get_is_topmost (hildon_program_get_instance ());
1377
1378         /* If the screen is on and the app is in the
1379            foreground we don't show anything */
1380         if (!(screen_on && app_in_foreground)) {
1381                 /* Play a sound */
1382                 if (play_sound)
1383                         hildon_play_system_sound (MODEST_NEW_MAIL_SOUND_FILE);
1384
1385                 /* Activate LED. This must be deactivated by
1386                    modest_platform_remove_new_mail_notifications */
1387 #ifdef MODEST_HAVE_MCE
1388                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1389                                      MCE_SERVICE,
1390                                      MCE_REQUEST_PATH,
1391                                      MCE_REQUEST_IF,
1392                                      MCE_ACTIVATE_LED_PATTERN,
1393                                      NULL,
1394                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1395                                      DBUS_TYPE_INVALID);
1396 #endif
1397         }
1398 }
1399
1400 void 
1401 modest_platform_on_new_headers_received (TnyList *header_list,
1402                                          gboolean show_visual)
1403 {
1404         g_return_if_fail (TNY_IS_LIST(header_list));
1405
1406         if (tny_list_get_length(header_list) == 0) {
1407                 g_warning ("%s: header list is empty", __FUNCTION__);
1408                 return;
1409         }
1410         
1411         if (!show_visual) {
1412                 modest_platform_push_email_notification ();
1413                 /* We do a return here to avoid indentation with an else */
1414                 return;
1415         }
1416
1417 #ifdef MODEST_HAVE_HILDON_NOTIFY
1418         gboolean play_sound;
1419
1420         /* Check whether or not we should play a sound */
1421         play_sound = modest_conf_get_bool (modest_runtime_get_conf (),
1422                                            MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1423                                            NULL);
1424
1425         HildonNotification *notification;
1426         TnyIterator *iter;
1427         GSList *notifications_list = NULL;
1428
1429         /* Get previous notifications ids */
1430         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1431                                                    MODEST_CONF_NOTIFICATION_IDS, 
1432                                                    MODEST_CONF_VALUE_INT, NULL);
1433
1434         iter = tny_list_create_iterator (header_list);
1435         while (!tny_iterator_is_done (iter)) {
1436                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1437                 const gchar *display_date;
1438                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1439                 TnyFolder *folder = tny_header_get_folder (header);
1440                 gboolean first_notification = TRUE;
1441                 gint notif_id;
1442
1443                 /* constant string, don't free */
1444                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1445
1446                 display_address = g_strdup(tny_header_get_from (header));
1447                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1448                 
1449                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1450                 notification = hildon_notification_new (summary,
1451                                                         tny_header_get_subject (header),
1452                                                         "qgn_list_messagin",
1453                                                         "email.arrive");
1454                 /* Create the message URL */
1455                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1456                                        tny_header_get_uid (header));
1457
1458                 hildon_notification_add_dbus_action(notification,
1459                                                     "default",
1460                                                     "Cancel",
1461                                                     MODEST_DBUS_SERVICE,
1462                                                     MODEST_DBUS_OBJECT,
1463                                                     MODEST_DBUS_IFACE,
1464                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1465                                                     G_TYPE_STRING, url,
1466                                                     -1);
1467
1468                 /* Play sound if the user wants. Show the LED
1469                    pattern. Show and play just one */
1470                 if (G_UNLIKELY (first_notification)) {
1471                         first_notification = FALSE;
1472                         if (play_sound)  {
1473                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1474                                                                     "sound-file", MODEST_NEW_MAIL_SOUND_FILE);
1475                         }
1476
1477                         /* Set the led pattern */
1478                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1479                                                             "dialog-type", 4);
1480                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1481                                                             "led-pattern",
1482                                                             MODEST_NEW_MAIL_LIGHTING_PATTERN);                  
1483                 }
1484
1485                 /* Notify. We need to do this in an idle because this function
1486                    could be called from a thread */
1487                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1488
1489                 /* Save id in the list */
1490                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1491                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1492                 /* We don't listen for the "closed" signal, because we
1493                    don't care about if the notification was removed or
1494                    not to store the list in gconf */
1495         
1496                 /* Free & carry on */
1497                 g_free (display_address);
1498                 g_free (summary);
1499                 g_free (url);
1500                 g_object_unref (folder);
1501                 g_object_unref (header);
1502                 tny_iterator_next (iter);
1503         }
1504         g_object_unref (iter);
1505
1506         /* Save the ids */
1507         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1508                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1509
1510         g_slist_free (notifications_list);
1511         
1512 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1513 }
1514
1515 void
1516 modest_platform_remove_new_mail_notifications (gboolean only_visuals) 
1517 {
1518         if (only_visuals) {
1519 #ifdef MODEST_HAVE_MCE
1520                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1521                                      MCE_SERVICE,
1522                                      MCE_REQUEST_PATH,
1523                                      MCE_REQUEST_IF,
1524                                      MCE_DEACTIVATE_LED_PATTERN,
1525                                      NULL,
1526                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1527                                      DBUS_TYPE_INVALID);
1528 #endif
1529                 return;
1530         }
1531
1532 #ifdef MODEST_HAVE_HILDON_NOTIFY
1533         GSList *notif_list = NULL;
1534
1535         /* Get previous notifications ids */
1536         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1537                                            MODEST_CONF_NOTIFICATION_IDS, 
1538                                            MODEST_CONF_VALUE_INT, NULL);
1539
1540         while (notif_list) {
1541                 gint notif_id;
1542                 NotifyNotification *notif;
1543
1544                 /* Nasty HACK to remove the notifications, set the id
1545                    of the existing ones and then close them */
1546                 notif_id = GPOINTER_TO_INT(notif_list->data);
1547                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1548                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1549
1550                 /* Close the notification, note that some ids could be
1551                    already invalid, but we don't care because it does
1552                    not fail */
1553                 notify_notification_close(notif, NULL);
1554                 g_object_unref(notif);
1555
1556                 /* Delete the link, it's like going to the next */
1557                 notif_list = g_slist_delete_link (notif_list, notif_list);
1558         }
1559
1560         /* Save the ids */
1561         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1562                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1563
1564         g_slist_free (notif_list);
1565
1566 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1567 }
1568
1569
1570
1571 GtkWidget * 
1572 modest_platform_get_global_settings_dialog ()
1573 {
1574         return modest_maemo_global_settings_dialog_new ();
1575 }
1576
1577 void
1578 modest_platform_show_help (GtkWindow *parent_window, 
1579                            const gchar *help_id)
1580 {
1581         osso_return_t result;
1582         g_return_if_fail (help_id);
1583
1584         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1585                                    help_id, HILDON_HELP_SHOW_DIALOG);
1586         
1587         if (result != OSSO_OK) {
1588                 gchar *error_msg;
1589                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1590                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1591                                                 NULL,
1592                                                 error_msg);
1593                 g_free (error_msg);
1594         }
1595 }
1596
1597 void 
1598 modest_platform_show_search_messages (GtkWindow *parent_window)
1599 {
1600         osso_return_t result = OSSO_ERROR;
1601         
1602         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1603                                              "osso_global_search",
1604                                              "search_email", NULL, DBUS_TYPE_INVALID);
1605
1606         if (result != OSSO_OK) {
1607                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1608         }
1609 }
1610
1611 void 
1612 modest_platform_show_addressbook (GtkWindow *parent_window)
1613 {
1614         osso_return_t result = OSSO_ERROR;
1615         
1616         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1617                                              "osso_addressbook",
1618                                              "top_application", NULL, DBUS_TYPE_INVALID);
1619
1620         if (result != OSSO_OK) {
1621                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1622         }
1623 }
1624
1625 GtkWidget *
1626 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1627 {
1628         GtkWidget *widget = modest_folder_view_new (query);
1629
1630         /* Show one account by default */
1631         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1632                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1633
1634         /* Restore settings */
1635         modest_widget_memory_restore (modest_runtime_get_conf(), 
1636                                       G_OBJECT (widget),
1637                                       MODEST_CONF_FOLDER_VIEW_KEY);
1638
1639         return widget;
1640 }
1641
1642 void
1643 banner_finish (gpointer data, GObject *object)
1644 {
1645         ModestWindowMgr *mgr = (ModestWindowMgr *) data;
1646         modest_window_mgr_unregister_banner (mgr);
1647         g_object_unref (mgr);
1648 }
1649
1650 void 
1651 modest_platform_information_banner (GtkWidget *parent,
1652                                     const gchar *icon_name,
1653                                     const gchar *text)
1654 {
1655         GtkWidget *banner;
1656         ModestWindowMgr *mgr;
1657
1658         mgr = modest_runtime_get_window_mgr ();
1659         banner = hildon_banner_show_information (parent, icon_name, text);
1660
1661         modest_window_mgr_register_banner (mgr);
1662         g_object_ref (mgr);
1663         g_object_weak_ref ((GObject *) banner, banner_finish, mgr);
1664 }
1665
1666 void
1667 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1668                                                  const gchar *icon_name,
1669                                                  const gchar *text,
1670                                                  gint timeout)
1671 {
1672         GtkWidget *banner;
1673         banner = hildon_banner_show_information (parent, icon_name, text);
1674         hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1675 }
1676
1677 GtkWidget *
1678 modest_platform_animation_banner (GtkWidget *parent,
1679                                   const gchar *animation_name,
1680                                   const gchar *text)
1681 {
1682         GtkWidget *inf_note = NULL;
1683
1684         g_return_val_if_fail (text != NULL, NULL);
1685
1686         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1687
1688         return inf_note;
1689 }
1690
1691 typedef struct
1692 {
1693         GMainLoop* loop;
1694         TnyAccount *account;
1695         gboolean is_online;
1696         gint count_tries;
1697 } CheckAccountIdleData;
1698
1699 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1700
1701 static gboolean 
1702 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1703 {
1704         gboolean stop_trying = FALSE;
1705         g_return_val_if_fail (data && data->account, FALSE);
1706         
1707         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1708                 tny_account_get_connection_status (data->account));     
1709         
1710         if (data && data->account && 
1711                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1712                  * after which the account is likely to be usable, or never likely to be usable soon: */
1713                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1714         {
1715                 data->is_online = TRUE;
1716                 
1717                 stop_trying = TRUE;
1718         } else {
1719                 /* Give up if we have tried too many times: */
1720                 if (data->count_tries >= NUMBER_OF_TRIES) {
1721                         stop_trying = TRUE;
1722                 } else {
1723                         /* Wait for another timeout: */
1724                         ++(data->count_tries);
1725                 }
1726         }
1727         
1728         if (stop_trying) {
1729                 /* Allow the function that requested this idle callback to continue: */
1730                 if (data->loop)
1731                         g_main_loop_quit (data->loop);
1732                         
1733                 if (data->account)
1734                         g_object_unref (data->account);
1735                 
1736                 return FALSE; /* Don't call this again. */
1737         } else {
1738                 return TRUE; /* Call this timeout callback again. */
1739         }
1740 }
1741
1742 /* Return TRUE immediately if the account is already online,
1743  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1744  * soon as the account is online, or FALSE if the account does 
1745  * not become online in the NUMBER_OF_TRIES seconds.
1746  * This is useful when the D-Bus method was run immediately after 
1747  * the application was started (when using D-Bus activation), 
1748  * because the account usually takes a short time to go online.
1749  * The return value is maybe not very useful.
1750  */
1751 gboolean
1752 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1753 {
1754         g_return_val_if_fail (account, FALSE);
1755         
1756         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1757         
1758         if (!tny_device_is_online (modest_runtime_get_device())) {
1759                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1760                 return FALSE;
1761         }
1762         
1763         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1764          * so we avoid wait unnecessarily: */
1765         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1766                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1767                 return TRUE;            
1768         }
1769                 
1770         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1771                 __FUNCTION__, tny_account_get_connection_status (account));
1772         
1773         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1774          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1775          * we want to avoid. */
1776         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1777                 return TRUE;
1778                 
1779         /* This blocks on the result: */
1780         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1781         data->is_online = FALSE;
1782         data->account = account;
1783         g_object_ref (data->account);
1784         data->count_tries = 0;
1785                 
1786         GMainContext *context = NULL; /* g_main_context_new (); */
1787         data->loop = g_main_loop_new (context, FALSE /* not running */);
1788
1789         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1790
1791         /* This main loop will run until the idle handler has stopped it: */
1792         g_main_loop_run (data->loop);
1793
1794         g_main_loop_unref (data->loop);
1795         /* g_main_context_unref (context); */
1796
1797         g_slice_free (CheckAccountIdleData, data);
1798         
1799         return data->is_online; 
1800 }
1801
1802
1803
1804 static void
1805 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1806 {
1807         /* GTK_RESPONSE_HELP means we need to show the certificate */
1808         if (response_id == GTK_RESPONSE_APPLY) {
1809                 GtkWidget *note;
1810                 gchar *msg;
1811                 
1812                 /* Do not close the dialog */
1813                 g_signal_stop_emission_by_name (dialog, "response");
1814
1815                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1816                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1817                 gtk_dialog_run (GTK_DIALOG(note));
1818                 gtk_widget_destroy (note);
1819         }
1820 }
1821
1822
1823 gboolean
1824 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1825                                                      const gchar *certificate)
1826 {
1827         GtkWidget *note;
1828         gint response;
1829         ModestWindow *main_win;
1830         
1831         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1832                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1833                            __FUNCTION__);
1834                 return FALSE;
1835         }
1836
1837         /* don't create it */
1838         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1839         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1840         
1841         
1842         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1843                                            server_name);
1844         
1845         /* We use GTK_RESPONSE_APPLY because we want the button in the
1846            middle of OK and CANCEL the same as the browser does for
1847            example. With GTK_RESPONSE_HELP the view button is aligned
1848            to the left while the other two to the right */
1849         note = hildon_note_new_confirmation_add_buttons  (
1850                 GTK_WINDOW(main_win),
1851                 question,
1852                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1853                 _("mcen_bd_view"),          GTK_RESPONSE_APPLY,   /* abusing this... */
1854                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1855                 NULL, NULL);
1856         
1857         g_signal_connect (G_OBJECT(note), "response", 
1858                           G_CALLBACK(on_cert_dialog_response),
1859                           (gpointer) certificate);
1860         
1861         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1862                                      GTK_WINDOW (note));
1863         response = gtk_dialog_run(GTK_DIALOG(note));
1864
1865         on_destroy_dialog (GTK_DIALOG(note));
1866         g_free (question);
1867         
1868         return response == GTK_RESPONSE_OK;
1869 }
1870
1871 gboolean
1872 modest_platform_run_alert_dialog (const gchar* prompt, 
1873                                   gboolean is_question)
1874 {       
1875         ModestWindow *main_win; 
1876
1877         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1878                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1879                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1880                 return is_question ? FALSE : TRUE;
1881         }
1882
1883         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1884         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1885         
1886         gboolean retval = TRUE;
1887         if (is_question) {
1888                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1889                  * when it is a question.
1890                  * Obviously, we need tinymail to use more specific error codes instead,
1891                  * so we know what buttons to show. */
1892                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1893                                                                               prompt));
1894                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1895                                              GTK_WINDOW (dialog));
1896                 
1897                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1898                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1899                 
1900                 on_destroy_dialog (GTK_DIALOG(dialog));         
1901         } else {
1902                 /* Just show the error text and use the default response: */
1903                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1904                                                         prompt, FALSE);
1905         }
1906         return retval;
1907 }
1908
1909 /***************/
1910 typedef struct {
1911         GtkWindow *parent_window;
1912         ModestConnectedPerformer callback;
1913         TnyAccount *account;
1914         gpointer user_data;
1915         gchar *iap;
1916         TnyDevice *device;
1917 } OnWentOnlineInfo;
1918  
1919 static void 
1920 on_went_online_info_free (OnWentOnlineInfo *info)
1921 {
1922         /* And if we cleanup, we DO cleanup  :-)  */
1923         
1924         if (info->device)
1925                 g_object_unref (info->device);
1926         if (info->iap)
1927                 g_free (info->iap);
1928         if (info->parent_window)
1929                 g_object_unref (info->parent_window);
1930         if (info->account)
1931                 g_object_unref (info->account);
1932         
1933         g_slice_free (OnWentOnlineInfo, info);
1934         
1935         /* We're done ... */
1936         
1937         return;
1938 }
1939  
1940 static void
1941 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1942 {
1943         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1944  
1945         /* Now it's really time to callback to the caller. If going online didn't succeed,
1946          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1947          * canceled will be set. Etcetera etcetera. */
1948         
1949         if (info->callback) {
1950                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1951         }
1952         
1953         /* This is our last call, we must cleanup here if we didn't yet do that */
1954         on_went_online_info_free (info);
1955         
1956         return;
1957 }
1958  
1959  
1960 static void
1961 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1962 {
1963         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1964         info->iap = g_strdup (iap_id);
1965         
1966         if (canceled || err || !info->account) {
1967         
1968                 /* If there's a problem or if there's no account (then that's it for us, we callback
1969                  * the caller's callback now. He'll have to handle err or canceled, of course.
1970                  * We are not really online, as the account is not really online here ... */    
1971                 
1972                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1973                  * this info. We don't cleanup err, Tinymail does that! */
1974                 
1975                 if (info->callback) {
1976                         
1977                         /* info->account can be NULL here, this means that the user did not
1978                          * provide a nice account instance. We'll assume that the user knows
1979                          * what he's doing and is happy with just the device going online. 
1980                          * 
1981                          * We can't do magic, we don't know what account the user wants to
1982                          * see going online. So just the device goes online, end of story */
1983                         
1984                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1985                 }
1986                 
1987         } else if (info->account) {
1988                 
1989                 /* If there's no problem and if we have an account, we'll put the account
1990                  * online too. When done, the callback of bringing the account online
1991                  * will callback the caller's callback. This is the most normal case. */
1992  
1993                 info->device = TNY_DEVICE (g_object_ref (device));
1994                 
1995                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1996                                               on_account_went_online, info);
1997                 
1998                 /* The on_account_went_online cb frees up the info, go look if you
1999                  * don't believe me! (so we return here) */
2000                 
2001                 return;
2002         }
2003         
2004         /* We cleanup if we are not bringing the account online too */
2005         on_went_online_info_free (info);
2006  
2007         return; 
2008 }
2009         
2010 void 
2011 modest_platform_connect_and_perform (GtkWindow *parent_window, 
2012                                      gboolean force,
2013                                      TnyAccount *account, 
2014                                      ModestConnectedPerformer callback, 
2015                                      gpointer user_data)
2016 {
2017         gboolean device_online;
2018         TnyDevice *device;
2019         TnyConnectionStatus conn_status;
2020         OnWentOnlineInfo *info;
2021         
2022         device = modest_runtime_get_device();
2023         device_online = tny_device_is_online (device);
2024
2025         /* If there is no account check only the device status */
2026         if (!account) {
2027                 
2028                 if (device_online) {
2029  
2030                         /* We promise to instantly perform the callback, so ... */
2031                         if (callback) {
2032                                 callback (FALSE, NULL, parent_window, account, user_data);
2033                         }
2034                         
2035                 } else {
2036                         
2037                         info = g_slice_new0 (OnWentOnlineInfo);
2038                         
2039                         info->iap = NULL;
2040                         info->device = NULL;
2041                         info->account = NULL;
2042                 
2043                         if (parent_window)
2044                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2045                         else
2046                                 info->parent_window = NULL;
2047                         info->user_data = user_data;
2048                         info->callback = callback;
2049                 
2050                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2051                                                               force, on_conic_device_went_online, 
2052                                                               info);
2053  
2054                         /* We'll cleanup in on_conic_device_went_online */
2055                 }
2056  
2057                 /* The other code has no more reason to run. This is all that we can do for the
2058                  * caller (he should have given us a nice and clean account instance!). We
2059                  * can't do magic, we don't know what account he intends to bring online. So
2060                  * we'll just bring the device online (and await his false bug report). */
2061                 
2062                 return;
2063         }
2064  
2065         
2066         /* Return if the account is already connected */
2067         
2068         conn_status = tny_account_get_connection_status (account);
2069         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
2070  
2071                 /* We promise to instantly perform the callback, so ... */
2072                 if (callback) {
2073                         callback (FALSE, NULL, parent_window, account, user_data);
2074                 }
2075                 
2076                 return;
2077         }
2078         
2079         /* Else, we are in a state that requires that we go online before we
2080          * call the caller's callback. */
2081         
2082         info = g_slice_new0 (OnWentOnlineInfo);
2083         
2084         info->device = NULL;
2085         info->iap = NULL;
2086         info->account = TNY_ACCOUNT (g_object_ref (account));
2087         
2088         if (parent_window)
2089                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2090         else
2091                 info->parent_window = NULL;
2092         
2093         /* So we'll put the callback away for later ... */
2094         
2095         info->user_data = user_data;
2096         info->callback = callback;
2097         
2098         if (!device_online) {
2099  
2100                 /* If also the device is offline, then we connect both the device 
2101                  * and the account */
2102                 
2103                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2104                                                       force, on_conic_device_went_online, 
2105                                                       info);
2106                 
2107         } else {
2108                 
2109                 /* If the device is online, we'll just connect the account */
2110                 
2111                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
2112                                               on_account_went_online, info);
2113         }
2114  
2115         /* The info gets freed by on_account_went_online or on_conic_device_went_online
2116          * in both situations, go look if you don't believe me! */
2117         
2118         return;
2119 }
2120
2121 void
2122 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
2123                                                gboolean force,
2124                                                TnyFolderStore *folder_store, 
2125                                                ModestConnectedPerformer callback, 
2126                                                gpointer user_data)
2127 {
2128         TnyAccount *account = NULL;
2129         
2130         if (!folder_store) {
2131                 /* We promise to instantly perform the callback, so ... */
2132                 if (callback) {
2133                         callback (FALSE, NULL, parent_window, NULL, user_data);
2134                 }
2135                 return; 
2136                 
2137                 /* Original comment: Maybe it is something local. */
2138                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
2139                 
2140         } else if (TNY_IS_FOLDER (folder_store)) {
2141                 /* Get the folder's parent account: */
2142                 account = tny_folder_get_account(TNY_FOLDER (folder_store));
2143         } else if (TNY_IS_ACCOUNT (folder_store)) {
2144                 /* Use the folder store as an account: */
2145                 account = TNY_ACCOUNT (folder_store);
2146         }
2147  
2148         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2149                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2150                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2151                         
2152                         /* This IS a local account like a maildir account, which does not require 
2153                          * a connection. (original comment had a vague assumption in its language
2154                          * usage. There's no assuming needed, this IS what it IS: a local account), */
2155  
2156                         /* We promise to instantly perform the callback, so ... */
2157                         if (callback) {
2158                                 callback (FALSE, NULL, parent_window, account, user_data);
2159                         }
2160                         
2161                         return;
2162                 }
2163         }
2164  
2165         modest_platform_connect_and_perform (parent_window, force, account, callback, user_data);
2166  
2167         return;
2168 }
2169
2170 static void
2171 src_account_connect_performer (gboolean canceled, 
2172                                GError *err,
2173                                GtkWindow *parent_window, 
2174                                TnyAccount *src_account, 
2175                                gpointer user_data)
2176 {
2177         DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data;
2178
2179         if (canceled || err) {
2180                 /* If there was any error call the user callback */
2181                 info->callback (canceled, err, parent_window, src_account, info->data);
2182         } else {
2183                 /* Connect the destination account */
2184                 modest_platform_connect_if_remote_and_perform (parent_window, TRUE, 
2185                                                                TNY_FOLDER_STORE (info->dst_account),
2186                                                                info->callback, info->data);
2187         }
2188
2189         /* Free the info object */
2190         g_object_unref (info->dst_account);
2191         g_slice_free (DoubleConnectionInfo, info);
2192 }
2193
2194
2195 void 
2196 modest_platform_double_connect_and_perform (GtkWindow *parent_window, 
2197                                             gboolean force,
2198                                             TnyFolderStore *folder_store,
2199                                             DoubleConnectionInfo *connect_info)
2200 {
2201         modest_platform_connect_if_remote_and_perform(parent_window, 
2202                                                       force,
2203                                                       folder_store, 
2204                                                       src_account_connect_performer, 
2205                                                       connect_info);
2206 }
2207
2208 GtkWidget *
2209 modest_platform_get_account_settings_dialog (ModestAccountSettings *settings)
2210 {
2211         ModestAccountSettingsDialog *dialog = modest_account_settings_dialog_new ();
2212
2213         modest_account_settings_dialog_set_account (dialog, settings);
2214         return GTK_WIDGET (dialog);
2215 }
2216
2217 GtkWidget *
2218 modest_platform_get_account_settings_wizard ()
2219 {
2220         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2221
2222         return GTK_WIDGET (dialog);
2223 }
2224
2225 ModestConnectedVia
2226 modest_platform_get_current_connection (void)
2227 {
2228         TnyDevice *device = NULL;
2229         ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY;
2230         
2231         device = modest_runtime_get_device ();
2232
2233         if (!tny_device_is_online (device))
2234                 return MODEST_CONNECTED_VIA_ANY;
2235
2236 #ifdef MODEST_HAVE_CONIC
2237         /* Get iap id */
2238         const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
2239         if (iap_id) {
2240                 ConIcIap *iap = tny_maemo_conic_device_get_iap (
2241                         TNY_MAEMO_CONIC_DEVICE (device), iap_id);
2242                 const gchar *bearer_type = con_ic_iap_get_bearer_type (iap);
2243                 if (bearer_type) {
2244                         if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) ||
2245                             !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) ||
2246                             !strcmp (bearer_type, "WIMAX")) {
2247                                 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX;
2248                         } else {
2249                                 retval = MODEST_CONNECTED_VIA_ANY;
2250                         }
2251                 }       
2252                 g_object_unref (iap);
2253         }
2254 #else
2255         retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */  
2256 #endif /* MODEST_HAVE_CONIC */
2257         return retval;
2258 }