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