Fix plugin api, so that it includes all the new required headers.
[modest] / src / hildon2 / 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
33 #include <modest-platform.h>
34 #include <modest-defs.h>
35 #include <modest-runtime.h>
36 #include <modest-main-window.h>
37 #include <modest-header-view.h>
38 #include "modest-hildon2-global-settings-dialog.h"
39 #include "modest-widget-memory.h"
40 #include <modest-hildon-includes.h>
41 #include <modest-maemo-utils.h>
42 #include <dbus_api/modest-dbus-callbacks.h>
43 #include <modest-osso-autosave-callbacks.h>
44 #include <libosso.h>
45 #include <tny-maemo-conic-device.h>
46 #include <tny-simple-list.h>
47 #include <tny-merge-folder.h>
48 #include <tny-error.h>
49 #include <tny-folder.h>
50 #include <tny-account-store-view.h>
51 #include <gtk/gtkicontheme.h>
52 #include <gtk/gtkmenuitem.h>
53 #include <gtk/gtkmain.h>
54 #include <modest-text-utils.h>
55 #include "modest-tny-folder.h"
56 #include "modest-tny-account.h"
57 #include <string.h>
58 #include <libgnomevfs/gnome-vfs-mime-utils.h>
59 #include <modest-account-settings-dialog.h>
60 #include <modest-easysetup-wizard-dialog.h>
61 #include "modest-hildon2-sort-dialog.h"
62 #include <hildon/hildon.h>
63 #include <osso-mem.h>
64 #include "hildon2/modest-hildon2-details-dialog.h"
65 #include "hildon2/modest-hildon2-window-mgr.h"
66 #include <keys_nokia.h>
67 #include <libprofile.h>
68 #include <canberra.h>
69 #include <modest-datetime-formatter.h>
70 #include "modest-header-window.h"
71 #include <modest-folder-window.h>
72 #include <modest-account-mgr.h>
73 #include <modest-account-mgr-helpers.h>
74 #include <modest-ui-constants.h>
75 #include <modest-selector-picker.h>
76 #include <modest-icon-names.h>
77
78 #ifdef MODEST_HAVE_MCE
79 #include <mce/dbus-names.h>
80 #endif /*MODEST_HAVE_MCE*/
81
82 #ifdef MODEST_HAVE_ABOOK
83 #include <libosso-abook/osso-abook.h>
84 #endif /*MODEST_HAVE_ABOOK*/
85
86 #ifdef MODEST_HAVE_LIBALARM
87 #include <alarmd/libalarm.h> /* For alarm_event_add(), etc. */
88 #endif /*MODEST_HAVE_LIBALARM*/
89
90
91 #define HILDON_OSSO_URI_ACTION "uri-action"
92 #define URI_ACTION_COPY "copy:"
93 #define MODEST_NEW_MAIL_LIGHTING_PATTERN "PatternCommunicationEmail"
94 #define PROFILE_MAIL_TONE PROFILEKEY_EMAIL_ALERT_TONE
95 #define PROFILE_MAIL_VOLUME PROFILEKEY_EMAIL_ALERT_VOLUME
96
97 #define COMMON_FOLDER_DIALOG_ENTRY "entry"
98 #define COMMON_FOLDER_DIALOG_ACCOUNT_PICKER "account-picker"
99 #define FOLDER_PICKER_CURRENT_FOLDER "current-folder"
100 #define MODEST_ALARMD_APPID PACKAGE_NAME
101
102
103 static void _modest_platform_play_email_tone (void);
104
105
106 static void     
107 on_modest_conf_update_interval_changed (ModestConf* self, 
108                                         const gchar *key, 
109                                         ModestConfEvent event,
110                                         ModestConfNotificationId id, 
111                                         gpointer user_data)
112 {
113         g_return_if_fail (key);
114         
115         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
116                 const guint update_interval_minutes = 
117                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
118                 modest_platform_set_update_interval (update_interval_minutes);
119         }
120 }
121
122
123
124 static gboolean
125 check_required_files (void)
126 {
127         FILE *mcc_file = modest_maemo_open_mcc_mapping_file ();
128         if (!mcc_file) {
129                 g_printerr ("modest: check for mcc file failed\n");
130                 return FALSE;
131         } else 
132                 fclose (mcc_file);
133         
134         if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
135             access(MODEST_MAEMO_PROVIDER_DATA_FILE, R_OK) != 0) {
136                 g_printerr ("modest: cannot find providers data\n");
137                 return FALSE;
138         }
139         
140         return TRUE;
141 }
142
143
144 /* the gpointer here is the osso_context. */
145 gboolean
146 modest_platform_init (int argc, char *argv[])
147 {
148         osso_context_t *osso_context;
149         
150         osso_hw_state_t hw_state = { 0 };
151         DBusConnection *con;    
152         GSList *acc_names;
153         
154         if (!check_required_files ()) {
155                 g_printerr ("modest: missing required files\n");
156                 return FALSE;
157         }
158         
159         osso_context =  osso_initialize(PACKAGE,PACKAGE_VERSION,
160                                         FALSE, NULL);   
161         if (!osso_context) {
162                 g_printerr ("modest: failed to acquire osso context\n");
163                 return FALSE;
164         }
165         modest_maemo_utils_set_osso_context (osso_context);
166
167         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
168                 g_printerr ("modest: could not get dbus connection\n");
169                 return FALSE;
170         }
171
172         /* Add a D-Bus handler to be used when the main osso-rpc 
173          * D-Bus handler has not handled something.
174          * We use this for D-Bus methods that need to use more complex types 
175          * than osso-rpc supports. 
176          */
177         if (!dbus_connection_add_filter (con,
178                                          modest_dbus_req_filter,
179                                          NULL,
180                                          NULL)) {
181
182                 g_printerr ("modest: Could not add D-Bus filter\n");
183                 return FALSE;
184         }
185
186         /* Register our simple D-Bus callbacks, via the osso API: */
187         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
188                                MODEST_DBUS_SERVICE, 
189                                MODEST_DBUS_OBJECT, 
190                                MODEST_DBUS_IFACE,
191                                modest_dbus_req_handler, NULL /* user_data */);
192         if (result != OSSO_OK) {
193                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
194                 return FALSE;
195         }
196
197         /* Register hardware event dbus callback: */
198         hw_state.shutdown_ind = TRUE;
199         osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
200
201         /* Register osso auto-save callbacks: */
202         result = osso_application_set_autosave_cb (osso_context, 
203                 modest_on_osso_application_autosave, NULL /* user_data */);
204         if (result != OSSO_OK) {
205                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
206                 return FALSE;
207         }
208         
209
210         /* Make sure that the update interval is changed whenever its gconf key 
211          * is changed */
212         /* CAUTION: we're not using here the
213            modest_conf_listen_to_namespace because we know that there
214            are other parts of Modest listening for this namespace, so
215            we'll receive the notifications anyway. We basically do not
216            use it because there is no easy way to do the
217            modest_conf_forget_namespace */
218         ModestConf *conf = modest_runtime_get_conf ();
219         g_signal_connect (G_OBJECT(conf),
220                           "key_changed",
221                           G_CALLBACK (on_modest_conf_update_interval_changed), 
222                           NULL);
223
224         /* only force the setting of the default interval, if there are actually
225          * any accounts */
226         acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
227         if (acc_names) {
228                 /* Get the initial update interval from gconf: */
229                 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
230                                                        MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
231                 modest_account_mgr_free_account_names (acc_names);
232         }
233
234         
235 #ifdef MODEST_HAVE_ABOOK
236         /* initialize the addressbook */
237         if (!osso_abook_init (&argc, &argv, osso_context)) {
238                 g_printerr ("modest: failed to initialized addressbook\n");
239                 return FALSE;
240         }
241 #endif /*MODEST_HAVE_ABOOK*/
242
243         return TRUE;
244 }
245
246 gboolean
247 modest_platform_uninit (void)
248 {
249         osso_context_t *osso_context =
250                 modest_maemo_utils_get_osso_context ();
251         if (osso_context)
252                 osso_deinitialize (osso_context);
253
254         return TRUE;
255 }
256
257
258
259
260 TnyDevice*
261 modest_platform_get_new_device (void)
262 {
263         return TNY_DEVICE (tny_maemo_conic_device_new ());
264 }
265
266 gchar*
267 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
268                                     gchar **effective_mime_type)
269 {
270         GString *mime_str = NULL;
271         gchar *icon_name  = NULL;
272         gchar **icons, **cursor;
273         
274         if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) 
275                 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
276         else {
277                 mime_str = g_string_new (mime_type);
278                 g_string_ascii_down (mime_str);
279         }
280         
281         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
282         
283         for (cursor = icons; cursor; ++cursor) {
284                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
285                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
286                         icon_name = g_strdup ("qgn_list_messagin");
287                         break;
288                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
289                         icon_name = g_strdup (*cursor);
290                         break;
291                 }
292         }
293         g_strfreev (icons);
294
295         if (effective_mime_type)
296                 *effective_mime_type = g_string_free (mime_str, FALSE);
297         else
298                 g_string_free (mime_str, TRUE);
299         
300         return icon_name;
301 }
302
303
304 static gboolean
305 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
306 {
307         GError *err = NULL;
308         gboolean result;
309
310         g_return_val_if_fail (uri, FALSE);
311         
312         result = hildon_uri_open (uri, action, &err);
313         if (!result) {
314                 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
315                             uri, action,  err && err->message ? err->message : "unknown error");
316                 if (err)
317                         g_error_free (err);
318         }
319         return result;
320 }
321
322
323
324 gboolean 
325 modest_platform_activate_uri (const gchar *uri)
326 {
327         HildonURIAction *action;
328         gboolean result = FALSE;
329         GSList *actions, *iter = NULL;
330         
331         g_return_val_if_fail (uri, FALSE);
332         if (!uri)
333                 return FALSE;
334
335         /* don't try to activate file: uri's -- they might confuse the user,
336          * and/or might have security implications */
337         if (!g_str_has_prefix (uri, "file:")) {
338                 
339                 actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
340                 
341                 for (iter = actions; iter; iter = g_slist_next (iter)) {
342                         action = (HildonURIAction*) iter->data;
343                         if (action && strcmp (hildon_uri_action_get_service (action),
344                                               "com.nokia.modest") == 0) {
345                                 result = checked_hildon_uri_open (uri, action);
346                                 break;
347                         }
348                 }
349                 
350                 /* if we could not open it with email, try something else */
351                 if (!result)
352                         result = checked_hildon_uri_open (uri, NULL);   
353         } 
354         
355         if (!result) {
356                 ModestWindow *parent =
357                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
358                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
359                                                 _("mcen_ib_unsupported_link"));
360                 g_warning ("%s: cannot open uri '%s'", __FUNCTION__,uri);
361         } 
362         
363         return result;
364 }
365
366 gboolean 
367 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
368 {
369         gint result = 0;
370         DBusConnection *con;
371         gchar *uri_path = NULL;
372         
373         uri_path = gnome_vfs_get_uri_from_local_path (path);    
374         con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
375         
376         if (mime_type)
377                 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
378         if (result != 1)
379                 result = hildon_mime_open_file (con, uri_path);
380         if (result != 1)
381                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"), FALSE);
382         
383         return result != 1;
384 }
385
386 typedef struct  {
387         GSList *actions;
388         gchar  *uri;
389 } ModestPlatformPopupInfo;
390
391 static gboolean
392 delete_uri_popup (GtkWidget *menu,
393                   GdkEvent *event,
394                   gpointer userdata)
395 {
396         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
397
398         g_free (popup_info->uri);
399         hildon_uri_free_actions (popup_info->actions);
400
401         return FALSE;
402 }
403
404 static void
405 activate_uri_popup_item (GtkMenuItem *menu_item,
406                          gpointer userdata)
407 {
408         GSList *node;
409         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
410         const gchar* action_name;
411
412         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
413         if (!action_name) {
414                 g_printerr ("modest: no action name defined\n");
415                 return;
416         }
417
418         /* special handling for the copy menu item -- copy the uri to the clipboard */
419         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
420         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
421                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
422                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
423
424                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
425                         action_name += strlen ("mailto:");
426                 
427                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
428                 modest_platform_information_banner (NULL, NULL, _CS("ecoc_ib_edwin_copied"));
429                 return; /* we're done */
430         }
431         
432         /* now, the real uri-actions... */
433         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
434                 HildonURIAction *action = (HildonURIAction *) node->data;
435                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
436                         if (!checked_hildon_uri_open (popup_info->uri, action)) {
437                                 ModestWindow *parent =
438                                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
439                                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
440                                                                 _("mcen_ib_unsupported_link"));
441                         }
442                         break;
443                 }
444         }
445 }
446
447 gboolean 
448 modest_platform_show_uri_popup (const gchar *uri)
449 {
450         GSList *actions_list;
451
452         if (uri == NULL)
453                 return FALSE;
454         
455         actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
456         if (actions_list) {
457
458                 GtkWidget *menu = gtk_menu_new ();
459                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
460
461                 /* don't add actions for file: uri's -- they might confuse the user,
462                  * and/or might have security implications
463                  * we still allow to copy the url though
464                  */
465                 if (!g_str_has_prefix (uri, "file:")) {                 
466                 
467                         GSList *node;                   
468                         popup_info->actions = actions_list;
469                         popup_info->uri = g_strdup (uri);
470
471                         for (node = actions_list; node != NULL; node = g_slist_next (node)) {
472                                 GtkWidget *menu_item;
473                                 const gchar *action_name;
474                                 const gchar *translation_domain;
475                                 HildonURIAction *action = (HildonURIAction *) node->data;
476                                 action_name = hildon_uri_action_get_name (action);
477                                 translation_domain = hildon_uri_action_get_translation_domain (action);
478                                 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
479                                 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
480                                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
481                                                   popup_info);
482
483                                 if (hildon_uri_is_default_action (action, NULL)) {
484                                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
485                                 } else {
486                                         gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
487                                 }
488                                 gtk_widget_show (menu_item);
489                         }
490                 }
491
492
493                 /* and what to do when the link is deleted */
494                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
495                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
496
497         } else {
498                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
499         }
500
501         return TRUE;
502 }
503
504
505 GdkPixbuf*
506 modest_platform_get_icon (const gchar *name, guint icon_size)
507 {
508         GError *err = NULL;
509         GdkPixbuf* pixbuf = NULL;
510         GtkIconTheme *current_theme = NULL;
511
512         g_return_val_if_fail (name, NULL);
513
514         /* strlen == 0 is not really an error; it just
515          * means the icon is not available
516          */
517         if (!name || strlen(name) == 0)
518                 return NULL;
519
520         current_theme = gtk_icon_theme_get_default ();
521         pixbuf = gtk_icon_theme_load_icon (current_theme, name, icon_size,
522                                            GTK_ICON_LOOKUP_NO_SVG,
523                                            &err);
524         if (!pixbuf) {
525                 g_printerr ("modest: error loading theme icon '%s': %s\n",
526                             name, err->message);
527                 g_error_free (err);
528         } 
529         return pixbuf;
530 }
531
532 const gchar*
533 modest_platform_get_app_name (void)
534 {
535         return _("mcen_ap_name");
536 }
537
538 static void
539 entry_insert_text (GtkEditable *editable,
540                    const gchar *text,
541                    gint         length,
542                    gint        *position,
543                    gpointer     data)
544 {
545         gchar *chars;
546         gint chars_length;
547
548         chars = gtk_editable_get_chars (editable, 0, -1);
549         chars_length = g_utf8_strlen (chars, -1);
550         g_free (chars);
551
552         /* Show WID-INF036 */
553         if (chars_length >= 20) {
554                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
555                                                  _CS("ckdg_ib_maximum_characters_reached"));
556         } else {
557                 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
558                         /* Show an error */
559                         gchar *tmp, *msg;
560
561                         tmp = g_strndup (folder_name_forbidden_chars,
562                                          FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
563                         msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
564                         hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)),
565                                                          NULL, msg);
566                         g_free (msg);
567                         g_free (tmp);
568                 } else {
569                         if (length >= 20) {
570                                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
571                                                                  _CS("ckdg_ib_maximum_characters_reached"));
572                         }
573                         /* Write the text in the entry if it's valid */
574                         g_signal_handlers_block_by_func (editable,
575                                                          (gpointer) entry_insert_text, data);
576                         gtk_editable_insert_text (editable, text, length, position);
577                         g_signal_handlers_unblock_by_func (editable,
578                                                            (gpointer) entry_insert_text, data);
579                 }
580         }
581         /* Do not allow further processing */
582         g_signal_stop_emission_by_name (editable, "insert_text");
583 }
584
585 static void
586 entry_changed (GtkEditable *editable,
587                gpointer     user_data)
588 {
589         gchar *chars;
590         GtkWidget *ok_button;
591         GList *buttons;
592
593         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
594         ok_button = GTK_WIDGET (buttons->data);
595
596         chars = gtk_editable_get_chars (editable, 0, -1);
597         g_return_if_fail (chars != NULL);
598
599
600         if (g_utf8_strlen (chars,-1) >= 20) {
601                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
602                                                  _CS("ckdg_ib_maximum_characters_reached"));
603         }
604         gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
605
606         /* Free */
607         g_list_free (buttons);
608         g_free (chars);
609 }
610
611
612
613 static void
614 on_response (GtkDialog *dialog,
615              gint response,
616              gpointer user_data)
617 {
618         GtkWidget *entry, *picker;
619         TnyFolderStore *parent;
620         const gchar *new_name;
621         gboolean exists;
622
623         if (response != GTK_RESPONSE_ACCEPT)
624                 return;
625         
626         /* Get entry */
627         entry = g_object_get_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ENTRY);
628         picker = g_object_get_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ACCOUNT_PICKER);
629         
630         parent = TNY_FOLDER_STORE (user_data);
631         new_name = gtk_entry_get_text (GTK_ENTRY (entry));
632         exists = FALSE;
633         
634         if (picker != NULL) {
635
636                 parent = g_object_get_data (G_OBJECT (picker), FOLDER_PICKER_CURRENT_FOLDER);
637         }
638
639         /* Look for another folder with the same name */
640         if (modest_tny_folder_has_subfolder_with_name (parent, 
641                                                        new_name,
642                                                        TRUE)) {
643                 exists = TRUE;
644         }
645         
646         if (!exists) {
647                 if (TNY_IS_ACCOUNT (parent) &&
648                     modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
649                     modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
650                                                                          new_name)) {
651                         exists = TRUE;
652                 }
653         }
654         
655         if (exists) {
656                 
657                 /* Show an error */
658                 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), 
659                                                 NULL, _CS("ckdg_ib_folder_already_exists"));
660                 /* Select the text */
661                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
662                 gtk_widget_grab_focus (entry);
663                 /* Do not close the dialog */
664                 g_signal_stop_emission_by_name (dialog, "response");
665         }
666
667 }
668
669 typedef struct _FolderChooserData {
670         TnyFolderStore *store;
671         GtkWidget *dialog;
672 } FolderChooserData;
673
674 static void
675 folder_chooser_activated (ModestFolderView *folder_view,
676                           TnyFolderStore *folder,
677                           FolderChooserData *userdata)
678 {
679         userdata->store = folder;
680         gtk_dialog_response (GTK_DIALOG (userdata->dialog), GTK_RESPONSE_OK);
681 }
682
683 static TnyFolderStore *
684 folder_chooser_dialog_run (ModestFolderView *original)
685 {
686         GtkWidget *folder_view;
687         FolderChooserData userdata = {NULL, NULL};
688         GtkWidget *pannable;
689         const gchar *visible_id = NULL;
690
691         userdata.dialog = hildon_dialog_new ();
692         pannable = hildon_pannable_area_new ();
693         folder_view = modest_platform_create_folder_view (NULL);
694
695         gtk_window_set_title (GTK_WINDOW (userdata.dialog), _FM("ckdg_ti_change_folder"));
696
697         modest_folder_view_copy_model (MODEST_FOLDER_VIEW (original), 
698                                        MODEST_FOLDER_VIEW (folder_view));
699
700         visible_id = 
701                 modest_folder_view_get_account_id_of_visible_server_account (MODEST_FOLDER_VIEW(original));
702         modest_folder_view_set_account_id_of_visible_server_account (MODEST_FOLDER_VIEW(folder_view),
703                                                                      visible_id);
704
705         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (userdata.dialog)->vbox), pannable);
706         gtk_container_add (GTK_CONTAINER (pannable), folder_view);
707         gtk_widget_set_size_request (pannable, -1, 320);
708
709         gtk_widget_show (folder_view);
710         gtk_widget_show (pannable);
711         gtk_widget_show (userdata.dialog);
712         g_signal_connect (G_OBJECT (folder_view), "folder-activated", 
713                           G_CALLBACK (folder_chooser_activated), 
714                           (gpointer) &userdata);
715
716         gtk_dialog_run (GTK_DIALOG (userdata.dialog));
717         gtk_widget_destroy (userdata.dialog);
718
719         return userdata.store;
720 }
721
722 static gchar *
723 folder_store_get_display_name (TnyFolderStore *store)
724 {
725         if (TNY_IS_ACCOUNT (store)) {
726                 return g_strdup (tny_account_get_name (TNY_ACCOUNT (store)));
727         } else {
728                 gchar *fname;
729                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
730
731                 fname = g_strdup (tny_folder_get_name (TNY_FOLDER (store)));
732                 type = tny_folder_get_folder_type (TNY_FOLDER (store));
733                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (store)) ||
734                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (store))) {
735                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (store));
736                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
737                                 g_free (fname);
738                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
739                         }
740                 } else {
741                         /* Sometimes an special folder is reported by the server as
742                            NORMAL, like some versions of Dovecot */
743                         if (type == TNY_FOLDER_TYPE_NORMAL ||
744                             type == TNY_FOLDER_TYPE_UNKNOWN) {
745                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (store));
746                         }
747                 }
748
749                 if (type == TNY_FOLDER_TYPE_INBOX) {
750                         g_free (fname);
751                         fname = g_strdup (_("mcen_me_folder_inbox"));
752                 }
753                 return fname;
754         }
755 }
756
757 static void
758 folder_picker_set_store (GtkButton *button, TnyFolderStore *store)
759 {
760         gchar *name;
761
762         if (store == NULL) {
763                 g_object_set_data (G_OBJECT (button), FOLDER_PICKER_CURRENT_FOLDER, NULL);
764         } else {
765                 GdkPixbuf *pixbuf;
766                 const gchar *icon_name = NULL;
767
768                 g_object_ref (store);
769                 g_object_set_data_full (G_OBJECT (button), FOLDER_PICKER_CURRENT_FOLDER, 
770                                         store, (GDestroyNotify) g_object_unref);
771                 name = folder_store_get_display_name (store);
772                 hildon_button_set_value (HILDON_BUTTON (button), name);
773                 g_free (name);
774
775                 /* Select icon */
776                 if (TNY_IS_ACCOUNT (store)) {
777                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (store)))
778                             icon_name = MODEST_FOLDER_ICON_LOCAL_FOLDERS;
779                         else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (store)))
780                                 icon_name = MODEST_FOLDER_ICON_MMC;
781                         else
782                                 icon_name = MODEST_FOLDER_ICON_ACCOUNT;
783                 } else {
784                         if (modest_tny_folder_is_remote_folder (TNY_FOLDER (store))) {
785                                 TnyFolderType type = modest_tny_folder_guess_folder_type (TNY_FOLDER (store));
786                                 switch (type) {
787                                 case TNY_FOLDER_TYPE_INBOX:
788                                         icon_name = MODEST_FOLDER_ICON_INBOX;
789                                         break;
790                                 default:
791                                         icon_name = MODEST_FOLDER_ICON_ACCOUNT;
792                                 }
793                         } else if (modest_tny_folder_is_local_folder (TNY_FOLDER (store)))
794                                 icon_name = MODEST_FOLDER_ICON_NORMAL;
795                         else if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (store)))
796                                 icon_name = MODEST_FOLDER_ICON_MMC_FOLDER;
797                 }
798
799                 /* Set icon */
800                 pixbuf = modest_platform_get_icon (icon_name, MODEST_ICON_SIZE_SMALL);
801
802                 if (pixbuf) {
803                         hildon_button_set_image (HILDON_BUTTON (button),
804                                                  gtk_image_new_from_pixbuf (pixbuf));
805                         g_object_unref (pixbuf);
806                 }
807         }
808 }
809
810 static void
811 folder_picker_clicked (GtkButton *button,
812                        ModestFolderView *folder_view)
813 {
814         TnyFolderStore *store;
815
816         store = folder_chooser_dialog_run (folder_view);
817         if (store) {
818                 folder_picker_set_store (GTK_BUTTON (button), store);
819         }
820 }
821
822 static GtkWidget *
823 folder_picker_new (ModestFolderView *folder_view, TnyFolderStore *suggested)
824 {
825         GtkWidget *button;
826
827         button = hildon_button_new (MODEST_EDITABLE_SIZE,
828                                     HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
829
830         hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 1.0);
831
832         if (suggested) {
833                 folder_picker_set_store (GTK_BUTTON (button), suggested);
834         }
835
836         g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (folder_picker_clicked), folder_view);
837
838         return button;
839 }
840
841
842 static gint
843 modest_platform_run_folder_common_dialog (GtkWindow *parent_window,
844                                           TnyFolderStore *suggested_parent,
845                                           const gchar *dialog_title,
846                                           const gchar *label_text,
847                                           const gchar *suggested_name,
848                                           gboolean show_name,
849                                           gboolean show_parent,
850                                           gchar **folder_name,
851                                           TnyFolderStore **parent)
852 {
853         GtkWidget *accept_btn = NULL; 
854         GtkWidget *dialog, *entry = NULL, *label_entry = NULL,  *label_location = NULL, *hbox;
855         GtkWidget *account_picker = NULL;
856         GList *buttons = NULL;
857         gint result;
858         GtkSizeGroup *sizegroup;
859         ModestFolderView *folder_view;
860         ModestWindow *folder_window;
861         ModestHildon2WindowMgr *window_mgr;
862
863         window_mgr = (ModestHildon2WindowMgr *) modest_runtime_get_window_mgr ();
864         folder_window = modest_hildon2_window_mgr_get_folder_window (window_mgr);
865         g_return_val_if_fail (MODEST_IS_FOLDER_WINDOW (folder_window), GTK_RESPONSE_NONE);
866
867         folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (folder_window));
868
869         /* Ask the user for the folder name */
870         dialog = gtk_dialog_new_with_buttons (dialog_title,
871                                               parent_window,
872                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
873                                               _FM("ckdg_bd_new_folder_dialog_ok"),
874                                               GTK_RESPONSE_ACCEPT,
875                                               NULL);
876
877         /* Add accept button (with unsensitive handler) */
878         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
879         accept_btn = GTK_WIDGET (buttons->data);
880
881         sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
882
883         if (show_name) {
884                 label_entry = gtk_label_new (label_text);
885                 entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
886                 gtk_entry_set_max_length (GTK_ENTRY (entry), 20);
887
888                 gtk_misc_set_alignment (GTK_MISC (label_entry), 0.0, 0.5);
889                 gtk_size_group_add_widget (sizegroup, label_entry);
890
891                 if (suggested_name)
892                         gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
893                 else
894                         gtk_entry_set_text (GTK_ENTRY (entry), _FM("ckdg_va_new_folder_name_stub"));
895                 gtk_entry_set_width_chars (GTK_ENTRY (entry),
896                                            MAX (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1),
897                                                 g_utf8_strlen (_FM("ckdg_va_new_folder_name_stub"), -1)));
898                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
899         }
900
901         if (show_parent) {
902
903                 label_location = gtk_label_new (_FM("ckdg_fi_new_folder_location"));
904
905                 gtk_misc_set_alignment (GTK_MISC (label_location), 0.0, 0.5);
906                 gtk_size_group_add_widget (sizegroup, label_location);
907
908                 account_picker = folder_picker_new (folder_view, suggested_parent);
909         }
910
911         g_object_unref (sizegroup);
912
913         /* Connect to the response method to avoid closing the dialog
914            when an invalid name is selected*/
915         g_signal_connect (dialog,
916                           "response",
917                           G_CALLBACK (on_response),
918                           suggested_parent);
919
920         if (show_name) {
921                 /* Track entry changes */
922                 g_signal_connect (entry,
923                                   "insert-text",
924                                   G_CALLBACK (entry_insert_text),
925                                   dialog);
926                 g_signal_connect (entry,
927                                   "changed",
928                                   G_CALLBACK (entry_changed),
929                                   dialog);
930         }
931
932
933         /* Some locales like pt_BR need this to get the full window
934            title shown */
935         gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1);
936
937         /* Create the hbox */
938         if (show_name) {
939                 hbox = gtk_hbox_new (FALSE, 12);
940                 gtk_box_pack_start (GTK_BOX (hbox), label_entry, FALSE, FALSE, 0);
941                 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
942
943                 /* Add hbox to dialog */
944                 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
945                                     hbox, FALSE, FALSE, 0);
946                 g_object_set_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ENTRY, entry);
947         }
948
949         if (show_parent) {
950                 hbox = gtk_hbox_new (FALSE, 12);
951                 gtk_box_pack_start (GTK_BOX (hbox), label_location, FALSE, FALSE, 0);
952                 gtk_box_pack_start (GTK_BOX (hbox), account_picker, TRUE, TRUE, 0);
953
954                 /* Add hbox to dialog */
955                 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
956                                     hbox, FALSE, FALSE, 0);
957                 g_object_set_data (G_OBJECT (dialog), COMMON_FOLDER_DIALOG_ACCOUNT_PICKER, account_picker);
958         }
959         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
960                                      GTK_WINDOW (dialog), parent_window);
961         gtk_widget_show_all (GTK_WIDGET(dialog));
962
963         result = gtk_dialog_run (GTK_DIALOG(dialog));
964         if (result == GTK_RESPONSE_ACCEPT) {
965                 if (show_name)
966                         *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
967                 if (show_parent) {
968                         *parent = g_object_get_data (G_OBJECT (account_picker), FOLDER_PICKER_CURRENT_FOLDER);
969                         if (*parent)
970                                 g_object_ref (*parent);
971                 }
972         }
973
974         gtk_widget_destroy (dialog);
975
976         while (gtk_events_pending ())
977                 gtk_main_iteration ();
978
979         return result;
980 }
981
982 gint
983 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
984                                        TnyFolderStore *suggested_folder,
985                                        gchar *suggested_name,
986                                        gchar **folder_name,
987                                        TnyFolderStore **parent_folder)
988 {
989         gchar *real_suggested_name = NULL;
990         gint result;
991         ModestTnyAccountStore *acc_store;
992         TnyAccount *account;
993
994         if(suggested_name == NULL)
995         {
996                 const gchar *default_name = _FM("ckdg_va_new_folder_name_stub");
997                 unsigned int i;
998
999                 for(i = 0; i < 100; ++ i) {
1000                         gboolean exists = FALSE;
1001
1002                         if (i == 0)
1003                                 real_suggested_name = g_strdup (default_name);
1004                         else
1005                                 real_suggested_name = g_strdup_printf ("%s(%d)",
1006                                                                        _FM("ckdg_va_new_folder_name_stub"),
1007                                                                        i);
1008                         exists = modest_tny_folder_has_subfolder_with_name (suggested_folder,
1009                                                                             real_suggested_name,
1010                                                                             TRUE);
1011
1012                         if (!exists)
1013                                 break;
1014
1015                         g_free (real_suggested_name);
1016                 }
1017
1018                 /* Didn't find a free number */
1019                 if (i == 100)
1020                         real_suggested_name = g_strdup (default_name);
1021         } else {
1022                 real_suggested_name = suggested_name;
1023         }
1024
1025         /* In hildon 2.2 we always suggest the archive folder as parent */
1026         acc_store = modest_runtime_get_account_store ();
1027         account = modest_tny_account_store_get_mmc_folders_account (acc_store);
1028         if (account) {
1029                 suggested_folder = (TnyFolderStore *)
1030                         modest_tny_account_get_special_folder (account, 
1031                                                                TNY_FOLDER_TYPE_ARCHIVE);
1032                 g_object_unref (account);
1033                 account = NULL;
1034         }
1035
1036         /* If there is not archive folder then fallback to local folders account */ 
1037         if (!suggested_folder)
1038                 suggested_folder = (TnyFolderStore *)
1039                         modest_tny_account_store_get_local_folders_account (acc_store);
1040
1041         result = modest_platform_run_folder_common_dialog (parent_window, 
1042                                                            suggested_folder,
1043                                                            _HL("ckdg_ti_new_folder"),
1044                                                            _FM("ckdg_fi_new_folder_name"),
1045                                                            real_suggested_name,
1046                                                            TRUE,
1047                                                            TRUE,
1048                                                            folder_name,
1049                                                            parent_folder);
1050
1051         if (suggested_name == NULL)
1052                 g_free(real_suggested_name);
1053
1054         return result;
1055 }
1056
1057 gint
1058 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
1059                                           TnyFolderStore *parent_folder,
1060                                           const gchar *suggested_name,
1061                                           gchar **folder_name)
1062 {
1063         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
1064
1065         return modest_platform_run_folder_common_dialog (parent_window, 
1066                                                          parent_folder,
1067                                                          _HL("ckdg_ti_rename_folder"),
1068                                                          _HL("ckdg_fi_rename_name"),
1069                                                          suggested_name,
1070                                                          TRUE,
1071                                                          FALSE,
1072                                                          folder_name,
1073                                                          NULL);
1074 }
1075
1076
1077
1078 static void
1079 on_destroy_dialog (GtkWidget *dialog)
1080 {
1081         /* This could happen when the dialogs get programatically
1082            hidden or destroyed (for example when closing the
1083            application while a dialog is being shown) */
1084         if (!GTK_IS_WIDGET (dialog))
1085                 return;
1086
1087         gtk_widget_destroy (dialog);
1088
1089         if (gtk_events_pending ())
1090                 gtk_main_iteration ();
1091 }
1092
1093 gint
1094 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
1095                                          const gchar *message)
1096 {
1097         GtkWidget *dialog;
1098         gint response;
1099         
1100         dialog = hildon_note_new_confirmation (parent_window, message);
1101         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1102                                      GTK_WINDOW (dialog), parent_window);
1103
1104         response = gtk_dialog_run (GTK_DIALOG (dialog));
1105
1106         on_destroy_dialog (dialog);
1107
1108         return response;
1109 }
1110
1111 gint
1112 modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window,
1113                                                       const gchar *message,
1114                                                       const gchar *button_accept,
1115                                                       const gchar *button_cancel)
1116 {
1117         GtkWidget *dialog;
1118         gint response;
1119         
1120         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
1121                                                            button_accept, GTK_RESPONSE_ACCEPT,
1122                                                            button_cancel, GTK_RESPONSE_CANCEL,
1123                                                            NULL);
1124
1125         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1126                                      GTK_WINDOW (dialog), parent_window);
1127
1128         response = gtk_dialog_run (GTK_DIALOG (dialog));
1129
1130         on_destroy_dialog (dialog);
1131
1132         return response;
1133 }
1134         
1135 void
1136 modest_platform_run_information_dialog (GtkWindow *parent_window,
1137                                         const gchar *message,
1138                                         gboolean block)
1139 {
1140         GtkWidget *note;
1141         
1142         note = hildon_note_new_information (parent_window, message);
1143         if (block)
1144                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1145                                              GTK_WINDOW (note), parent_window);
1146         
1147         if (block) {
1148                 gtk_dialog_run (GTK_DIALOG (note));
1149         
1150                 on_destroy_dialog (note);
1151         } else {
1152                 g_signal_connect_swapped (note,
1153                                           "response", 
1154                                           G_CALLBACK (on_destroy_dialog),
1155                                           note);
1156
1157                 gtk_widget_show_all (note);
1158         }
1159 }
1160
1161 typedef struct _ConnectAndWaitData {
1162         GMutex *mutex;
1163         GMainLoop *wait_loop;
1164         gboolean has_callback;
1165         gulong handler;
1166 } ConnectAndWaitData;
1167
1168
1169 static void
1170 quit_wait_loop (TnyAccount *account,
1171                 ConnectAndWaitData *data) 
1172 {
1173         /* Set the has_callback to TRUE (means that the callback was
1174            executed and wake up every code waiting for cond to be
1175            TRUE */
1176         g_mutex_lock (data->mutex);
1177         data->has_callback = TRUE;
1178         if (data->wait_loop)
1179                 g_main_loop_quit (data->wait_loop);
1180         g_mutex_unlock (data->mutex);
1181 }
1182
1183 static void
1184 on_connection_status_changed (TnyAccount *account, 
1185                               TnyConnectionStatus status,
1186                               gpointer user_data)
1187 {
1188         TnyConnectionStatus conn_status;
1189         ConnectAndWaitData *data;
1190                         
1191         /* Ignore if reconnecting or disconnected */
1192         conn_status = tny_account_get_connection_status (account);
1193         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1194             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1195                 return;
1196
1197         /* Remove the handler */
1198         data = (ConnectAndWaitData *) user_data;
1199         g_signal_handler_disconnect (account, data->handler);
1200
1201         /* Quit from wait loop */
1202         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1203 }
1204
1205 static void
1206 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1207                                     gboolean canceled, 
1208                                     GError *err, 
1209                                     gpointer user_data)
1210 {
1211         /* Quit from wait loop */
1212         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1213 }
1214
1215 gboolean 
1216 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1217                                   TnyAccount *account)
1218 {
1219         ConnectAndWaitData *data = NULL;
1220         gboolean device_online;
1221         TnyDevice *device;
1222         TnyConnectionStatus conn_status;
1223         gboolean user_requested;
1224         
1225         device = modest_runtime_get_device();
1226         device_online = tny_device_is_online (device);
1227
1228         /* Whether the connection is user requested or automatically
1229            requested, for example via D-Bus */
1230         user_requested = (parent_window) ? TRUE : FALSE;
1231
1232         /* If there is no account check only the device status */
1233         if (!account) {
1234                 if (device_online)
1235                         return TRUE;
1236                 else
1237                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1238                                                                NULL, user_requested);
1239         }
1240
1241         /* Return if the account is already connected */
1242         conn_status = tny_account_get_connection_status (account);
1243         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1244                 return TRUE;
1245
1246         /* Create the helper */
1247         data = g_slice_new0 (ConnectAndWaitData);
1248         data->mutex = g_mutex_new ();
1249         data->has_callback = FALSE;
1250
1251         /* Connect the device */
1252         if (!device_online) {
1253                 /* Track account connection status changes */
1254                 data->handler = g_signal_connect (account, "connection-status-changed",
1255                                                   G_CALLBACK (on_connection_status_changed),
1256                                                   data);
1257                 /* Try to connect the device */
1258                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1259                                                                 NULL, user_requested);
1260
1261                 /* If the device connection failed then exit */
1262                 if (!device_online && data->handler)
1263                         goto frees;
1264         } else {
1265                 /* Force a reconnection of the account */
1266                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1267                                               on_tny_camel_account_set_online_cb, data);
1268         }
1269
1270         /* Wait until the callback is executed */
1271         g_mutex_lock (data->mutex);
1272         if (!data->has_callback) {
1273                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1274                 gdk_threads_leave ();
1275                 g_mutex_unlock (data->mutex);
1276                 g_main_loop_run (data->wait_loop);
1277                 g_mutex_lock (data->mutex);
1278                 gdk_threads_enter ();
1279         }
1280         g_mutex_unlock (data->mutex);
1281
1282  frees:
1283         if (data) {
1284                 if (g_signal_handler_is_connected (account, data->handler))
1285                         g_signal_handler_disconnect (account, data->handler);
1286                 g_mutex_free (data->mutex);
1287                 g_main_loop_unref (data->wait_loop);
1288                 g_slice_free (ConnectAndWaitData, data);
1289         }
1290
1291         conn_status = tny_account_get_connection_status (account);
1292         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1293 }
1294
1295 gboolean 
1296 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1297 {
1298         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1299                 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) {
1300                         /* This must be a maildir account, which does not require a connection: */
1301                         return TRUE;
1302                 }
1303         }
1304
1305         return modest_platform_connect_and_wait (parent_window, account);
1306 }
1307
1308 gboolean 
1309 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1310 {
1311         if (!folder_store)
1312                 return TRUE; /* Maybe it is something local. */
1313                 
1314         gboolean result = TRUE;
1315         if (TNY_IS_FOLDER (folder_store)) {
1316                 /* Get the folder's parent account: */
1317                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1318                 if (account != NULL) {
1319                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1320                         g_object_unref (account);
1321                 }
1322         } else if (TNY_IS_ACCOUNT (folder_store)) {
1323                 /* Use the folder store as an account: */
1324                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1325         }
1326
1327         return result;
1328 }
1329
1330 GtkWidget *
1331 modest_platform_create_sort_dialog       (GtkWindow *parent_window)
1332 {
1333         GtkWidget *dialog;
1334
1335         dialog = modest_hildon2_sort_dialog_new (parent_window);
1336
1337         return dialog;
1338 }
1339
1340
1341 gboolean 
1342 modest_platform_set_update_interval (guint minutes)
1343 {
1344 #ifdef MODEST_HAVE_LIBALARM
1345         
1346         ModestConf *conf = modest_runtime_get_conf ();
1347         if (!conf)
1348                 return FALSE;
1349                 
1350         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1351
1352         /* Delete any existing alarm,
1353          * because we will replace it: */
1354         if (alarm_cookie) {
1355                 if (alarmd_event_del(alarm_cookie) != 0)
1356                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1357                 alarm_cookie = 0;
1358                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1359         }
1360         
1361         /* 0 means no updates: */
1362         if (minutes == 0)
1363                 return TRUE;
1364         
1365      
1366         /* Register alarm: */
1367         
1368         /* Set the interval in alarm_event_t structure: */
1369         alarm_event_t *event = alarm_event_create ();
1370         alarm_event_add_actions (event, 1);
1371         alarm_action_t *action = alarm_event_get_action (event, 0);
1372         alarm_event_set_alarm_appid (event, MODEST_ALARMD_APPID);
1373         event->alarm_time = minutes * 60; /* seconds */
1374         
1375         /* Set recurrence every few minutes: */
1376         event->recur_secs = minutes*60;
1377         event->recur_count = -1; /* Means infinite */
1378
1379         /* Specify what should happen when the alarm happens:
1380          * It should call this D-Bus method: */
1381          
1382         action->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1383         action->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1384         action->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1385         action->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1386         action->flags = ALARM_ACTION_WHEN_TRIGGERED | ALARM_ACTION_TYPE_DBUS | ALARM_ACTION_DBUS_USE_ACTIVATION;
1387
1388         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1389          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1390          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1391          * This is why we want to use the Alarm API instead of just g_timeout_add().
1392          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1393          * ALARM_EVENT_CONNECTED will prevent the alarm from being called in case that the device is offline
1394          */
1395         event->flags = ALARM_EVENT_CONNECTED;
1396         
1397         alarm_cookie = alarmd_event_add (event);
1398
1399         /* now, free it */
1400         alarm_event_delete (event);
1401         
1402         /* Store the alarm ID in GConf, so we can remove it later:
1403          * This is apparently valid between application instances. */
1404         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1405         
1406         if (!alarm_cookie) {
1407             /* Error */
1408             g_debug ("Error setting alarm event. \n");
1409             
1410             return FALSE;
1411         }
1412 #endif /* MODEST_HAVE_LIBALARM */       
1413         return TRUE;
1414 }
1415
1416 void
1417 modest_platform_push_email_notification(void)
1418 {
1419         gboolean screen_on, app_in_foreground;
1420
1421         /* Get the window status */
1422         app_in_foreground = hildon_program_get_is_topmost (hildon_program_get_instance ());
1423
1424         screen_on = modest_window_mgr_screen_is_on (modest_runtime_get_window_mgr ());
1425
1426         /* If the screen is on and the app is in the
1427            foreground we don't show anything */
1428         if (!(screen_on && app_in_foreground)) {
1429
1430                 _modest_platform_play_email_tone ();
1431
1432                 /* Activate LED. This must be deactivated by
1433                    modest_platform_remove_new_mail_notifications */
1434 #ifdef MODEST_HAVE_MCE
1435                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1436                                      MCE_SERVICE,
1437                                      MCE_REQUEST_PATH,
1438                                      MCE_REQUEST_IF,
1439                                      MCE_ACTIVATE_LED_PATTERN,
1440                                      NULL,
1441                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1442                                      DBUS_TYPE_INVALID);
1443 #endif
1444         }
1445 }
1446
1447 void 
1448 modest_platform_on_new_headers_received (TnyList *header_list,
1449                                          gboolean show_visual)
1450 {
1451         g_return_if_fail (TNY_IS_LIST(header_list));
1452
1453         if (tny_list_get_length(header_list) == 0) {
1454                 g_warning ("%s: header list is empty", __FUNCTION__);
1455                 return;
1456         }
1457         
1458         if (!show_visual) {
1459                 modest_platform_push_email_notification ();
1460                 /* We do a return here to avoid indentation with an else */
1461                 return;
1462         }
1463
1464 #ifdef MODEST_HAVE_HILDON_NOTIFY
1465         HildonNotification *notification;
1466         TnyIterator *iter;
1467         GSList *notifications_list = NULL;
1468
1469         /* Get previous notifications ids */
1470         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1471                                                    MODEST_CONF_NOTIFICATION_IDS, 
1472                                                    MODEST_CONF_VALUE_INT, NULL);
1473
1474         iter = tny_list_create_iterator (header_list);
1475         while (!tny_iterator_is_done (iter)) {
1476                 gchar *url = NULL, *display_address = NULL;
1477                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1478                 TnyFolder *folder = tny_header_get_folder (header);
1479                 gboolean first_notification = TRUE;
1480                 gint notif_id;
1481                 gchar *str;
1482
1483                 display_address = tny_header_dup_from (header);
1484                 /* string is changed in-place */
1485                 modest_text_utils_get_display_address (display_address);
1486
1487                 str = tny_header_dup_subject (header);
1488                 notification = hildon_notification_new (display_address,
1489                                                         str,
1490                                                         "qgn_list_messagin",
1491                                                         "email-message");
1492                 g_free (str);
1493                 /* Create the message URL */
1494                 str = tny_header_dup_uid (header);
1495                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1496                                        str);
1497                 g_free (str);
1498
1499                 hildon_notification_add_dbus_action(notification,
1500                                                     "default",
1501                                                     "Cancel",
1502                                                     MODEST_DBUS_SERVICE,
1503                                                     MODEST_DBUS_OBJECT,
1504                                                     MODEST_DBUS_IFACE,
1505                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1506                                                     G_TYPE_STRING, url,
1507                                                     -1);
1508
1509                 /* Play sound if the user wants. Show the LED
1510                    pattern. Show and play just one */
1511                 if (G_UNLIKELY (first_notification)) {
1512                         gchar *active_profile;
1513                         gchar *mail_tone;
1514                         gchar *mail_volume;
1515                         gint mail_volume_int;
1516
1517                         first_notification = FALSE;
1518
1519                         active_profile = profile_get_profile ();
1520                         mail_tone = profile_get_value (active_profile, PROFILE_MAIL_TONE);
1521                         mail_volume = profile_get_value (active_profile, PROFILE_MAIL_VOLUME);
1522                         mail_volume_int = profile_parse_int (mail_volume);
1523
1524                         if (mail_volume_int > 0)
1525                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1526                                                                     "sound-file", mail_tone);
1527
1528                         g_free (mail_volume);
1529                         g_free (mail_tone);
1530                         g_free (active_profile);
1531
1532                         /* Set the led pattern */
1533                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1534                                                             "dialog-type", 4);
1535                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1536                                                             "led-pattern",
1537                                                             MODEST_NEW_MAIL_LIGHTING_PATTERN);                  
1538                 }
1539
1540                 /* Notify. We need to do this in an idle because this function
1541                    could be called from a thread */
1542                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1543
1544                 /* Save id in the list */
1545                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1546                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1547                 /* We don't listen for the "closed" signal, because we
1548                    don't care about if the notification was removed or
1549                    not to store the list in gconf */
1550         
1551                 /* Free & carry on */
1552                 g_free (display_address);
1553                 g_free (url);
1554                 g_object_unref (folder);
1555                 g_object_unref (header);
1556                 tny_iterator_next (iter);
1557         }
1558         g_object_unref (iter);
1559
1560         /* Save the ids */
1561         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1562                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1563
1564         g_slist_free (notifications_list);
1565         
1566 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1567 }
1568
1569 void
1570 modest_platform_remove_new_mail_notifications (gboolean only_visuals) 
1571 {
1572         if (only_visuals) {
1573 #ifdef MODEST_HAVE_MCE
1574                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1575                                      MCE_SERVICE,
1576                                      MCE_REQUEST_PATH,
1577                                      MCE_REQUEST_IF,
1578                                      MCE_DEACTIVATE_LED_PATTERN,
1579                                      NULL,
1580                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1581                                      DBUS_TYPE_INVALID);
1582 #endif
1583                 return;
1584         }
1585
1586 #ifdef MODEST_HAVE_HILDON_NOTIFY
1587         GSList *notif_list = NULL;
1588
1589         /* Get previous notifications ids */
1590         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1591                                            MODEST_CONF_NOTIFICATION_IDS, 
1592                                            MODEST_CONF_VALUE_INT, NULL);
1593
1594         while (notif_list) {
1595                 gint notif_id;
1596                 NotifyNotification *notif;
1597
1598                 /* Nasty HACK to remove the notifications, set the id
1599                    of the existing ones and then close them */
1600                 notif_id = GPOINTER_TO_INT(notif_list->data);
1601                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1602                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1603
1604                 /* Close the notification, note that some ids could be
1605                    already invalid, but we don't care because it does
1606                    not fail */
1607                 notify_notification_close(notif, NULL);
1608                 g_object_unref(notif);
1609
1610                 /* Delete the link, it's like going to the next */
1611                 notif_list = g_slist_delete_link (notif_list, notif_list);
1612         }
1613
1614         /* Save the ids */
1615         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1616                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1617
1618         g_slist_free (notif_list);
1619
1620 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1621 }
1622
1623
1624
1625 GtkWidget * 
1626 modest_platform_get_global_settings_dialog ()
1627 {
1628         return modest_hildon2_global_settings_dialog_new ();
1629 }
1630
1631 void
1632 modest_platform_show_help (GtkWindow *parent_window, 
1633                            const gchar *help_id)
1634 {
1635         return;
1636 }
1637
1638 void 
1639 modest_platform_show_search_messages (GtkWindow *parent_window)
1640 {
1641         osso_return_t result = OSSO_ERROR;
1642         
1643         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1644                                              "osso_global_search",
1645                                              "search_email", NULL, DBUS_TYPE_INVALID);
1646
1647         if (result != OSSO_OK) {
1648                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1649         }
1650 }
1651
1652 void 
1653 modest_platform_show_addressbook (GtkWindow *parent_window)
1654 {
1655         osso_return_t result = OSSO_ERROR;
1656
1657         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1658                                              "osso_addressbook",
1659                                              "top_application", NULL, DBUS_TYPE_INVALID);
1660
1661         if (result != OSSO_OK) {
1662                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1663         }
1664 }
1665
1666 GtkWidget *
1667 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1668 {
1669         GtkWidget *widget = modest_folder_view_new (query);
1670
1671         /* Show one account by default */
1672         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1673                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1674
1675         /* Restore settings */
1676         modest_widget_memory_restore (modest_runtime_get_conf(), 
1677                                       G_OBJECT (widget),
1678                                       MODEST_CONF_FOLDER_VIEW_KEY);
1679
1680         return widget;
1681 }
1682
1683 void
1684 banner_finish (gpointer data, GObject *object)
1685 {
1686         ModestWindowMgr *mgr = (ModestWindowMgr *) data;
1687         modest_window_mgr_unregister_banner (mgr);
1688         g_object_unref (mgr);
1689 }
1690
1691 void 
1692 modest_platform_information_banner (GtkWidget *parent,
1693                                     const gchar *icon_name,
1694                                     const gchar *text)
1695 {
1696         GtkWidget *banner, *banner_parent = NULL;
1697         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1698
1699         if (modest_window_mgr_get_num_windows (mgr) == 0)
1700                 return;
1701
1702         if (parent && GTK_IS_WINDOW (parent)) {
1703                 /* If the window is the active one then show the
1704                    banner on top of this window */
1705                 if (gtk_window_is_active (GTK_WINDOW (parent)))
1706                         banner_parent = parent;
1707                 /* If the window is not the topmost but it's visible
1708                    (it's minimized for example) then show the banner
1709                    with no parent */ 
1710                 else if (GTK_WIDGET_VISIBLE (parent))
1711                         banner_parent = NULL;
1712                 /* If the window is hidden (like the main window when
1713                    running in the background) then do not show
1714                    anything */
1715                 else 
1716                         return;
1717         }
1718
1719
1720         banner = hildon_banner_show_information (banner_parent, icon_name, text);
1721
1722         modest_window_mgr_register_banner (mgr);
1723         g_object_ref (mgr);
1724         g_object_weak_ref ((GObject *) banner, banner_finish, mgr);
1725 }
1726
1727 void
1728 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1729                                                  const gchar *icon_name,
1730                                                  const gchar *text,
1731                                                  gint timeout)
1732 {
1733         GtkWidget *banner;
1734
1735         if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1736                 return;
1737
1738         banner = hildon_banner_show_information (parent, icon_name, text);
1739         hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1740 }
1741
1742 GtkWidget *
1743 modest_platform_animation_banner (GtkWidget *parent,
1744                                   const gchar *animation_name,
1745                                   const gchar *text)
1746 {
1747         GtkWidget *inf_note = NULL;
1748
1749         g_return_val_if_fail (text != NULL, NULL);
1750
1751         if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1752                 return NULL;
1753
1754         /* If the parent is not visible then do not show */
1755         if (parent && !GTK_WIDGET_VISIBLE (parent))
1756                 return NULL;
1757
1758         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1759
1760         return inf_note;
1761 }
1762
1763 typedef struct
1764 {
1765         GMainLoop* loop;
1766         TnyAccount *account;
1767         gboolean is_online;
1768         gint count_tries;
1769 } CheckAccountIdleData;
1770
1771 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1772
1773 static gboolean 
1774 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1775 {
1776         gboolean stop_trying = FALSE;
1777         g_return_val_if_fail (data && data->account, FALSE);
1778         
1779         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1780                 tny_account_get_connection_status (data->account));     
1781         
1782         if (data && data->account && 
1783                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1784                  * after which the account is likely to be usable, or never likely to be usable soon: */
1785                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1786         {
1787                 data->is_online = TRUE;
1788                 
1789                 stop_trying = TRUE;
1790         } else {
1791                 /* Give up if we have tried too many times: */
1792                 if (data->count_tries >= NUMBER_OF_TRIES) {
1793                         stop_trying = TRUE;
1794                 } else {
1795                         /* Wait for another timeout: */
1796                         ++(data->count_tries);
1797                 }
1798         }
1799         
1800         if (stop_trying) {
1801                 /* Allow the function that requested this idle callback to continue: */
1802                 if (data->loop)
1803                         g_main_loop_quit (data->loop);
1804                         
1805                 if (data->account)
1806                         g_object_unref (data->account);
1807                 
1808                 return FALSE; /* Don't call this again. */
1809         } else {
1810                 return TRUE; /* Call this timeout callback again. */
1811         }
1812 }
1813
1814 /* Return TRUE immediately if the account is already online,
1815  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1816  * soon as the account is online, or FALSE if the account does 
1817  * not become online in the NUMBER_OF_TRIES seconds.
1818  * This is useful when the D-Bus method was run immediately after 
1819  * the application was started (when using D-Bus activation), 
1820  * because the account usually takes a short time to go online.
1821  * The return value is maybe not very useful.
1822  */
1823 gboolean
1824 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1825 {
1826         gboolean is_online;
1827
1828         g_return_val_if_fail (account, FALSE);
1829
1830         if (!tny_device_is_online (modest_runtime_get_device())) {
1831                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1832                 return FALSE;
1833         }
1834
1835         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1836          * so we avoid wait unnecessarily: */
1837         if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
1838                 return TRUE;
1839
1840         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1841          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1842          * we want to avoid. */
1843         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1844                 return TRUE;
1845                 
1846         /* This blocks on the result: */
1847         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1848         data->is_online = FALSE;
1849         data->account = account;
1850         g_object_ref (data->account);
1851         data->count_tries = 0;
1852                 
1853         GMainContext *context = NULL; /* g_main_context_new (); */
1854         data->loop = g_main_loop_new (context, FALSE /* not running */);
1855
1856         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1857
1858         /* This main loop will run until the idle handler has stopped it: */
1859         g_main_loop_run (data->loop);
1860
1861         g_main_loop_unref (data->loop);
1862         /* g_main_context_unref (context); */
1863
1864         is_online = data->is_online;
1865         g_slice_free (CheckAccountIdleData, data);
1866         
1867         return is_online;       
1868 }
1869
1870
1871
1872 static void
1873 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1874 {
1875         /* GTK_RESPONSE_HELP means we need to show the certificate */
1876         if (response_id == GTK_RESPONSE_APPLY) {
1877                 GtkWidget *note;
1878                 gchar *msg;
1879                 
1880                 /* Do not close the dialog */
1881                 g_signal_stop_emission_by_name (dialog, "response");
1882
1883                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1884                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1885                 gtk_dialog_run (GTK_DIALOG(note));
1886                 gtk_widget_destroy (note);
1887         }
1888 }
1889
1890
1891 gboolean
1892 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1893                                                      const gchar *certificate)
1894 {
1895         GtkWidget *note;
1896         gint response;
1897         ModestWindow *win;
1898         HildonWindowStack *stack;
1899
1900         stack = hildon_window_stack_get_default ();
1901         win = MODEST_WINDOW (hildon_window_stack_peek (stack));
1902
1903         if (!win) {
1904           g_warning ("%s: don't show dialogs if there's no window shown; assuming 'Cancel'",
1905                            __FUNCTION__);
1906                 return FALSE;
1907         }
1908
1909         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1910                                            server_name);
1911
1912         /* We use GTK_RESPONSE_APPLY because we want the button in the
1913            middle of OK and CANCEL the same as the browser does for
1914            example. With GTK_RESPONSE_HELP the view button is aligned
1915            to the left while the other two to the right */
1916         note = hildon_note_new_confirmation_add_buttons  (
1917                 NULL,
1918                 question,
1919                 _HL("wdgt_bd_yes"),     GTK_RESPONSE_OK,
1920                 _HL("wdgt_bd_view"),          GTK_RESPONSE_APPLY,   /* abusing this... */
1921                 _HL("wdgt_bd_no"), GTK_RESPONSE_CANCEL,
1922                 NULL, NULL);
1923
1924         g_signal_connect (G_OBJECT(note), "response", 
1925                           G_CALLBACK(on_cert_dialog_response),
1926                           (gpointer) certificate);
1927
1928         response = gtk_dialog_run(GTK_DIALOG(note));
1929
1930         on_destroy_dialog (note);
1931         g_free (question);
1932
1933         return response == GTK_RESPONSE_OK;
1934 }
1935
1936 gboolean
1937 modest_platform_run_alert_dialog (const gchar* prompt,
1938                                   gboolean is_question)
1939 {
1940         ModestWindow *top_win;
1941         HildonWindowStack *stack;
1942
1943         stack = hildon_window_stack_get_default ();
1944         top_win = MODEST_WINDOW (hildon_window_stack_peek (stack));
1945
1946         if (!top_win) {
1947                 g_warning ("%s: don't show dialogs if there's no window shown; assuming 'Cancel'",
1948                            __FUNCTION__);
1949                 return FALSE;
1950         }
1951
1952         gboolean retval = TRUE;
1953         if (is_question) {
1954                 /* The Tinymail documentation says that we should show Yes and No buttons,
1955                  * when it is a question.
1956                  * Obviously, we need tinymail to use more specific error codes instead,
1957                  * so we know what buttons to show. */
1958                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (top_win), 
1959                                                                               prompt));
1960                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1961                                              GTK_WINDOW (dialog), GTK_WINDOW (top_win));
1962
1963                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1964                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1965
1966                 on_destroy_dialog (dialog);
1967         } else {
1968                 /* Just show the error text and use the default response: */
1969                 modest_platform_run_information_dialog (GTK_WINDOW (top_win), 
1970                                                         prompt, FALSE);
1971         }
1972         return retval;
1973 }
1974
1975 /***************/
1976 typedef struct {
1977         GtkWindow *parent_window;
1978         ModestConnectedPerformer callback;
1979         TnyAccount *account;
1980         gpointer user_data;
1981         gchar *iap;
1982         TnyDevice *device;
1983 } OnWentOnlineInfo;
1984  
1985 static void 
1986 on_went_online_info_free (OnWentOnlineInfo *info)
1987 {
1988         /* And if we cleanup, we DO cleanup  :-)  */
1989         
1990         if (info->device)
1991                 g_object_unref (info->device);
1992         if (info->iap)
1993                 g_free (info->iap);
1994         if (info->parent_window)
1995                 g_object_unref (info->parent_window);
1996         if (info->account)
1997                 g_object_unref (info->account);
1998         
1999         g_slice_free (OnWentOnlineInfo, info);
2000         
2001         /* We're done ... */
2002         
2003         return;
2004 }
2005  
2006 static void
2007 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
2008 {
2009         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
2010  
2011         /* Now it's really time to callback to the caller. If going online didn't succeed,
2012          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
2013          * canceled will be set. Etcetera etcetera. */
2014         
2015         if (info->callback) {
2016                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2017         }
2018         
2019         /* This is our last call, we must cleanup here if we didn't yet do that */
2020         on_went_online_info_free (info);
2021         
2022         return;
2023 }
2024  
2025  
2026 static void
2027 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
2028 {
2029         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
2030         info->iap = g_strdup (iap_id);
2031         
2032         if (canceled || err || !info->account) {
2033         
2034                 /* If there's a problem or if there's no account (then that's it for us, we callback
2035                  * the caller's callback now. He'll have to handle err or canceled, of course.
2036                  * We are not really online, as the account is not really online here ... */    
2037                 
2038                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
2039                  * this info. We don't cleanup err, Tinymail does that! */
2040                 
2041                 if (info->callback) {
2042                         
2043                         /* info->account can be NULL here, this means that the user did not
2044                          * provide a nice account instance. We'll assume that the user knows
2045                          * what he's doing and is happy with just the device going online. 
2046                          * 
2047                          * We can't do magic, we don't know what account the user wants to
2048                          * see going online. So just the device goes online, end of story */
2049                         
2050                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
2051                 }
2052                 
2053         } else if (info->account) {
2054                 
2055                 /* If there's no problem and if we have an account, we'll put the account
2056                  * online too. When done, the callback of bringing the account online
2057                  * will callback the caller's callback. This is the most normal case. */
2058  
2059                 info->device = TNY_DEVICE (g_object_ref (device));
2060                 
2061                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
2062                                               on_account_went_online, info);
2063                 
2064                 /* The on_account_went_online cb frees up the info, go look if you
2065                  * don't believe me! (so we return here) */
2066                 
2067                 return;
2068         }
2069         
2070         /* We cleanup if we are not bringing the account online too */
2071         on_went_online_info_free (info);
2072  
2073         return; 
2074 }
2075         
2076 void 
2077 modest_platform_connect_and_perform (GtkWindow *parent_window, 
2078                                      gboolean force,
2079                                      TnyAccount *account, 
2080                                      ModestConnectedPerformer callback, 
2081                                      gpointer user_data)
2082 {
2083         gboolean device_online;
2084         TnyDevice *device;
2085         TnyConnectionStatus conn_status;
2086         OnWentOnlineInfo *info;
2087         
2088         device = modest_runtime_get_device();
2089         device_online = tny_device_is_online (device);
2090
2091         /* If there is no account check only the device status */
2092         if (!account) {
2093                 
2094                 if (device_online) {
2095  
2096                         /* We promise to instantly perform the callback, so ... */
2097                         if (callback) {
2098                                 callback (FALSE, NULL, parent_window, account, user_data);
2099                         }
2100                         
2101                 } else {
2102                         
2103                         info = g_slice_new0 (OnWentOnlineInfo);
2104                         
2105                         info->iap = NULL;
2106                         info->device = NULL;
2107                         info->account = NULL;
2108                 
2109                         if (parent_window)
2110                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2111                         else
2112                                 info->parent_window = NULL;
2113                         info->user_data = user_data;
2114                         info->callback = callback;
2115                 
2116                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2117                                                               force, on_conic_device_went_online, 
2118                                                               info);
2119  
2120                         /* We'll cleanup in on_conic_device_went_online */
2121                 }
2122  
2123                 /* The other code has no more reason to run. This is all that we can do for the
2124                  * caller (he should have given us a nice and clean account instance!). We
2125                  * can't do magic, we don't know what account he intends to bring online. So
2126                  * we'll just bring the device online (and await his false bug report). */
2127                 
2128                 return;
2129         }
2130  
2131         
2132         /* Return if the account is already connected */
2133         
2134         conn_status = tny_account_get_connection_status (account);
2135         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
2136  
2137                 /* We promise to instantly perform the callback, so ... */
2138                 if (callback) {
2139                         callback (FALSE, NULL, parent_window, account, user_data);
2140                 }
2141                 
2142                 return;
2143         }
2144         
2145         /* Else, we are in a state that requires that we go online before we
2146          * call the caller's callback. */
2147         
2148         info = g_slice_new0 (OnWentOnlineInfo);
2149         
2150         info->device = NULL;
2151         info->iap = NULL;
2152         info->account = TNY_ACCOUNT (g_object_ref (account));
2153         
2154         if (parent_window)
2155                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
2156         else
2157                 info->parent_window = NULL;
2158         
2159         /* So we'll put the callback away for later ... */
2160         
2161         info->user_data = user_data;
2162         info->callback = callback;
2163         
2164         if (!device_online) {
2165  
2166                 /* If also the device is offline, then we connect both the device 
2167                  * and the account */
2168                 
2169                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
2170                                                       force, on_conic_device_went_online, 
2171                                                       info);
2172                 
2173         } else {
2174                 
2175                 /* If the device is online, we'll just connect the account */
2176                 
2177                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
2178                                               on_account_went_online, info);
2179         }
2180  
2181         /* The info gets freed by on_account_went_online or on_conic_device_went_online
2182          * in both situations, go look if you don't believe me! */
2183         
2184         return;
2185 }
2186
2187 void
2188 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
2189                                                gboolean force,
2190                                                TnyFolderStore *folder_store, 
2191                                                ModestConnectedPerformer callback, 
2192                                                gpointer user_data)
2193 {
2194         TnyAccount *account = NULL;
2195
2196         if (!folder_store ||
2197             (TNY_IS_MERGE_FOLDER (folder_store) &&
2198              (tny_folder_get_folder_type (TNY_FOLDER(folder_store)) == TNY_FOLDER_TYPE_OUTBOX))) {
2199
2200                 /* We promise to instantly perform the callback, so ... */
2201                 if (callback) {
2202                         GError *error = NULL;
2203                         g_set_error (&error, TNY_ERROR_DOMAIN, TNY_SERVICE_ERROR_UNKNOWN,
2204                                      "Unable to move or not found folder");
2205                         callback (FALSE, error, parent_window, NULL, user_data);
2206                         g_error_free (error);
2207                 }
2208                 return;
2209
2210         } else if (TNY_IS_FOLDER (folder_store)) {
2211                 /* Get the folder's parent account: */
2212                 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2213         } else if (TNY_IS_ACCOUNT (folder_store)) {
2214                 /* Use the folder store as an account: */
2215                 account = TNY_ACCOUNT (g_object_ref (folder_store));
2216         }
2217
2218         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2219                 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) {
2220                         /* No need to connect a local account */
2221                         if (callback)
2222                                 callback (FALSE, NULL, parent_window, account, user_data);
2223
2224                         goto clean;
2225                 }
2226         }
2227         modest_platform_connect_and_perform (parent_window, force, account, callback, user_data);
2228
2229  clean:
2230         if (account)
2231                 g_object_unref (account);
2232 }
2233
2234 static void
2235 src_account_connect_performer (gboolean canceled,
2236                                GError *err,
2237                                GtkWindow *parent_window,
2238                                TnyAccount *src_account,
2239                                gpointer user_data)
2240 {
2241         DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data;
2242
2243         if (canceled || err) {
2244                 /* If there was any error call the user callback */
2245                 info->callback (canceled, err, parent_window, src_account, info->data);
2246         } else {
2247                 /* Connect the destination account */
2248                 modest_platform_connect_if_remote_and_perform (parent_window, TRUE, 
2249                                                                TNY_FOLDER_STORE (info->dst_account),
2250                                                                info->callback, info->data);
2251         }
2252
2253         /* Free the info object */
2254         g_object_unref (info->dst_account);
2255         g_slice_free (DoubleConnectionInfo, info);
2256 }
2257
2258
2259 void 
2260 modest_platform_double_connect_and_perform (GtkWindow *parent_window, 
2261                                             gboolean force,
2262                                             TnyFolderStore *folder_store,
2263                                             DoubleConnectionInfo *connect_info)
2264 {
2265         modest_platform_connect_if_remote_and_perform(parent_window, 
2266                                                       force,
2267                                                       folder_store, 
2268                                                       src_account_connect_performer, 
2269                                                       connect_info);
2270 }
2271
2272 GtkWidget *
2273 modest_platform_get_account_settings_wizard (void)
2274 {
2275         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2276
2277         return GTK_WIDGET (dialog);
2278 }
2279
2280 ModestConnectedVia
2281 modest_platform_get_current_connection (void)
2282 {
2283         TnyDevice *device = NULL;
2284         ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY;
2285         
2286         device = modest_runtime_get_device ();
2287
2288         if (!tny_device_is_online (device))
2289                 return MODEST_CONNECTED_VIA_ANY;
2290
2291 #ifdef MODEST_HAVE_CONIC
2292         /* Get iap id */
2293         const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
2294         if (iap_id) {
2295                 ConIcIap *iap = tny_maemo_conic_device_get_iap (
2296                         TNY_MAEMO_CONIC_DEVICE (device), iap_id);
2297                 const gchar *bearer_type = con_ic_iap_get_bearer_type (iap);
2298                 if (bearer_type) {
2299                         if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) ||
2300                             !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) ||
2301                             !strcmp (bearer_type, "WIMAX")) {
2302                                 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX;
2303                         } else {
2304                                 retval = MODEST_CONNECTED_VIA_ANY;
2305                         }
2306                 }       
2307                 g_object_unref (iap);
2308         }
2309 #else
2310         retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */  
2311 #endif /* MODEST_HAVE_CONIC */
2312         return retval;
2313 }
2314
2315
2316
2317 gboolean
2318 modest_platform_check_memory_low (ModestWindow *win,
2319                                   gboolean visuals)
2320 {
2321         gboolean lowmem;
2322         
2323         /* are we in low memory state? */
2324         lowmem = osso_mem_in_lowmem_state () ? TRUE : FALSE;
2325         
2326         if (win && lowmem && visuals)
2327                 modest_platform_run_information_dialog (
2328                         GTK_WINDOW(win),
2329                         _KR("memr_ib_operation_disabled"),
2330                         TRUE);
2331
2332         if (lowmem)
2333                 g_debug ("%s: low memory reached. disallowing some operations",
2334                          __FUNCTION__);
2335
2336         return lowmem;
2337 }
2338
2339 void 
2340 modest_platform_run_folder_details_dialog (GtkWindow *parent_window,
2341                                            TnyFolder *folder)
2342 {
2343         GtkWidget *dialog;
2344         
2345         /* Create dialog */
2346         dialog = modest_hildon2_details_dialog_new_with_folder (parent_window, folder);
2347
2348         /* Run dialog */
2349         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2350                                      GTK_WINDOW (dialog), 
2351                                      parent_window);
2352         gtk_widget_show_all (dialog);
2353
2354         g_signal_connect_swapped (dialog, "response", 
2355                                   G_CALLBACK (gtk_widget_destroy),
2356                                   dialog);
2357 }
2358
2359 void
2360 modest_platform_run_header_details_dialog (GtkWindow *parent_window,
2361                                            TnyHeader *header)
2362 {
2363         GtkWidget *dialog;
2364
2365         /* Create dialog */
2366         dialog = modest_hildon2_details_dialog_new_with_header (parent_window, header);
2367
2368         /* Run dialog */
2369         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2370                                      GTK_WINDOW (dialog),
2371                                      parent_window);
2372         gtk_widget_show_all (dialog);
2373
2374         g_signal_connect_swapped (dialog, "response", 
2375                                   G_CALLBACK (gtk_widget_destroy),
2376                                   dialog);
2377 }
2378
2379 osso_context_t *
2380 modest_platform_get_osso_context (void)
2381 {
2382         return modest_maemo_utils_get_osso_context ();
2383 }
2384
2385 static void
2386 _modest_platform_play_email_tone (void)
2387 {
2388         gchar *active_profile;
2389         gchar *mail_tone;
2390         gchar *mail_volume;
2391         gint mail_volume_int;
2392         int ret;
2393         ca_context *ca_con = NULL;
2394         ca_proplist *pl = NULL;
2395
2396         active_profile = profile_get_profile ();
2397         mail_tone = profile_get_value (active_profile, PROFILE_MAIL_TONE);
2398         mail_volume = profile_get_value (active_profile, PROFILE_MAIL_VOLUME);
2399         mail_volume_int = profile_parse_int (mail_volume);
2400
2401         if (mail_volume_int > 0) {
2402
2403                 if ((ret = ca_context_create(&ca_con)) != CA_SUCCESS) {
2404                         g_warning("ca_context_create: %s\n", ca_strerror(ret));
2405                         return;
2406                 }
2407
2408                 if ((ret = ca_context_open(ca_con)) != CA_SUCCESS) {
2409                         g_warning("ca_context_open: %s\n", ca_strerror(ret));
2410                         ca_context_destroy(ca_con);
2411                         return;
2412                 }
2413
2414                 ca_proplist_create(&pl);
2415                 ca_proplist_sets(pl, CA_PROP_MEDIA_FILENAME, mail_tone);
2416                 ca_proplist_setf(pl, CA_PROP_CANBERRA_VOLUME, "%f", (gfloat) mail_volume_int);
2417
2418                 ret = ca_context_play_full(ca_con, 0, pl, NULL, NULL);
2419                 g_debug("ca_context_play_full (vol %f): %s\n", (gfloat) mail_volume_int, ca_strerror(ret));
2420
2421                 ca_proplist_destroy(pl);
2422                 ca_context_destroy(ca_con);
2423         }
2424
2425         g_free (mail_volume);
2426         g_free (mail_tone);
2427         g_free (active_profile);
2428 }
2429
2430 static void
2431 on_move_to_dialog_folder_activated (GtkTreeView       *tree_view,
2432                                     GtkTreePath       *path,
2433                                     GtkTreeViewColumn *column,
2434                                     gpointer           user_data)
2435 {
2436         gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_OK);
2437 }
2438
2439 GtkWidget *
2440 modest_platform_create_move_to_dialog (GtkWindow *parent_window,
2441                                        GtkWidget **folder_view)
2442 {
2443         GtkWidget *dialog, *folder_view_container;
2444
2445         /* Create dialog. We cannot use a touch selector because we
2446            need to use here the folder view widget directly */
2447         dialog = gtk_dialog_new_with_buttons (_("mcen_ti_moveto_folders_title"),
2448                                               GTK_WINDOW (parent_window),
2449                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR |
2450                                               GTK_DIALOG_DESTROY_WITH_PARENT,
2451                                               _HL("wdgt_bd_new"), MODEST_GTK_RESPONSE_NEW_FOLDER,
2452                                               NULL);
2453
2454         /* Create folder view */
2455         *folder_view = modest_platform_create_folder_view (NULL);
2456
2457         /* Simulate the behaviour of a HildonPickerDialog by emitting
2458            a response when a folder is selected */
2459         g_signal_connect (*folder_view, "row-activated",
2460                           G_CALLBACK (on_move_to_dialog_folder_activated),
2461                           dialog);
2462
2463         tny_account_store_view_set_account_store (TNY_ACCOUNT_STORE_VIEW (*folder_view),
2464                                                   (TnyAccountStore *) modest_runtime_get_account_store ());
2465
2466         /* Create pannable and add it to the dialog */
2467         folder_view_container = hildon_pannable_area_new ();
2468         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), folder_view_container);
2469         gtk_container_add (GTK_CONTAINER (folder_view_container), *folder_view);
2470
2471         gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 300);
2472
2473         gtk_widget_show (GTK_DIALOG (dialog)->vbox);
2474         gtk_widget_show (folder_view_container);
2475         gtk_widget_show (*folder_view);
2476
2477         return dialog;
2478 }
2479
2480 TnyList *
2481 modest_platform_get_list_to_move (ModestWindow *window)
2482 {
2483         TnyList *list = NULL;
2484
2485         if (MODEST_IS_HEADER_WINDOW (window)) {
2486                 ModestHeaderView *header_view;
2487
2488                 header_view = modest_header_window_get_header_view (MODEST_HEADER_WINDOW (window));
2489                 list = modest_header_view_get_selected_headers (header_view);
2490         } else if (MODEST_IS_FOLDER_WINDOW (window)) {
2491                 ModestFolderView *folder_view;
2492                 TnyFolderStore *selected_folder;
2493
2494                 list = TNY_LIST (tny_simple_list_new ());
2495                 folder_view = modest_folder_window_get_folder_view (MODEST_FOLDER_WINDOW (window));
2496                 selected_folder = modest_folder_view_get_selected (folder_view);
2497                 if (selected_folder) {
2498                         tny_list_prepend (list, G_OBJECT (selected_folder));
2499                         g_object_unref (selected_folder);
2500                 }
2501                 return list;
2502         } else if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
2503                 TnyHeader *header;
2504
2505                 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
2506                 if (header) {
2507                         list = TNY_LIST (tny_simple_list_new ());
2508                         tny_list_prepend (list, G_OBJECT (header));
2509                         g_object_unref (header);
2510                 }
2511         } else {
2512                 g_return_val_if_reached (NULL);
2513         }
2514
2515         return list;
2516 }