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