9127de660b1fd3a6f9ef649f08a7ca43bedb7502
[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_confirmation_dialog_with_buttons (GtkWindow *parent_window,
990                                                       const gchar *message,
991                                                       const gchar *button_accept,
992                                                       const gchar *button_cancel)
993 {
994         GtkWidget *dialog;
995         gint response;
996         
997         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
998                                                            button_accept, GTK_RESPONSE_ACCEPT,
999                                                            button_cancel, GTK_RESPONSE_CANCEL,
1000                                                            NULL);
1001         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1002                                      GTK_WINDOW (dialog));
1003
1004         response = gtk_dialog_run (GTK_DIALOG (dialog));
1005
1006         on_destroy_dialog (GTK_DIALOG(dialog));
1007         
1008         while (gtk_events_pending ())
1009                 gtk_main_iteration ();
1010
1011         return response;
1012 }
1013         
1014 gint
1015 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
1016                                    const gchar *message)
1017 {
1018         GtkWidget *dialog;
1019         gint response;
1020         
1021         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1022                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
1023                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
1024                                                            NULL);
1025         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
1026         response = gtk_dialog_run (GTK_DIALOG (dialog));
1027         
1028         on_destroy_dialog (GTK_DIALOG(dialog));
1029
1030         while (gtk_events_pending ())
1031                 gtk_main_iteration ();
1032
1033         return response;
1034 }
1035
1036
1037
1038 void
1039 modest_platform_run_information_dialog (GtkWindow *parent_window,
1040                                         const gchar *message)
1041 {
1042         GtkWidget *note;
1043         
1044         note = hildon_note_new_information (parent_window, message);
1045         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1046                                      GTK_WINDOW (note));
1047         
1048         g_signal_connect_swapped (note,
1049                                   "response", 
1050                                   G_CALLBACK (on_destroy_dialog),
1051                                   note);
1052
1053         gtk_widget_show_all (note);
1054 }
1055
1056
1057
1058 typedef struct _ConnectAndWaitData {
1059         GMutex *mutex;
1060         GMainLoop *wait_loop;
1061         gboolean has_callback;
1062         gulong handler;
1063 } ConnectAndWaitData;
1064
1065
1066 static void
1067 quit_wait_loop (TnyAccount *account,
1068                 ConnectAndWaitData *data) 
1069 {
1070         /* Set the has_callback to TRUE (means that the callback was
1071            executed and wake up every code waiting for cond to be
1072            TRUE */
1073         g_mutex_lock (data->mutex);
1074         data->has_callback = TRUE;
1075         if (data->wait_loop)
1076                 g_main_loop_quit (data->wait_loop);
1077         g_mutex_unlock (data->mutex);
1078 }
1079
1080 static void
1081 on_connection_status_changed (TnyAccount *account, 
1082                               TnyConnectionStatus status,
1083                               gpointer user_data)
1084 {
1085         TnyConnectionStatus conn_status;
1086         ConnectAndWaitData *data;
1087                         
1088         /* Ignore if reconnecting or disconnected */
1089         conn_status = tny_account_get_connection_status (account);
1090         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1091             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1092                 return;
1093
1094         /* Remove the handler */
1095         data = (ConnectAndWaitData *) user_data;
1096         g_signal_handler_disconnect (account, data->handler);
1097
1098         /* Quit from wait loop */
1099         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1100 }
1101
1102 static void
1103 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1104                                     gboolean canceled, 
1105                                     GError *err, 
1106                                     gpointer user_data)
1107 {
1108         /* Quit from wait loop */
1109         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1110 }
1111
1112 gboolean 
1113 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1114                                   TnyAccount *account)
1115 {
1116         ConnectAndWaitData *data = NULL;
1117         gboolean device_online;
1118         TnyDevice *device;
1119         TnyConnectionStatus conn_status;
1120         gboolean user_requested;
1121         
1122         device = modest_runtime_get_device();
1123         device_online = tny_device_is_online (device);
1124
1125         /* Whether the connection is user requested or automatically
1126            requested, for example via D-Bus */
1127         user_requested = (parent_window) ? TRUE : FALSE;
1128
1129         /* If there is no account check only the device status */
1130         if (!account) {
1131                 if (device_online)
1132                         return TRUE;
1133                 else
1134                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1135                                                                NULL, user_requested);
1136         }
1137
1138         /* Return if the account is already connected */
1139         conn_status = tny_account_get_connection_status (account);
1140         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1141                 return TRUE;
1142
1143         /* Create the helper */
1144         data = g_slice_new0 (ConnectAndWaitData);
1145         data->mutex = g_mutex_new ();
1146         data->has_callback = FALSE;
1147
1148         /* Connect the device */
1149         if (!device_online) {
1150                 /* Track account connection status changes */
1151                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1152                                                   G_CALLBACK (on_connection_status_changed),
1153                                                   data);
1154                 /* Try to connect the device */
1155                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1156                                                                 NULL, user_requested);
1157
1158                 /* If the device connection failed then exit */
1159                 if (!device_online && data->handler)
1160                         goto frees;
1161         } else {
1162                 /* Force a reconnection of the account */
1163                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1164                                               on_tny_camel_account_set_online_cb, data);
1165         }
1166
1167         /* Wait until the callback is executed */
1168         g_mutex_lock (data->mutex);
1169         if (!data->has_callback) {
1170                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1171                 gdk_threads_leave ();
1172                 g_mutex_unlock (data->mutex);
1173                 g_main_loop_run (data->wait_loop);
1174                 g_mutex_lock (data->mutex);
1175                 gdk_threads_enter ();
1176         }
1177         g_mutex_unlock (data->mutex);
1178
1179  frees:
1180         if (data) {
1181                 if (g_signal_handler_is_connected (account, data->handler))
1182                         g_signal_handler_disconnect (account, data->handler);
1183                 g_mutex_free (data->mutex);
1184                 g_main_loop_unref (data->wait_loop);
1185                 g_slice_free (ConnectAndWaitData, data);
1186         }
1187
1188         conn_status = tny_account_get_connection_status (account);
1189         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1190 }
1191
1192 gboolean 
1193 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1194 {
1195         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1196                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1197                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1198                         /* This must be a maildir account, which does not require a connection: */
1199                         return TRUE;
1200                 }
1201         }
1202
1203         return modest_platform_connect_and_wait (parent_window, account);
1204 }
1205
1206 gboolean 
1207 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1208 {
1209         if (!folder_store)
1210                 return TRUE; /* Maybe it is something local. */
1211                 
1212         gboolean result = TRUE;
1213         if (TNY_IS_FOLDER (folder_store)) {
1214                 /* Get the folder's parent account: */
1215                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1216                 if (account != NULL) {
1217                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1218                         g_object_unref (account);
1219                 }
1220         } else if (TNY_IS_ACCOUNT (folder_store)) {
1221                 /* Use the folder store as an account: */
1222                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1223         }
1224
1225         return result;
1226 }
1227
1228 void
1229 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1230                                  ModestSortDialogType type)
1231 {
1232         GtkWidget *dialog = NULL;
1233
1234         /* Build dialog */
1235         dialog = hildon_sort_dialog_new (parent_window);
1236         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1237                                      GTK_WINDOW (dialog));
1238
1239         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1240                                         "applications_email_sort",
1241                                         modest_maemo_utils_get_osso_context());
1242
1243         /* Fill sort keys */
1244         switch (type) {
1245         case MODEST_SORT_HEADERS:
1246                 launch_sort_headers_dialog (parent_window, 
1247                                             HILDON_SORT_DIALOG(dialog));
1248                 break;
1249         }
1250         
1251         /* Free */
1252         on_destroy_dialog (GTK_DIALOG(dialog));
1253 }
1254
1255
1256 gboolean 
1257 modest_platform_set_update_interval (guint minutes)
1258 {
1259 #ifdef MODEST_HAVE_LIBALARM
1260         
1261         ModestConf *conf = modest_runtime_get_conf ();
1262         if (!conf)
1263                 return FALSE;
1264                 
1265         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1266
1267         /* Delete any existing alarm,
1268          * because we will replace it: */
1269         if (alarm_cookie) {
1270                 if (alarm_event_del(alarm_cookie) != 1)
1271                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1272                 alarm_cookie = 0;
1273                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1274         }
1275         
1276         /* 0 means no updates: */
1277         if (minutes == 0)
1278                 return TRUE;
1279         
1280      
1281         /* Register alarm: */
1282         
1283         /* Set the interval in alarm_event_t structure: */
1284         alarm_event_t *event = g_new0(alarm_event_t, 1);
1285         event->alarm_time = minutes * 60; /* seconds */
1286         
1287         /* Set recurrence every few minutes: */
1288         event->recurrence = minutes;
1289         event->recurrence_count = -1; /* Means infinite */
1290
1291         /* Specify what should happen when the alarm happens:
1292          * It should call this D-Bus method: */
1293          
1294         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1295         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1296         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1297         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1298
1299         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1300          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1301          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1302          * This is why we want to use the Alarm API instead of just g_timeout_add().
1303          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1304          */
1305         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1306         
1307         alarm_cookie = alarm_event_add (event);
1308
1309         /* now, free it */
1310         alarm_event_free (event);
1311         
1312         /* Store the alarm ID in GConf, so we can remove it later:
1313          * This is apparently valid between application instances. */
1314         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1315         
1316         if (!alarm_cookie) {
1317             /* Error */
1318             const alarm_error_t alarm_error = alarmd_get_error ();
1319             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1320             
1321             /* Give people some clue: */
1322             /* The alarm API should have a function for this: */
1323             if (alarm_error == ALARMD_ERROR_DBUS) {
1324                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1325             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1326                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1327             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1328                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1329             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1330                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1331             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1332                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1333             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1334                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1335             }
1336             
1337             return FALSE;
1338         }
1339 #endif /* MODEST_HAVE_LIBALARM */       
1340         return TRUE;
1341 }
1342
1343 void 
1344 modest_platform_on_new_headers_received (TnyList *header_list) 
1345 {
1346 #ifdef MODEST_HAVE_HILDON_NOTIFY
1347         HildonNotification *notification;
1348         TnyIterator *iter;
1349         GSList *notifications_list = NULL;
1350
1351         /* Get previous notifications ids */
1352         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1353                                                    MODEST_CONF_NOTIFICATION_IDS, 
1354                                                    MODEST_CONF_VALUE_INT, NULL);
1355
1356         iter = tny_list_create_iterator (header_list);
1357         while (!tny_iterator_is_done (iter)) {
1358                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1359                 const gchar *display_date;
1360                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1361                 TnyFolder *folder = tny_header_get_folder (header);
1362                 gboolean first_notification = TRUE;
1363                 gint notif_id;
1364
1365                 /* constant string, don't free */
1366                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1367
1368                 display_address = g_strdup(tny_header_get_from (header));
1369                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1370                 
1371                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1372                 notification = hildon_notification_new (summary,
1373                                                         tny_header_get_subject (header),
1374                                                         "qgn_list_messagin",
1375                                                         "email.arrive");
1376                 /* Create the message URL */
1377                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1378                                        tny_header_get_uid (header));
1379
1380                 hildon_notification_add_dbus_action(notification,
1381                                                     "default",
1382                                                     "Cancel",
1383                                                     MODEST_DBUS_SERVICE,
1384                                                     MODEST_DBUS_OBJECT,
1385                                                     MODEST_DBUS_IFACE,
1386                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1387                                                     G_TYPE_STRING, url,
1388                                                     -1);
1389
1390                 /* Play sound if the user wants. Show the LED
1391                    pattern. Show and play just one */
1392                 if (G_UNLIKELY (first_notification)) {
1393                         first_notification = FALSE;
1394                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1395                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1396                                                   NULL))  {
1397                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1398                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1399                         }
1400
1401                         /* Set the led pattern */
1402                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1403                                                             "dialog-type", 4);
1404                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1405                                                             "led-pattern",
1406                                                             "PatternCommunicationEmail");                       
1407                 }
1408
1409                 /* Notify. We need to do this in an idle because this function
1410                    could be called from a thread */
1411                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1412
1413                 /* Save id in the list */
1414                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1415                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1416                 /* We don't listen for the "closed" signal, because we
1417                    don't care about if the notification was removed or
1418                    not to store the list in gconf */
1419         
1420                 /* Free & carry on */
1421                 g_free (display_address);
1422                 g_free (summary);
1423                 g_free (url);
1424                 g_object_unref (folder);
1425                 g_object_unref (header);
1426                 tny_iterator_next (iter);
1427         }
1428         g_object_unref (iter);
1429
1430         /* Save the ids */
1431         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1432                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1433
1434         g_slist_free (notifications_list);
1435         
1436 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1437 }
1438
1439 void
1440 modest_platform_remove_new_mail_notifications (void) 
1441 {
1442 #ifdef MODEST_HAVE_HILDON_NOTIFY
1443         GSList *notif_list = NULL;
1444
1445         /* Get previous notifications ids */
1446         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1447                                            MODEST_CONF_NOTIFICATION_IDS, 
1448                                            MODEST_CONF_VALUE_INT, NULL);
1449
1450         while (notif_list) {
1451                 gint notif_id;
1452                 NotifyNotification *notif;
1453
1454                 /* Nasty HACK to remove the notifications, set the id
1455                    of the existing ones and then close them */
1456                 notif_id = GPOINTER_TO_INT(notif_list->data);
1457                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1458                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1459
1460                 /* Close the notification, note that some ids could be
1461                    already invalid, but we don't care because it does
1462                    not fail */
1463                 notify_notification_close(notif, NULL);
1464                 g_object_unref(notif);
1465
1466                 /* Delete the link, it's like going to the next */
1467                 notif_list = g_slist_delete_link (notif_list, notif_list);
1468         }
1469
1470         /* Save the ids */
1471         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1472                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1473
1474         g_slist_free (notif_list);
1475
1476 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1477 }
1478
1479
1480
1481 GtkWidget * 
1482 modest_platform_get_global_settings_dialog ()
1483 {
1484         return modest_maemo_global_settings_dialog_new ();
1485 }
1486
1487 void
1488 modest_platform_show_help (GtkWindow *parent_window, 
1489                            const gchar *help_id)
1490 {
1491         osso_return_t result;
1492         g_return_if_fail (help_id);
1493
1494         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1495                                    help_id, HILDON_HELP_SHOW_DIALOG);
1496         
1497         if (result != OSSO_OK) {
1498                 gchar *error_msg;
1499                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1500                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1501                                                 NULL,
1502                                                 error_msg);
1503                 g_free (error_msg);
1504         }
1505 }
1506
1507 void 
1508 modest_platform_show_search_messages (GtkWindow *parent_window)
1509 {
1510         osso_return_t result = OSSO_ERROR;
1511         
1512         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1513                                              "osso_global_search",
1514                                              "search_email", NULL, DBUS_TYPE_INVALID);
1515
1516         if (result != OSSO_OK) {
1517                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1518         }
1519 }
1520
1521 void 
1522 modest_platform_show_addressbook (GtkWindow *parent_window)
1523 {
1524         osso_return_t result = OSSO_ERROR;
1525         
1526         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1527                                              "osso_addressbook",
1528                                              "top_application", NULL, DBUS_TYPE_INVALID);
1529
1530         if (result != OSSO_OK) {
1531                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1532         }
1533 }
1534
1535 GtkWidget *
1536 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1537 {
1538         GtkWidget *widget = modest_folder_view_new (query);
1539
1540         /* Show one account by default */
1541         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1542                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1543
1544
1545         /* Restore settings */
1546         modest_widget_memory_restore (modest_runtime_get_conf(), 
1547                                       G_OBJECT (widget),
1548                                       MODEST_CONF_FOLDER_VIEW_KEY);
1549
1550         return widget;
1551 }
1552
1553 void 
1554 modest_platform_information_banner (GtkWidget *parent,
1555                                     const gchar *icon_name,
1556                                     const gchar *text)
1557 {
1558         hildon_banner_show_information (parent, icon_name, text);
1559 }
1560
1561 GtkWidget *
1562 modest_platform_animation_banner (GtkWidget *parent,
1563                                   const gchar *animation_name,
1564                                   const gchar *text)
1565 {
1566         GtkWidget *inf_note = NULL;
1567
1568         g_return_val_if_fail (text != NULL, NULL);
1569
1570         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1571
1572         return inf_note;
1573 }
1574
1575 typedef struct
1576 {
1577         GMainLoop* loop;
1578         TnyAccount *account;
1579         gboolean is_online;
1580         gint count_tries;
1581 } CheckAccountIdleData;
1582
1583 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1584
1585 static gboolean 
1586 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1587 {
1588         gboolean stop_trying = FALSE;
1589         g_return_val_if_fail (data && data->account, FALSE);
1590         
1591         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1592                 tny_account_get_connection_status (data->account));     
1593         
1594         if (data && data->account && 
1595                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1596                  * after which the account is likely to be usable, or never likely to be usable soon: */
1597                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1598         {
1599                 data->is_online = TRUE;
1600                 
1601                 stop_trying = TRUE;
1602         } else {
1603                 /* Give up if we have tried too many times: */
1604                 if (data->count_tries >= NUMBER_OF_TRIES) {
1605                         stop_trying = TRUE;
1606                 } else {
1607                         /* Wait for another timeout: */
1608                         ++(data->count_tries);
1609                 }
1610         }
1611         
1612         if (stop_trying) {
1613                 /* Allow the function that requested this idle callback to continue: */
1614                 if (data->loop)
1615                         g_main_loop_quit (data->loop);
1616                         
1617                 if (data->account)
1618                         g_object_unref (data->account);
1619                 
1620                 return FALSE; /* Don't call this again. */
1621         } else {
1622                 return TRUE; /* Call this timeout callback again. */
1623         }
1624 }
1625
1626 /* Return TRUE immediately if the account is already online,
1627  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1628  * soon as the account is online, or FALSE if the account does 
1629  * not become online in the NUMBER_OF_TRIES seconds.
1630  * This is useful when the D-Bus method was run immediately after 
1631  * the application was started (when using D-Bus activation), 
1632  * because the account usually takes a short time to go online.
1633  * The return value is maybe not very useful.
1634  */
1635 gboolean
1636 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1637 {
1638         g_return_val_if_fail (account, FALSE);
1639         
1640         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1641         
1642         if (!tny_device_is_online (modest_runtime_get_device())) {
1643                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1644                 return FALSE;
1645         }
1646         
1647         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1648          * so we avoid wait unnecessarily: */
1649         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1650                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1651                 return TRUE;            
1652         }
1653                 
1654         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1655                 __FUNCTION__, tny_account_get_connection_status (account));
1656         
1657         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1658          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1659          * we want to avoid. */
1660         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1661                 return TRUE;
1662                 
1663         /* This blocks on the result: */
1664         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1665         data->is_online = FALSE;
1666         data->account = account;
1667         g_object_ref (data->account);
1668         data->count_tries = 0;
1669                 
1670         GMainContext *context = NULL; /* g_main_context_new (); */
1671         data->loop = g_main_loop_new (context, FALSE /* not running */);
1672
1673         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1674
1675         /* This main loop will run until the idle handler has stopped it: */
1676         g_main_loop_run (data->loop);
1677
1678         g_main_loop_unref (data->loop);
1679         /* g_main_context_unref (context); */
1680
1681         g_slice_free (CheckAccountIdleData, data);
1682         
1683         return data->is_online; 
1684 }
1685
1686
1687
1688 static void
1689 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1690 {
1691         /* GTK_RESPONSE_HELP means we need to show the certificate */
1692         if (response_id == GTK_RESPONSE_HELP) {
1693                 GtkWidget *note;
1694                 gchar *msg;
1695                 
1696                 /* Do not close the dialog */
1697                 g_signal_stop_emission_by_name (dialog, "response");
1698
1699                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1700                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1701                 gtk_dialog_run (GTK_DIALOG(note));
1702                 gtk_widget_destroy (note);
1703         }
1704 }
1705
1706
1707 gboolean
1708 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1709                                                      const gchar *certificate)
1710 {
1711         GtkWidget *note;
1712         gint response;
1713         ModestWindow *main_win;
1714         
1715         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1716                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1717                            __FUNCTION__);
1718                 return FALSE;
1719         }
1720
1721         /* don't create it */
1722         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1723         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1724         
1725         
1726         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1727                                            server_name);
1728         
1729         note = hildon_note_new_confirmation_add_buttons  (
1730                 GTK_WINDOW(main_win),
1731                 question,
1732                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1733                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1734                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1735                 NULL, NULL);
1736         
1737         g_signal_connect (G_OBJECT(note), "response", 
1738                           G_CALLBACK(on_cert_dialog_response),
1739                           (gpointer) certificate);
1740         
1741         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1742                                      GTK_WINDOW (note));
1743         response = gtk_dialog_run(GTK_DIALOG(note));
1744
1745         on_destroy_dialog (GTK_DIALOG(note));
1746         g_free (question);
1747         
1748         return response == GTK_RESPONSE_OK;
1749 }
1750
1751 gboolean
1752 modest_platform_run_alert_dialog (const gchar* prompt, 
1753                                   gboolean is_question)
1754 {       
1755         ModestWindow *main_win; 
1756
1757         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1758                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1759                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1760                 return is_question ? FALSE : TRUE;
1761         }
1762
1763         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1764         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1765         
1766         gboolean retval = TRUE;
1767         if (is_question) {
1768                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1769                  * when it is a question.
1770                  * Obviously, we need tinymail to use more specific error codes instead,
1771                  * so we know what buttons to show. */
1772                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1773                                                                               prompt));
1774                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1775                                              GTK_WINDOW (dialog));
1776                 
1777                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1778                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1779                 
1780                 on_destroy_dialog (GTK_DIALOG(dialog));         
1781         } else {
1782                 /* Just show the error text and use the default response: */
1783                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1784                                                         prompt);
1785         }
1786         return retval;
1787 }
1788
1789 /***************/
1790 typedef struct {
1791         GtkWindow *parent_window;
1792         ModestConnectedPerformer callback;
1793         TnyAccount *account;
1794         gpointer user_data;
1795         gchar *iap;
1796         TnyDevice *device;
1797 } OnWentOnlineInfo;
1798  
1799 static void 
1800 on_went_online_info_free (OnWentOnlineInfo *info)
1801 {
1802         /* And if we cleanup, we DO cleanup  :-)  */
1803         
1804         if (info->device)
1805                 g_object_unref (info->device);
1806         if (info->iap)
1807                 g_free (info->iap);
1808         if (info->parent_window)
1809                 g_object_unref (info->parent_window);
1810         if (info->account)
1811                 g_object_unref (info->account);
1812         
1813         g_slice_free (OnWentOnlineInfo, info);
1814         
1815         /* We're done ... */
1816         
1817         return;
1818 }
1819  
1820 static void
1821 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1822 {
1823         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1824  
1825         /* Now it's really time to callback to the caller. If going online didn't succeed,
1826          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1827          * canceled will be set. Etcetera etcetera. */
1828         
1829         if (info->callback) {
1830                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1831         }
1832         
1833         /* This is our last call, we must cleanup here if we didn't yet do that */
1834         on_went_online_info_free (info);
1835         
1836         return;
1837 }
1838  
1839  
1840 static void
1841 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1842 {
1843         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1844         info->iap = g_strdup (iap_id);
1845         
1846         if (canceled || err || !info->account) {
1847         
1848                 /* If there's a problem or if there's no account (then that's it for us, we callback
1849                  * the caller's callback now. He'll have to handle err or canceled, of course.
1850                  * We are not really online, as the account is not really online here ... */    
1851                 
1852                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1853                  * this info. We don't cleanup err, Tinymail does that! */
1854                 
1855                 if (info->callback) {
1856                         
1857                         /* info->account can be NULL here, this means that the user did not
1858                          * provide a nice account instance. We'll assume that the user knows
1859                          * what he's doing and is happy with just the device going online. 
1860                          * 
1861                          * We can't do magic, we don't know what account the user wants to
1862                          * see going online. So just the device goes online, end of story */
1863                         
1864                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1865                 }
1866                 
1867         } else if (info->account) {
1868                 
1869                 /* If there's no problem and if we have an account, we'll put the account
1870                  * online too. When done, the callback of bringing the account online
1871                  * will callback the caller's callback. This is the most normal case. */
1872  
1873                 info->device = TNY_DEVICE (g_object_ref (device));
1874                 
1875                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1876                                               on_account_went_online, info);
1877                 
1878                 /* The on_account_went_online cb frees up the info, go look if you
1879                  * don't believe me! (so we return here) */
1880                 
1881                 return;
1882         }
1883         
1884         /* We cleanup if we are not bringing the account online too */
1885         on_went_online_info_free (info);
1886  
1887         return; 
1888 }
1889         
1890 void 
1891 modest_platform_connect_and_perform (GtkWindow *parent_window, 
1892                                      TnyAccount *account, 
1893                                      ModestConnectedPerformer callback, 
1894                                      gpointer user_data)
1895 {
1896         gboolean device_online;
1897         TnyDevice *device;
1898         TnyConnectionStatus conn_status;
1899         OnWentOnlineInfo *info;
1900         gboolean user_requested;
1901         
1902         device = modest_runtime_get_device();
1903         device_online = tny_device_is_online (device);
1904
1905         /* Whether the connection is user requested or automatically
1906            requested, for example via D-Bus */
1907         user_requested = (parent_window) ? TRUE : FALSE;
1908
1909         /* If there is no account check only the device status */
1910         if (!account) {
1911                 
1912                 if (device_online) {
1913  
1914                         /* We promise to instantly perform the callback, so ... */
1915                         if (callback) {
1916                                 callback (FALSE, NULL, parent_window, account, user_data);
1917                         }
1918                         
1919                 } else {
1920                         
1921                         info = g_slice_new0 (OnWentOnlineInfo);
1922                         
1923                         info->iap = NULL;
1924                         info->device = NULL;
1925                         info->account = NULL;
1926                 
1927                         if (parent_window)
1928                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1929                         else
1930                                 info->parent_window = NULL;
1931                         info->user_data = user_data;
1932                         info->callback = callback;
1933                 
1934                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1935                                                               user_requested, on_conic_device_went_online, 
1936                                                               info);
1937  
1938                         /* We'll cleanup in on_conic_device_went_online */
1939                 }
1940  
1941                 /* The other code has no more reason to run. This is all that we can do for the
1942                  * caller (he should have given us a nice and clean account instance!). We
1943                  * can't do magic, we don't know what account he intends to bring online. So
1944                  * we'll just bring the device online (and await his false bug report). */
1945                 
1946                 return;
1947         }
1948  
1949         
1950         /* Return if the account is already connected */
1951         
1952         conn_status = tny_account_get_connection_status (account);
1953         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1954  
1955                 /* We promise to instantly perform the callback, so ... */
1956                 if (callback) {
1957                         callback (FALSE, NULL, parent_window, account, user_data);
1958                 }
1959                 
1960                 return;
1961         }
1962         
1963         /* Else, we are in a state that requires that we go online before we
1964          * call the caller's callback. */
1965         
1966         info = g_slice_new0 (OnWentOnlineInfo);
1967         
1968         info->device = NULL;
1969         info->iap = NULL;
1970         info->account = TNY_ACCOUNT (g_object_ref (account));
1971         
1972         if (parent_window)
1973                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1974         else
1975                 info->parent_window = NULL;
1976         
1977         /* So we'll put the callback away for later ... */
1978         
1979         info->user_data = user_data;
1980         info->callback = callback;
1981         
1982         if (!device_online) {
1983  
1984                 /* If also the device is offline, then we connect both the device 
1985                  * and the account */
1986                 
1987                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1988                                                       user_requested, on_conic_device_went_online, 
1989                                                       info);
1990                 
1991         } else {
1992                 
1993                 /* If the device is online, we'll just connect the account */
1994                 
1995                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1996                                               on_account_went_online, info);
1997         }
1998  
1999         /* The info gets freed by on_account_went_online or on_conic_device_went_online
2000          * in both situations, go look if you don't believe me! */
2001         
2002         return;
2003 }
2004
2005 void
2006 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
2007                                                             TnyFolderStore *folder_store, 
2008                                                             ModestConnectedPerformer callback, 
2009                                                             gpointer user_data)
2010 {
2011         TnyAccount *account = NULL;
2012         
2013         if (!folder_store) {
2014                 /* We promise to instantly perform the callback, so ... */
2015                 if (callback) {
2016                         callback (FALSE, NULL, parent_window, NULL, user_data);
2017                 }
2018                 return; 
2019                 
2020                 /* Original comment: Maybe it is something local. */
2021                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
2022                 
2023         } else if (TNY_IS_FOLDER (folder_store)) {
2024                 /* Get the folder's parent account: */
2025                 account = tny_folder_get_account(TNY_FOLDER (folder_store));
2026         } else if (TNY_IS_ACCOUNT (folder_store)) {
2027                 /* Use the folder store as an account: */
2028                 account = TNY_ACCOUNT (folder_store);
2029         }
2030  
2031         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2032                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2033                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2034                         
2035                         /* This IS a local account like a maildir account, which does not require 
2036                          * a connection. (original comment had a vague assumption in its language
2037                          * usage. There's no assuming needed, this IS what it IS: a local account), */
2038  
2039                         /* We promise to instantly perform the callback, so ... */
2040                         if (callback) {
2041                                 callback (FALSE, NULL, parent_window, account, user_data);
2042                         }
2043                         
2044                         return;
2045                 }
2046         }
2047  
2048         modest_platform_connect_and_perform (parent_window, account, callback, user_data);
2049  
2050         return;
2051 }
2052
2053 GtkWidget *
2054 modest_platform_get_account_settings_dialog (ModestAccountSettings *settings)
2055 {
2056         ModestAccountSettingsDialog *dialog = modest_account_settings_dialog_new ();
2057
2058         modest_account_settings_dialog_set_account (dialog, settings);
2059         return GTK_WIDGET (dialog);
2060 }
2061
2062 GtkWidget *
2063 modest_platform_get_account_settings_wizard ()
2064 {
2065         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2066
2067         return GTK_WIDGET (dialog);
2068 }