72e2ac530d7a8894d44606be3417065cf951be7d
[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, guint icon_size)
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         current_theme = gtk_icon_theme_get_default ();
495         pixbuf = gtk_icon_theme_load_icon (current_theme, name, icon_size,
496                                            GTK_ICON_LOOKUP_NO_SVG,
497                                            &err);
498         if (!pixbuf) {
499                 g_printerr ("modest: error loading theme icon '%s': %s\n",
500                             name, err->message);
501                 g_error_free (err);
502         } 
503         return pixbuf;
504 }
505
506 const gchar*
507 modest_platform_get_app_name (void)
508 {
509         return _("mcen_ap_name");
510 }
511
512 static void 
513 entry_insert_text (GtkEditable *editable,
514                    const gchar *text,
515                    gint         length,
516                    gint        *position,
517                    gpointer     data)
518 {
519         gchar *chars;
520         gint chars_length;
521
522         chars = gtk_editable_get_chars (editable, 0, -1);
523         chars_length = g_utf8_strlen (chars, -1);
524
525         /* Show WID-INF036 */
526         if (chars_length >= 20) {
527                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
528                                                  _CS("ckdg_ib_maximum_characters_reached"));
529         } else {
530                 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
531                         /* Show an error */
532                         gchar *tmp, *msg;
533                         
534                         tmp = g_strndup (folder_name_forbidden_chars, 
535                                          FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
536                         msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
537                         hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), 
538                                                          NULL, msg);
539                         g_free (msg);
540                         g_free (tmp);
541                 } else {        
542                         /* Write the text in the entry if it's valid */
543                         g_signal_handlers_block_by_func (editable,
544                                                          (gpointer) entry_insert_text, data);
545                         gtk_editable_insert_text (editable, text, length, position);
546                         g_signal_handlers_unblock_by_func (editable,
547                                                            (gpointer) entry_insert_text, data);
548                 }
549         }
550         /* Do not allow further processing */
551         g_signal_stop_emission_by_name (editable, "insert_text");
552 }
553
554 static void
555 entry_changed (GtkEditable *editable,
556                gpointer     user_data)
557 {
558         gchar *chars;
559         GtkWidget *ok_button;
560         GList *buttons;
561
562         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
563         ok_button = GTK_WIDGET (buttons->next->data);
564         
565         chars = gtk_editable_get_chars (editable, 0, -1);
566         g_return_if_fail (chars != NULL);
567
568         
569         if (g_utf8_strlen (chars,-1) >= 21)
570                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
571                                                  _CS("ckdg_ib_maximum_characters_reached"));
572         else
573                 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
574                 
575         /* Free */
576         g_list_free (buttons);
577         g_free (chars);
578 }
579
580 static guint
581 checked_hildon_sort_dialog_add_sort_key (HildonSortDialog *dialog, const gchar* key, guint max)
582 {
583         gint sort_key;
584         
585         g_return_val_if_fail (dialog && HILDON_IS_SORT_DIALOG(dialog), 0);
586         g_return_val_if_fail (key, 0);
587         
588         sort_key = hildon_sort_dialog_add_sort_key (dialog, key);
589         if (sort_key < 0 || sort_key >= max) {
590                 g_warning ("%s: out of range (%d) for %s", __FUNCTION__, sort_key, key);
591                 return 0;
592         } else
593                 return (guint)sort_key; 
594 }
595
596
597 static void
598 launch_sort_headers_dialog (GtkWindow *parent_window,
599                             HildonSortDialog *dialog)
600 {
601         ModestHeaderView *header_view = NULL;
602         GList *cols = NULL;
603         GtkSortType sort_type;
604         gint sort_key;
605         gint default_key = 0;
606         gint result;
607         gboolean outgoing = FALSE;
608         gint current_sort_colid = -1;
609         GtkSortType current_sort_type;
610         gint attachments_sort_id;
611         gint priority_sort_id;
612         GtkTreeSortable *sortable;
613         
614         /* Get header window */
615         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
616                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
617                                                                                       MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
618         }
619         if (!header_view)
620                 return;
621         
622         /* Add sorting keys */
623         cols = modest_header_view_get_columns (header_view);
624         if (cols == NULL) 
625                 return;
626 #define SORT_ID_NUM 6
627         int sort_model_ids[SORT_ID_NUM];
628         int sort_ids[SORT_ID_NUM];
629
630         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
631                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
632
633         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"),
634                                                             SORT_ID_NUM);
635         if (outgoing) {
636                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
637                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
638         } else {
639                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
640                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
641         }
642
643         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"),
644                                                             SORT_ID_NUM);
645         if (outgoing) {
646                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
647                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
648         } else {
649                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
650                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
651         }
652         default_key = sort_key;
653
654         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"),
655                                                             SORT_ID_NUM);
656         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
657         if (outgoing)
658                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
659         else
660                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
661
662         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"),
663                                                             SORT_ID_NUM);
664         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
665         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
666         attachments_sort_id = sort_key;
667
668         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"),
669                                                             SORT_ID_NUM);
670         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
671         sort_ids[sort_key] = 0;
672
673         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"),
674                                                             SORT_ID_NUM);
675         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
676         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY_MASK;
677         priority_sort_id = sort_key;
678         
679         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
680                                       (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
681         /* Launch dialogs */
682         if (!gtk_tree_sortable_get_sort_column_id (sortable,
683                                                    &current_sort_colid, &current_sort_type)) {
684                 hildon_sort_dialog_set_sort_key (dialog, default_key);
685                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
686         } else {
687                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
688                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
689                         gpointer flags_sort_type_pointer;
690                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
691                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
692                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
693                         else
694                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
695                 } else {
696                         gint current_sort_keyid = 0;
697                         while (current_sort_keyid < 6) {
698                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
699                                         break;
700                                 else 
701                                         current_sort_keyid++;
702                         }
703                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
704                 }
705         }
706
707         result = gtk_dialog_run (GTK_DIALOG (dialog));
708         if (result == GTK_RESPONSE_OK) {
709                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
710                 if (sort_key < 0 || sort_key > SORT_ID_NUM -1) {
711                         g_warning ("%s: out of range (%d)", __FUNCTION__, sort_key);
712                         sort_key = 0;
713                 }
714
715                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
716                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
717                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
718                                            GINT_TO_POINTER (sort_ids[sort_key]));
719                         /* This is a hack to make it resort rows always when flag fields are
720                          * selected. If we do not do this, changing sort field from priority to
721                          * attachments does not work */
722                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
723                 } else {
724                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
725                                                                  sort_model_ids[sort_key]);
726                 }
727
728                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
729                 gtk_tree_sortable_sort_column_changed (sortable);
730         }
731
732         modest_widget_memory_save (modest_runtime_get_conf (),
733                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
734         
735         /* free */
736         g_list_free(cols);      
737 }
738
739
740
741 static void
742 on_response (GtkDialog *dialog,
743              gint response,
744              gpointer user_data)
745 {
746         GList *child_vbox, *child_hbox;
747         GtkWidget *hbox, *entry;
748         TnyFolderStore *parent;
749         const gchar *new_name;
750         gboolean exists;
751
752         if (response != GTK_RESPONSE_ACCEPT)
753                 return;
754         
755         /* Get entry */
756         child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
757         hbox = child_vbox->data;
758         child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
759         entry = child_hbox->next->data;
760         
761         parent = TNY_FOLDER_STORE (user_data);
762         new_name = gtk_entry_get_text (GTK_ENTRY (entry));
763         exists = FALSE;
764         
765         /* Look for another folder with the same name */
766         if (modest_tny_folder_has_subfolder_with_name (parent, 
767                                                        new_name,
768                                                        TRUE)) {
769                 exists = TRUE;
770         }
771         
772         if (!exists) {
773                 if (TNY_IS_ACCOUNT (parent) &&
774                     modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
775                     modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
776                                                                          new_name)) {
777                         exists = TRUE;
778                 }
779         }
780         
781         if (exists) {
782                 
783                 /* Show an error */
784                 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), 
785                                                 NULL, _CS("ckdg_ib_folder_already_exists"));
786                 /* Select the text */
787                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
788                 gtk_widget_grab_focus (entry);
789                 /* Do not close the dialog */
790                 g_signal_stop_emission_by_name (dialog, "response");
791         }
792 }
793
794
795
796 static gint
797 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
798                                         TnyFolderStore *parent,
799                                         const gchar *dialog_title,
800                                         const gchar *label_text,
801                                         const gchar *suggested_name,
802                                         gchar **folder_name)
803 {
804         GtkWidget *accept_btn = NULL; 
805         GtkWidget *dialog, *entry, *label, *hbox;
806         GList *buttons = NULL;
807         gint result;
808
809         /* Ask the user for the folder name */
810         dialog = gtk_dialog_new_with_buttons (dialog_title,
811                                               parent_window,
812                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
813                                               _("mcen_bd_dialog_ok"),
814                                               GTK_RESPONSE_ACCEPT,
815                                               _("mcen_bd_dialog_cancel"),
816                                               GTK_RESPONSE_REJECT,
817                                               NULL);
818
819         /* Add accept button (with unsensitive handler) */
820         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
821         accept_btn = GTK_WIDGET (buttons->next->data);
822         /* Create label and entry */
823         label = gtk_label_new (label_text);
824         /* TODO: check that the suggested name does not exist */
825         /* We set 21 as maximum because we want to show WID-INF036
826            when the user inputs more that 20 */
827         entry = gtk_entry_new_with_max_length (21);
828         if (suggested_name)
829                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
830         else
831                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
832         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
833
834         /* Connect to the response method to avoid closing the dialog
835            when an invalid name is selected*/
836         g_signal_connect (dialog,
837                           "response",
838                           G_CALLBACK (on_response),
839                           parent);
840
841         /* Track entry changes */
842         g_signal_connect (entry,
843                           "insert-text",
844                           G_CALLBACK (entry_insert_text),
845                           dialog);
846         g_signal_connect (entry,
847                           "changed",
848                           G_CALLBACK (entry_changed),
849                           dialog);
850
851         /* Create the hbox */
852         hbox = gtk_hbox_new (FALSE, 12);
853         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
854         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
855
856         /* Add hbox to dialog */
857         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
858                             hbox, FALSE, FALSE, 0);
859         
860         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
861         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
862         
863         result = gtk_dialog_run (GTK_DIALOG(dialog));
864         if (result == GTK_RESPONSE_ACCEPT)
865                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
866
867         gtk_widget_destroy (dialog);
868
869         while (gtk_events_pending ())
870                 gtk_main_iteration ();
871
872         return result;
873 }
874
875 gint
876 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
877                                        TnyFolderStore *parent_folder,
878                                        gchar *suggested_name,
879                                        gchar **folder_name)
880 {
881         gchar *real_suggested_name = NULL;
882         gint result;
883
884         if(suggested_name == NULL)
885         {
886                 const gchar *default_name = _("mcen_ia_default_folder_name");
887                 unsigned int i;
888                 gchar num_str[3];
889
890                 for(i = 0; i < 100; ++ i) {
891                         gboolean exists = FALSE;
892
893                         sprintf(num_str, "%.2u", i);
894
895                         if (i == 0)
896                                 real_suggested_name = g_strdup (default_name);
897                         else
898                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
899                                                                        num_str);
900                         exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
901                                                                             real_suggested_name,
902                                                                             TRUE);
903
904                         if (!exists)
905                                 break;
906
907                         g_free (real_suggested_name);
908                 }
909
910                 /* Didn't find a free number */
911                 if (i == 100)
912                         real_suggested_name = g_strdup (default_name);
913         } else {
914                 real_suggested_name = suggested_name;
915         }
916
917         result = modest_platform_run_folder_name_dialog (parent_window, 
918                                                          parent_folder,
919                                                          _("mcen_ti_new_folder"),
920                                                          _("mcen_fi_new_folder_name"),
921                                                          real_suggested_name,
922                                                          folder_name);
923         if (suggested_name == NULL)
924                 g_free(real_suggested_name);
925
926         return result;
927 }
928
929 gint
930 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
931                                           TnyFolderStore *parent_folder,
932                                           const gchar *suggested_name,
933                                           gchar **folder_name)
934 {
935         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
936
937         return modest_platform_run_folder_name_dialog (parent_window, 
938                                                        parent_folder,
939                                                        _HL("ckdg_ti_rename_folder"),
940                                                        _HL("ckdg_fi_rename_name"),
941                                                        suggested_name,
942                                                        folder_name);
943 }
944
945
946
947 static void
948 on_destroy_dialog (GtkDialog *dialog)
949 {
950         gtk_widget_destroy (GTK_WIDGET(dialog));
951         if (gtk_events_pending ())
952                 gtk_main_iteration ();
953 }
954
955 gint
956 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
957                                          const gchar *message)
958 {
959         GtkWidget *dialog;
960         gint response;
961         
962         dialog = hildon_note_new_confirmation (parent_window, message);
963         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
964                                      GTK_WINDOW (dialog));
965
966         response = gtk_dialog_run (GTK_DIALOG (dialog));
967
968         on_destroy_dialog (GTK_DIALOG(dialog));
969         
970         while (gtk_events_pending ())
971                 gtk_main_iteration ();
972
973         return response;
974 }
975
976 gint
977 modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window,
978                                                       const gchar *message,
979                                                       const gchar *button_accept,
980                                                       const gchar *button_cancel)
981 {
982         GtkWidget *dialog;
983         gint response;
984         
985         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
986                                                            button_accept, GTK_RESPONSE_ACCEPT,
987                                                            button_cancel, GTK_RESPONSE_CANCEL,
988                                                            NULL);
989         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
990                                      GTK_WINDOW (dialog));
991
992         response = gtk_dialog_run (GTK_DIALOG (dialog));
993
994         on_destroy_dialog (GTK_DIALOG(dialog));
995         
996         while (gtk_events_pending ())
997                 gtk_main_iteration ();
998
999         return response;
1000 }
1001         
1002 gint
1003 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
1004                                    const gchar *message)
1005 {
1006         GtkWidget *dialog;
1007         gint response;
1008         
1009         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1010                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
1011                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
1012                                                            NULL);
1013         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
1014         response = gtk_dialog_run (GTK_DIALOG (dialog));
1015         
1016         on_destroy_dialog (GTK_DIALOG(dialog));
1017
1018         while (gtk_events_pending ())
1019                 gtk_main_iteration ();
1020
1021         return response;
1022 }
1023
1024
1025
1026 void
1027 modest_platform_run_information_dialog (GtkWindow *parent_window,
1028                                         const gchar *message)
1029 {
1030         GtkWidget *note;
1031         
1032         note = hildon_note_new_information (parent_window, message);
1033         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1034                                      GTK_WINDOW (note));
1035         
1036         g_signal_connect_swapped (note,
1037                                   "response", 
1038                                   G_CALLBACK (on_destroy_dialog),
1039                                   note);
1040
1041         gtk_widget_show_all (note);
1042 }
1043
1044
1045
1046 typedef struct _ConnectAndWaitData {
1047         GMutex *mutex;
1048         GMainLoop *wait_loop;
1049         gboolean has_callback;
1050         gulong handler;
1051 } ConnectAndWaitData;
1052
1053
1054 static void
1055 quit_wait_loop (TnyAccount *account,
1056                 ConnectAndWaitData *data) 
1057 {
1058         /* Set the has_callback to TRUE (means that the callback was
1059            executed and wake up every code waiting for cond to be
1060            TRUE */
1061         g_mutex_lock (data->mutex);
1062         data->has_callback = TRUE;
1063         if (data->wait_loop)
1064                 g_main_loop_quit (data->wait_loop);
1065         g_mutex_unlock (data->mutex);
1066 }
1067
1068 static void
1069 on_connection_status_changed (TnyAccount *account, 
1070                               TnyConnectionStatus status,
1071                               gpointer user_data)
1072 {
1073         TnyConnectionStatus conn_status;
1074         ConnectAndWaitData *data;
1075                         
1076         /* Ignore if reconnecting or disconnected */
1077         conn_status = tny_account_get_connection_status (account);
1078         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1079             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1080                 return;
1081
1082         /* Remove the handler */
1083         data = (ConnectAndWaitData *) user_data;
1084         g_signal_handler_disconnect (account, data->handler);
1085
1086         /* Quit from wait loop */
1087         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1088 }
1089
1090 static void
1091 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1092                                     gboolean canceled, 
1093                                     GError *err, 
1094                                     gpointer user_data)
1095 {
1096         /* Quit from wait loop */
1097         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1098 }
1099
1100 gboolean 
1101 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1102                                   TnyAccount *account)
1103 {
1104         ConnectAndWaitData *data = NULL;
1105         gboolean device_online;
1106         TnyDevice *device;
1107         TnyConnectionStatus conn_status;
1108         gboolean user_requested;
1109         
1110         device = modest_runtime_get_device();
1111         device_online = tny_device_is_online (device);
1112
1113         /* Whether the connection is user requested or automatically
1114            requested, for example via D-Bus */
1115         user_requested = (parent_window) ? TRUE : FALSE;
1116
1117         /* If there is no account check only the device status */
1118         if (!account) {
1119                 if (device_online)
1120                         return TRUE;
1121                 else
1122                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1123                                                                NULL, user_requested);
1124         }
1125
1126         /* Return if the account is already connected */
1127         conn_status = tny_account_get_connection_status (account);
1128         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1129                 return TRUE;
1130
1131         /* Create the helper */
1132         data = g_slice_new0 (ConnectAndWaitData);
1133         data->mutex = g_mutex_new ();
1134         data->has_callback = FALSE;
1135
1136         /* Connect the device */
1137         if (!device_online) {
1138                 /* Track account connection status changes */
1139                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1140                                                   G_CALLBACK (on_connection_status_changed),
1141                                                   data);
1142                 /* Try to connect the device */
1143                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1144                                                                 NULL, user_requested);
1145
1146                 /* If the device connection failed then exit */
1147                 if (!device_online && data->handler)
1148                         goto frees;
1149         } else {
1150                 /* Force a reconnection of the account */
1151                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1152                                               on_tny_camel_account_set_online_cb, data);
1153         }
1154
1155         /* Wait until the callback is executed */
1156         g_mutex_lock (data->mutex);
1157         if (!data->has_callback) {
1158                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1159                 gdk_threads_leave ();
1160                 g_mutex_unlock (data->mutex);
1161                 g_main_loop_run (data->wait_loop);
1162                 g_mutex_lock (data->mutex);
1163                 gdk_threads_enter ();
1164         }
1165         g_mutex_unlock (data->mutex);
1166
1167  frees:
1168         if (data) {
1169                 if (g_signal_handler_is_connected (account, data->handler))
1170                         g_signal_handler_disconnect (account, data->handler);
1171                 g_mutex_free (data->mutex);
1172                 g_main_loop_unref (data->wait_loop);
1173                 g_slice_free (ConnectAndWaitData, data);
1174         }
1175
1176         conn_status = tny_account_get_connection_status (account);
1177         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1178 }
1179
1180 gboolean 
1181 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1182 {
1183         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1184                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1185                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1186                         /* This must be a maildir account, which does not require a connection: */
1187                         return TRUE;
1188                 }
1189         }
1190
1191         return modest_platform_connect_and_wait (parent_window, account);
1192 }
1193
1194 gboolean 
1195 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1196 {
1197         if (!folder_store)
1198                 return TRUE; /* Maybe it is something local. */
1199                 
1200         gboolean result = TRUE;
1201         if (TNY_IS_FOLDER (folder_store)) {
1202                 /* Get the folder's parent account: */
1203                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1204                 if (account != NULL) {
1205                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1206                         g_object_unref (account);
1207                 }
1208         } else if (TNY_IS_ACCOUNT (folder_store)) {
1209                 /* Use the folder store as an account: */
1210                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1211         }
1212
1213         return result;
1214 }
1215
1216 void
1217 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1218                                  ModestSortDialogType type)
1219 {
1220         GtkWidget *dialog = NULL;
1221
1222         /* Build dialog */
1223         dialog = hildon_sort_dialog_new (parent_window);
1224         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1225                                      GTK_WINDOW (dialog));
1226
1227         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1228                                         "applications_email_sort",
1229                                         modest_maemo_utils_get_osso_context());
1230
1231         /* Fill sort keys */
1232         switch (type) {
1233         case MODEST_SORT_HEADERS:
1234                 launch_sort_headers_dialog (parent_window, 
1235                                             HILDON_SORT_DIALOG(dialog));
1236                 break;
1237         }
1238         
1239         /* Free */
1240         on_destroy_dialog (GTK_DIALOG(dialog));
1241 }
1242
1243
1244 gboolean 
1245 modest_platform_set_update_interval (guint minutes)
1246 {
1247 #ifdef MODEST_HAVE_LIBALARM
1248         
1249         ModestConf *conf = modest_runtime_get_conf ();
1250         if (!conf)
1251                 return FALSE;
1252                 
1253         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1254
1255         /* Delete any existing alarm,
1256          * because we will replace it: */
1257         if (alarm_cookie) {
1258                 if (alarm_event_del(alarm_cookie) != 1)
1259                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1260                 alarm_cookie = 0;
1261                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1262         }
1263         
1264         /* 0 means no updates: */
1265         if (minutes == 0)
1266                 return TRUE;
1267         
1268      
1269         /* Register alarm: */
1270         
1271         /* Set the interval in alarm_event_t structure: */
1272         alarm_event_t *event = g_new0(alarm_event_t, 1);
1273         event->alarm_time = minutes * 60; /* seconds */
1274         
1275         /* Set recurrence every few minutes: */
1276         event->recurrence = minutes;
1277         event->recurrence_count = -1; /* Means infinite */
1278
1279         /* Specify what should happen when the alarm happens:
1280          * It should call this D-Bus method: */
1281          
1282         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1283         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1284         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1285         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1286
1287         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1288          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1289          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1290          * This is why we want to use the Alarm API instead of just g_timeout_add().
1291          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1292          */
1293         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1294         
1295         alarm_cookie = alarm_event_add (event);
1296
1297         /* now, free it */
1298         alarm_event_free (event);
1299         
1300         /* Store the alarm ID in GConf, so we can remove it later:
1301          * This is apparently valid between application instances. */
1302         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1303         
1304         if (!alarm_cookie) {
1305             /* Error */
1306             const alarm_error_t alarm_error = alarmd_get_error ();
1307             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1308             
1309             /* Give people some clue: */
1310             /* The alarm API should have a function for this: */
1311             if (alarm_error == ALARMD_ERROR_DBUS) {
1312                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1313             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1314                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1315             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1316                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1317             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1318                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1319             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1320                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1321             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1322                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1323             }
1324             
1325             return FALSE;
1326         }
1327 #endif /* MODEST_HAVE_LIBALARM */       
1328         return TRUE;
1329 }
1330
1331 void 
1332 modest_platform_on_new_headers_received (TnyList *header_list) 
1333 {
1334 #ifdef MODEST_HAVE_HILDON_NOTIFY
1335         HildonNotification *notification;
1336         TnyIterator *iter;
1337         GSList *notifications_list = NULL;
1338
1339         /* Get previous notifications ids */
1340         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1341                                                    MODEST_CONF_NOTIFICATION_IDS, 
1342                                                    MODEST_CONF_VALUE_INT, NULL);
1343
1344         iter = tny_list_create_iterator (header_list);
1345         while (!tny_iterator_is_done (iter)) {
1346                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1347                 const gchar *display_date;
1348                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1349                 TnyFolder *folder = tny_header_get_folder (header);
1350                 gboolean first_notification = TRUE;
1351                 gint notif_id;
1352
1353                 /* constant string, don't free */
1354                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1355
1356                 display_address = g_strdup(tny_header_get_from (header));
1357                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1358                 
1359                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1360                 notification = hildon_notification_new (summary,
1361                                                         tny_header_get_subject (header),
1362                                                         "qgn_list_messagin",
1363                                                         "email.arrive");
1364                 /* Create the message URL */
1365                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1366                                        tny_header_get_uid (header));
1367
1368                 hildon_notification_add_dbus_action(notification,
1369                                                     "default",
1370                                                     "Cancel",
1371                                                     MODEST_DBUS_SERVICE,
1372                                                     MODEST_DBUS_OBJECT,
1373                                                     MODEST_DBUS_IFACE,
1374                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1375                                                     G_TYPE_STRING, url,
1376                                                     -1);
1377
1378                 /* Play sound if the user wants. Show the LED
1379                    pattern. Show and play just one */
1380                 if (G_UNLIKELY (first_notification)) {
1381                         first_notification = FALSE;
1382                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1383                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1384                                                   NULL))  {
1385                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1386                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1387                         }
1388
1389                         /* Set the led pattern */
1390                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1391                                                             "dialog-type", 4);
1392                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1393                                                             "led-pattern",
1394                                                             "PatternCommunicationEmail");                       
1395                 }
1396
1397                 /* Notify. We need to do this in an idle because this function
1398                    could be called from a thread */
1399                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1400
1401                 /* Save id in the list */
1402                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1403                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1404                 /* We don't listen for the "closed" signal, because we
1405                    don't care about if the notification was removed or
1406                    not to store the list in gconf */
1407         
1408                 /* Free & carry on */
1409                 g_free (display_address);
1410                 g_free (summary);
1411                 g_free (url);
1412                 g_object_unref (folder);
1413                 g_object_unref (header);
1414                 tny_iterator_next (iter);
1415         }
1416         g_object_unref (iter);
1417
1418         /* Save the ids */
1419         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1420                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1421
1422         g_slist_free (notifications_list);
1423         
1424 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1425 }
1426
1427 void
1428 modest_platform_remove_new_mail_notifications (void) 
1429 {
1430 #ifdef MODEST_HAVE_HILDON_NOTIFY
1431         GSList *notif_list = NULL;
1432
1433         /* Get previous notifications ids */
1434         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1435                                            MODEST_CONF_NOTIFICATION_IDS, 
1436                                            MODEST_CONF_VALUE_INT, NULL);
1437
1438         while (notif_list) {
1439                 gint notif_id;
1440                 NotifyNotification *notif;
1441
1442                 /* Nasty HACK to remove the notifications, set the id
1443                    of the existing ones and then close them */
1444                 notif_id = GPOINTER_TO_INT(notif_list->data);
1445                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1446                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1447
1448                 /* Close the notification, note that some ids could be
1449                    already invalid, but we don't care because it does
1450                    not fail */
1451                 notify_notification_close(notif, NULL);
1452                 g_object_unref(notif);
1453
1454                 /* Delete the link, it's like going to the next */
1455                 notif_list = g_slist_delete_link (notif_list, notif_list);
1456         }
1457
1458         /* Save the ids */
1459         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1460                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1461
1462         g_slist_free (notif_list);
1463
1464 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1465 }
1466
1467
1468
1469 GtkWidget * 
1470 modest_platform_get_global_settings_dialog ()
1471 {
1472         return modest_maemo_global_settings_dialog_new ();
1473 }
1474
1475 void
1476 modest_platform_show_help (GtkWindow *parent_window, 
1477                            const gchar *help_id)
1478 {
1479         osso_return_t result;
1480         g_return_if_fail (help_id);
1481
1482         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1483                                    help_id, HILDON_HELP_SHOW_DIALOG);
1484         
1485         if (result != OSSO_OK) {
1486                 gchar *error_msg;
1487                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1488                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1489                                                 NULL,
1490                                                 error_msg);
1491                 g_free (error_msg);
1492         }
1493 }
1494
1495 void 
1496 modest_platform_show_search_messages (GtkWindow *parent_window)
1497 {
1498         osso_return_t result = OSSO_ERROR;
1499         
1500         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1501                                              "osso_global_search",
1502                                              "search_email", NULL, DBUS_TYPE_INVALID);
1503
1504         if (result != OSSO_OK) {
1505                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1506         }
1507 }
1508
1509 void 
1510 modest_platform_show_addressbook (GtkWindow *parent_window)
1511 {
1512         osso_return_t result = OSSO_ERROR;
1513         
1514         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1515                                              "osso_addressbook",
1516                                              "top_application", NULL, DBUS_TYPE_INVALID);
1517
1518         if (result != OSSO_OK) {
1519                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1520         }
1521 }
1522
1523 GtkWidget *
1524 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1525 {
1526         GtkWidget *widget = modest_folder_view_new (query);
1527
1528         /* Show one account by default */
1529         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1530                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1531
1532
1533         /* Restore settings */
1534         modest_widget_memory_restore (modest_runtime_get_conf(), 
1535                                       G_OBJECT (widget),
1536                                       MODEST_CONF_FOLDER_VIEW_KEY);
1537
1538         return widget;
1539 }
1540
1541 void 
1542 modest_platform_information_banner (GtkWidget *parent,
1543                                     const gchar *icon_name,
1544                                     const gchar *text)
1545 {
1546         hildon_banner_show_information (parent, icon_name, text);
1547 }
1548
1549 void
1550 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1551                                                  const gchar *icon_name,
1552                                                  const gchar *text,
1553                                                  gint timeout)
1554 {
1555         GtkWidget *banner;
1556         banner = hildon_banner_show_information (parent, icon_name, text);
1557         hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1558 }
1559
1560 GtkWidget *
1561 modest_platform_animation_banner (GtkWidget *parent,
1562                                   const gchar *animation_name,
1563                                   const gchar *text)
1564 {
1565         GtkWidget *inf_note = NULL;
1566
1567         g_return_val_if_fail (text != NULL, NULL);
1568
1569         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1570
1571         return inf_note;
1572 }
1573
1574 typedef struct
1575 {
1576         GMainLoop* loop;
1577         TnyAccount *account;
1578         gboolean is_online;
1579         gint count_tries;
1580 } CheckAccountIdleData;
1581
1582 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1583
1584 static gboolean 
1585 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1586 {
1587         gboolean stop_trying = FALSE;
1588         g_return_val_if_fail (data && data->account, FALSE);
1589         
1590         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1591                 tny_account_get_connection_status (data->account));     
1592         
1593         if (data && data->account && 
1594                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1595                  * after which the account is likely to be usable, or never likely to be usable soon: */
1596                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1597         {
1598                 data->is_online = TRUE;
1599                 
1600                 stop_trying = TRUE;
1601         } else {
1602                 /* Give up if we have tried too many times: */
1603                 if (data->count_tries >= NUMBER_OF_TRIES) {
1604                         stop_trying = TRUE;
1605                 } else {
1606                         /* Wait for another timeout: */
1607                         ++(data->count_tries);
1608                 }
1609         }
1610         
1611         if (stop_trying) {
1612                 /* Allow the function that requested this idle callback to continue: */
1613                 if (data->loop)
1614                         g_main_loop_quit (data->loop);
1615                         
1616                 if (data->account)
1617                         g_object_unref (data->account);
1618                 
1619                 return FALSE; /* Don't call this again. */
1620         } else {
1621                 return TRUE; /* Call this timeout callback again. */
1622         }
1623 }
1624
1625 /* Return TRUE immediately if the account is already online,
1626  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1627  * soon as the account is online, or FALSE if the account does 
1628  * not become online in the NUMBER_OF_TRIES seconds.
1629  * This is useful when the D-Bus method was run immediately after 
1630  * the application was started (when using D-Bus activation), 
1631  * because the account usually takes a short time to go online.
1632  * The return value is maybe not very useful.
1633  */
1634 gboolean
1635 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1636 {
1637         g_return_val_if_fail (account, FALSE);
1638         
1639         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1640         
1641         if (!tny_device_is_online (modest_runtime_get_device())) {
1642                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1643                 return FALSE;
1644         }
1645         
1646         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1647          * so we avoid wait unnecessarily: */
1648         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1649                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1650                 return TRUE;            
1651         }
1652                 
1653         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1654                 __FUNCTION__, tny_account_get_connection_status (account));
1655         
1656         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1657          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1658          * we want to avoid. */
1659         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1660                 return TRUE;
1661                 
1662         /* This blocks on the result: */
1663         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1664         data->is_online = FALSE;
1665         data->account = account;
1666         g_object_ref (data->account);
1667         data->count_tries = 0;
1668                 
1669         GMainContext *context = NULL; /* g_main_context_new (); */
1670         data->loop = g_main_loop_new (context, FALSE /* not running */);
1671
1672         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1673
1674         /* This main loop will run until the idle handler has stopped it: */
1675         g_main_loop_run (data->loop);
1676
1677         g_main_loop_unref (data->loop);
1678         /* g_main_context_unref (context); */
1679
1680         g_slice_free (CheckAccountIdleData, data);
1681         
1682         return data->is_online; 
1683 }
1684
1685
1686
1687 static void
1688 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1689 {
1690         /* GTK_RESPONSE_HELP means we need to show the certificate */
1691         if (response_id == GTK_RESPONSE_HELP) {
1692                 GtkWidget *note;
1693                 gchar *msg;
1694                 
1695                 /* Do not close the dialog */
1696                 g_signal_stop_emission_by_name (dialog, "response");
1697
1698                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1699                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1700                 gtk_dialog_run (GTK_DIALOG(note));
1701                 gtk_widget_destroy (note);
1702         }
1703 }
1704
1705
1706 gboolean
1707 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1708                                                      const gchar *certificate)
1709 {
1710         GtkWidget *note;
1711         gint response;
1712         ModestWindow *main_win;
1713         
1714         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1715                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1716                            __FUNCTION__);
1717                 return FALSE;
1718         }
1719
1720         /* don't create it */
1721         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1722         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1723         
1724         
1725         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1726                                            server_name);
1727         
1728         note = hildon_note_new_confirmation_add_buttons  (
1729                 GTK_WINDOW(main_win),
1730                 question,
1731                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1732                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1733                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1734                 NULL, NULL);
1735         
1736         g_signal_connect (G_OBJECT(note), "response", 
1737                           G_CALLBACK(on_cert_dialog_response),
1738                           (gpointer) certificate);
1739         
1740         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1741                                      GTK_WINDOW (note));
1742         response = gtk_dialog_run(GTK_DIALOG(note));
1743
1744         on_destroy_dialog (GTK_DIALOG(note));
1745         g_free (question);
1746         
1747         return response == GTK_RESPONSE_OK;
1748 }
1749
1750 gboolean
1751 modest_platform_run_alert_dialog (const gchar* prompt, 
1752                                   gboolean is_question)
1753 {       
1754         ModestWindow *main_win; 
1755
1756         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1757                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1758                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1759                 return is_question ? FALSE : TRUE;
1760         }
1761
1762         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1763         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1764         
1765         gboolean retval = TRUE;
1766         if (is_question) {
1767                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1768                  * when it is a question.
1769                  * Obviously, we need tinymail to use more specific error codes instead,
1770                  * so we know what buttons to show. */
1771                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1772                                                                               prompt));
1773                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1774                                              GTK_WINDOW (dialog));
1775                 
1776                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1777                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1778                 
1779                 on_destroy_dialog (GTK_DIALOG(dialog));         
1780         } else {
1781                 /* Just show the error text and use the default response: */
1782                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1783                                                         prompt);
1784         }
1785         return retval;
1786 }
1787
1788 /***************/
1789 typedef struct {
1790         GtkWindow *parent_window;
1791         ModestConnectedPerformer callback;
1792         TnyAccount *account;
1793         gpointer user_data;
1794         gchar *iap;
1795         TnyDevice *device;
1796 } OnWentOnlineInfo;
1797  
1798 static void 
1799 on_went_online_info_free (OnWentOnlineInfo *info)
1800 {
1801         /* And if we cleanup, we DO cleanup  :-)  */
1802         
1803         if (info->device)
1804                 g_object_unref (info->device);
1805         if (info->iap)
1806                 g_free (info->iap);
1807         if (info->parent_window)
1808                 g_object_unref (info->parent_window);
1809         if (info->account)
1810                 g_object_unref (info->account);
1811         
1812         g_slice_free (OnWentOnlineInfo, info);
1813         
1814         /* We're done ... */
1815         
1816         return;
1817 }
1818  
1819 static void
1820 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1821 {
1822         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1823  
1824         /* Now it's really time to callback to the caller. If going online didn't succeed,
1825          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1826          * canceled will be set. Etcetera etcetera. */
1827         
1828         if (info->callback) {
1829                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1830         }
1831         
1832         /* This is our last call, we must cleanup here if we didn't yet do that */
1833         on_went_online_info_free (info);
1834         
1835         return;
1836 }
1837  
1838  
1839 static void
1840 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1841 {
1842         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1843         info->iap = g_strdup (iap_id);
1844         
1845         if (canceled || err || !info->account) {
1846         
1847                 /* If there's a problem or if there's no account (then that's it for us, we callback
1848                  * the caller's callback now. He'll have to handle err or canceled, of course.
1849                  * We are not really online, as the account is not really online here ... */    
1850                 
1851                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1852                  * this info. We don't cleanup err, Tinymail does that! */
1853                 
1854                 if (info->callback) {
1855                         
1856                         /* info->account can be NULL here, this means that the user did not
1857                          * provide a nice account instance. We'll assume that the user knows
1858                          * what he's doing and is happy with just the device going online. 
1859                          * 
1860                          * We can't do magic, we don't know what account the user wants to
1861                          * see going online. So just the device goes online, end of story */
1862                         
1863                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1864                 }
1865                 
1866         } else if (info->account) {
1867                 
1868                 /* If there's no problem and if we have an account, we'll put the account
1869                  * online too. When done, the callback of bringing the account online
1870                  * will callback the caller's callback. This is the most normal case. */
1871  
1872                 info->device = TNY_DEVICE (g_object_ref (device));
1873                 
1874                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1875                                               on_account_went_online, info);
1876                 
1877                 /* The on_account_went_online cb frees up the info, go look if you
1878                  * don't believe me! (so we return here) */
1879                 
1880                 return;
1881         }
1882         
1883         /* We cleanup if we are not bringing the account online too */
1884         on_went_online_info_free (info);
1885  
1886         return; 
1887 }
1888         
1889 void 
1890 modest_platform_connect_and_perform (GtkWindow *parent_window, 
1891                                      TnyAccount *account, 
1892                                      ModestConnectedPerformer callback, 
1893                                      gpointer user_data)
1894 {
1895         gboolean device_online;
1896         TnyDevice *device;
1897         TnyConnectionStatus conn_status;
1898         OnWentOnlineInfo *info;
1899         gboolean user_requested;
1900         
1901         device = modest_runtime_get_device();
1902         device_online = tny_device_is_online (device);
1903
1904         /* Whether the connection is user requested or automatically
1905            requested, for example via D-Bus */
1906         user_requested = (parent_window) ? TRUE : FALSE;
1907
1908         /* If there is no account check only the device status */
1909         if (!account) {
1910                 
1911                 if (device_online) {
1912  
1913                         /* We promise to instantly perform the callback, so ... */
1914                         if (callback) {
1915                                 callback (FALSE, NULL, parent_window, account, user_data);
1916                         }
1917                         
1918                 } else {
1919                         
1920                         info = g_slice_new0 (OnWentOnlineInfo);
1921                         
1922                         info->iap = NULL;
1923                         info->device = NULL;
1924                         info->account = NULL;
1925                 
1926                         if (parent_window)
1927                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1928                         else
1929                                 info->parent_window = NULL;
1930                         info->user_data = user_data;
1931                         info->callback = callback;
1932                 
1933                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1934                                                               user_requested, on_conic_device_went_online, 
1935                                                               info);
1936  
1937                         /* We'll cleanup in on_conic_device_went_online */
1938                 }
1939  
1940                 /* The other code has no more reason to run. This is all that we can do for the
1941                  * caller (he should have given us a nice and clean account instance!). We
1942                  * can't do magic, we don't know what account he intends to bring online. So
1943                  * we'll just bring the device online (and await his false bug report). */
1944                 
1945                 return;
1946         }
1947  
1948         
1949         /* Return if the account is already connected */
1950         
1951         conn_status = tny_account_get_connection_status (account);
1952         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1953  
1954                 /* We promise to instantly perform the callback, so ... */
1955                 if (callback) {
1956                         callback (FALSE, NULL, parent_window, account, user_data);
1957                 }
1958                 
1959                 return;
1960         }
1961         
1962         /* Else, we are in a state that requires that we go online before we
1963          * call the caller's callback. */
1964         
1965         info = g_slice_new0 (OnWentOnlineInfo);
1966         
1967         info->device = NULL;
1968         info->iap = NULL;
1969         info->account = TNY_ACCOUNT (g_object_ref (account));
1970         
1971         if (parent_window)
1972                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1973         else
1974                 info->parent_window = NULL;
1975         
1976         /* So we'll put the callback away for later ... */
1977         
1978         info->user_data = user_data;
1979         info->callback = callback;
1980         
1981         if (!device_online) {
1982  
1983                 /* If also the device is offline, then we connect both the device 
1984                  * and the account */
1985                 
1986                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1987                                                       user_requested, on_conic_device_went_online, 
1988                                                       info);
1989                 
1990         } else {
1991                 
1992                 /* If the device is online, we'll just connect the account */
1993                 
1994                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1995                                               on_account_went_online, info);
1996         }
1997  
1998         /* The info gets freed by on_account_went_online or on_conic_device_went_online
1999          * in both situations, go look if you don't believe me! */
2000         
2001         return;
2002 }
2003
2004 void
2005 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
2006                                                             TnyFolderStore *folder_store, 
2007                                                             ModestConnectedPerformer callback, 
2008                                                             gpointer user_data)
2009 {
2010         TnyAccount *account = NULL;
2011         
2012         if (!folder_store) {
2013                 /* We promise to instantly perform the callback, so ... */
2014                 if (callback) {
2015                         callback (FALSE, NULL, parent_window, NULL, user_data);
2016                 }
2017                 return; 
2018                 
2019                 /* Original comment: Maybe it is something local. */
2020                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
2021                 
2022         } else if (TNY_IS_FOLDER (folder_store)) {
2023                 /* Get the folder's parent account: */
2024                 account = tny_folder_get_account(TNY_FOLDER (folder_store));
2025         } else if (TNY_IS_ACCOUNT (folder_store)) {
2026                 /* Use the folder store as an account: */
2027                 account = TNY_ACCOUNT (folder_store);
2028         }
2029  
2030         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2031                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2032                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2033                         
2034                         /* This IS a local account like a maildir account, which does not require 
2035                          * a connection. (original comment had a vague assumption in its language
2036                          * usage. There's no assuming needed, this IS what it IS: a local account), */
2037  
2038                         /* We promise to instantly perform the callback, so ... */
2039                         if (callback) {
2040                                 callback (FALSE, NULL, parent_window, account, user_data);
2041                         }
2042                         
2043                         return;
2044                 }
2045         }
2046  
2047         modest_platform_connect_and_perform (parent_window, account, callback, user_data);
2048  
2049         return;
2050 }
2051
2052 GtkWidget *
2053 modest_platform_get_account_settings_dialog (ModestAccountSettings *settings)
2054 {
2055         ModestAccountSettingsDialog *dialog = modest_account_settings_dialog_new ();
2056
2057         modest_account_settings_dialog_set_account (dialog, settings);
2058         return GTK_WIDGET (dialog);
2059 }
2060
2061 GtkWidget *
2062 modest_platform_get_account_settings_wizard ()
2063 {
2064         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2065
2066         return GTK_WIDGET (dialog);
2067 }