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