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