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