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