ffe1cee6c4940a0d481bac7f37701890ad0d9ba9
[modest] / src / maemo / modest-platform.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <config.h>
31 #include <glib/gi18n.h>
32 #include <modest-platform.h>
33 #include <modest-runtime.h>
34 #include <modest-main-window.h>
35 #include <modest-header-view.h>
36 #include "maemo/modest-maemo-global-settings-dialog.h"
37 #include "modest-widget-memory.h"
38 #include <modest-hildon-includes.h>
39 #include <osso-helplib.h>
40 #include <dbus_api/modest-dbus-callbacks.h>
41 #include <libosso-abook/osso-abook.h>
42 #include <maemo/modest-osso-autosave-callbacks.h>
43 #include <libosso.h>
44 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
45 #include <tny-maemo-conic-device.h>
46 #include <tny-simple-list.h>
47 #include <tny-folder.h>
48 #include <tny-camel-imap-store-account.h>
49 #include <tny-camel-pop-store-account.h>
50 #include <gtk/gtkicontheme.h>
51 #include <gtk/gtkmenuitem.h>
52 #include <gtk/gtkmain.h>
53 #include <modest-text-utils.h>
54 #include <string.h>
55
56
57 #define HILDON_OSSO_URI_ACTION "uri-action"
58 #define URI_ACTION_COPY "copy:"
59
60 static osso_context_t *osso_context = NULL;
61
62 static void     
63 on_modest_conf_update_interval_changed (ModestConf* self, 
64                                         const gchar *key, 
65                                         ModestConfEvent event,
66                                         ModestConfNotificationId id, 
67                                         gpointer user_data)
68 {
69         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
70                 const guint update_interval_minutes = 
71                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
72                 modest_platform_set_update_interval (update_interval_minutes);
73         }
74 }
75
76 gboolean
77 modest_platform_init (int argc, char *argv[])
78 {
79         osso_hw_state_t hw_state = { 0 };
80         DBusConnection *con;    
81
82         osso_context =
83                 osso_initialize(PACKAGE,PACKAGE_VERSION,
84                                 FALSE, NULL);   
85         if (!osso_context) {
86                 g_printerr ("modest: failed to acquire osso context\n");
87                 return FALSE;
88         }
89
90         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
91                 g_printerr ("modest: could not get dbus connection\n");
92                 return FALSE;
93
94         }
95         
96         /* Add a D-Bus handler to be used when the main osso-rpc 
97          * D-Bus handler has not handled something.
98          * We use this for D-Bus methods that need to use more complex types 
99          * than osso-rpc supports. 
100          */
101         if (!dbus_connection_add_filter (con,
102                                          modest_dbus_req_filter,
103                                          NULL,
104                                          NULL)) {
105
106                 g_printerr ("modest: Could not add D-Bus filter\n");
107                 return FALSE;
108         }
109
110         /* Register our simple D-Bus callbacks, via the osso API: */
111         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
112                                MODEST_DBUS_SERVICE, 
113                                MODEST_DBUS_OBJECT, 
114                                MODEST_DBUS_IFACE,
115                                modest_dbus_req_handler, NULL /* user_data */);
116         if (result != OSSO_OK) {
117                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
118                 return FALSE;
119         }
120
121         /* Add handler for Exit D-BUS messages.
122          * Not used because osso_application_set_exit_cb() is deprecated and obsolete:
123         result = osso_application_set_exit_cb(osso_context,
124                                           modest_dbus_exit_event_handler,
125                                           (gpointer) NULL);
126         if (result != OSSO_OK) {
127                 g_print("Error setting exit callback (%d)\n", result);
128                 return OSSO_ERROR;
129         }
130         */
131
132         /* Register hardware event dbus callback: */
133         hw_state.shutdown_ind = TRUE;
134         osso_hw_set_event_cb(osso_context, NULL,/*&hw_state*/ modest_osso_cb_hw_state_handler, NULL);
135
136         /* Register osso auto-save callbacks: */
137         result = osso_application_set_autosave_cb (osso_context, 
138                 modest_on_osso_application_autosave, NULL /* user_data */);
139         if (result != OSSO_OK) {
140                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
141                 return FALSE;
142         }
143         
144
145         /* Make sure that the update interval is changed whenever its gconf key 
146          * is changed */
147         /* CAUTION: we're not using here the
148            modest_conf_listen_to_namespace because we know that there
149            are other parts of Modest listening for this namespace, so
150            we'll receive the notifications anyway. We basically do not
151            use it because there is no easy way to do the
152            modest_conf_forget_namespace */
153         ModestConf *conf = modest_runtime_get_conf ();
154         g_signal_connect (G_OBJECT(conf),
155                           "key_changed",
156                           G_CALLBACK (on_modest_conf_update_interval_changed), 
157                           NULL);
158                           
159         /* Get the initial update interval from gconf: */
160         on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
161                                                MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
162
163         /* initialize the addressbook */
164         if (!osso_abook_init (&argc, &argv, osso_context)) {
165                 g_printerr ("modest: failed to initialized addressbook\n");
166                 return FALSE;
167         }
168                 
169         return TRUE;
170 }
171
172 TnyDevice*
173 modest_platform_get_new_device (void)
174 {
175         return TNY_DEVICE (tny_maemo_conic_device_new ());
176 }
177
178
179 const gchar*
180 guess_mime_type_from_name (const gchar* name)
181 {
182         int i;
183         const gchar* ext;
184         const static gchar* octet_stream= "application/octet-stream";
185         const static gchar* mime_map[][2] = {
186                 { "pdf",  "application/pdf"},
187                 { "doc",  "application/msword"},
188                 { "xls",  "application/excel"},
189                 { "png",  "image/png" },
190                 { "gif",  "image/gif" },
191                 { "jpg",  "image/jpeg"},
192                 { "jpeg", "image/jpeg"},
193                 { "mp3",  "audio/mp3" }
194         };
195
196         if (!name)
197                 return octet_stream;
198         
199         ext = g_strrstr (name, ".");
200         if (!ext)
201                 return octet_stream;
202         
203         for (i = 0; i != G_N_ELEMENTS(mime_map); ++i) {
204                 if (!g_ascii_strcasecmp (mime_map[i][0], ext + 1)) /* +1: ignore '.'*/
205                         return mime_map[i][1];
206         }
207         return octet_stream;
208 }
209
210
211 gchar*
212 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
213                                     gchar **effective_mime_type)
214 {
215         GString *mime_str = NULL;
216         gchar *icon_name  = NULL;
217         gchar **icons, **cursor;
218
219         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
220                 mime_str = g_string_new (guess_mime_type_from_name(name));
221         else {
222                 mime_str = g_string_new (mime_type);
223                 g_string_ascii_down (mime_str);
224         }
225
226 #ifdef MODEST_HAVE_OSSO_MIME
227         icons = osso_mime_get_icon_names (mime_str->str, NULL);
228 #else
229         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
230 #endif /*MODEST_HAVE_OSSO_MIME*/
231         for (cursor = icons; cursor; ++cursor) {
232                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
233                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
234                         icon_name = g_strdup ("qgn_list_messagin");
235                         break;
236                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
237                         icon_name = g_strdup (*cursor);
238                         break;
239                 }
240         }
241         g_strfreev (icons);
242
243         if (effective_mime_type)
244                 *effective_mime_type = g_string_free (mime_str, FALSE);
245         else
246                 g_string_free (mime_str, TRUE);
247
248         return icon_name;
249 }
250
251
252
253
254 #ifdef MODEST_HAVE_OSSO_MIME
255 gboolean 
256 modest_platform_activate_uri (const gchar *uri)
257 {
258         OssoURIAction *action;
259         gboolean result = FALSE;
260         GSList *actions, *iter = NULL;
261         const gchar *scheme;
262         
263         g_return_val_if_fail (uri, FALSE);
264         if (!uri)
265                 return FALSE;
266
267         /* the default action should be email */
268         scheme = osso_uri_get_scheme_from_uri (uri, NULL);
269         actions = osso_uri_get_actions (scheme, NULL);
270         
271         for (iter = actions; iter; iter = g_slist_next (iter)) {
272                 action = (OssoURIAction*) iter->data;
273                 if (action && strcmp (osso_uri_action_get_name (action), "uri_link_compose_email") == 0) {
274                         GError *err = NULL;
275                         result = osso_uri_open (uri, action, &err);
276                         if (!result && err) {
277                                 g_printerr ("modest: modest_platform_activate_uri : %s",
278                                             err->message ? err->message : "unknown error");
279                                 g_error_free (err);
280                         }
281                         break;
282                 }
283         }
284
285         /* if we could open it with email, try something else */
286         if (!result)
287                 result = osso_uri_open (uri, NULL, NULL);       
288         
289                         
290         if (!result)
291                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
292         return result;
293 }
294
295 #else /* !MODEST_HAVE_OSSO_MIME*/
296
297 gboolean 
298 modest_platform_activate_uri (const gchar *uri)
299 {
300         HildonURIAction *action;
301         gboolean result = FALSE;
302         GSList *actions, *iter = NULL;
303         const gchar *scheme;
304         
305         g_return_val_if_fail (uri, FALSE);
306         if (!uri)
307                 return FALSE;
308
309         scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
310         actions = hildon_uri_get_actions (scheme, NULL);
311         
312         for (iter = actions; iter; iter = g_slist_next (iter)) {
313                 action = (HildonURIAction*) iter->data;
314                 if (action && strcmp (hildon_uri_action_get_service (action), "com.nokia.modest") == 0) {
315                         GError *err = NULL;
316                         result = hildon_uri_open (uri, action, &err);
317                         if (!result && err) {
318                                 g_printerr ("modest: modest_platform_activate_uri : %s",
319                                             err->message ? err->message : "unknown error");
320                                 g_error_free (err);
321                         }
322                         break;
323                 }
324         }
325         
326         /* if we could open it with email, try something else */
327         if (!result)
328                 result = hildon_uri_open (uri, NULL, NULL);     
329                 
330         if (!result)
331                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
332         
333         return result;
334 }
335
336
337 #endif /* MODEST_HAVE_OSSO_MIME*/
338
339 gboolean 
340 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
341 {
342         gint result;
343         DBusConnection *con;
344         gchar *uri_path = NULL;
345         GString *mime_str = NULL;
346
347         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
348                 mime_str = g_string_new (guess_mime_type_from_name(path));
349         else {
350                 mime_str = g_string_new (mime_type);
351                 g_string_ascii_down (mime_str);
352         }
353
354         uri_path = g_strconcat ("file://", path, NULL);
355         
356         con = osso_get_dbus_connection (osso_context);
357 #ifdef MODEST_HAVE_OSSO_MIME
358         result = osso_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
359 #else
360         result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
361 #endif /*MODEST_HAVE_OSSO_MIME*/
362         g_string_free (mime_str, TRUE);
363
364         if (result != 1)
365                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
366         return result != 1;
367 }
368
369 typedef struct  {
370         GSList *actions;
371         gchar  *uri;
372 } ModestPlatformPopupInfo;
373
374 static gboolean
375 delete_uri_popup (GtkWidget *menu,
376                   GdkEvent *event,
377                   gpointer userdata)
378 {
379         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
380
381         g_free (popup_info->uri);
382 #ifdef MODEST_HAVE_OSSO_MIME
383         osso_uri_free_actions (popup_info->actions);
384 #else
385         hildon_uri_free_actions (popup_info->actions);
386 #endif /*MODEST_HAVE_OSSO_MIME*/
387         return FALSE;
388 }
389
390 static void
391 activate_uri_popup_item (GtkMenuItem *menu_item,
392                          gpointer userdata)
393 {
394         GSList *node;
395         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
396         const gchar* action_name;
397
398         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
399         if (!action_name) {
400                 g_printerr ("modest: no action name defined\n");
401                 return;
402         }
403
404         /* special handling for the copy menu item -- copy the uri to the clipboard */
405         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
406         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
407                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
408                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
409
410                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
411                         action_name += strlen ("mailto:");
412                 
413                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
414                 return; /* we're done */
415         }
416         
417         /* now, the real uri-actions... */
418         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
419 #ifdef MODEST_HAVE_OSSO_MIME
420                 OssoURIAction *action = (OssoURIAction *) node->data;
421                 if (strcmp (action_name, osso_uri_action_get_name (action))==0) {
422                         osso_uri_open (popup_info->uri, action, NULL);
423                         break;
424                 }
425 #else
426                 HildonURIAction *action = (HildonURIAction *) node->data;
427                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
428                         hildon_uri_open (popup_info->uri, action, NULL);
429                         break;
430                 }
431 #endif /*MODEST_HAVE_OSSO_MIME*/
432         }
433 }
434
435 gboolean 
436 modest_platform_show_uri_popup (const gchar *uri)
437 {
438         gchar *scheme;
439         GSList *actions_list;
440
441         if (uri == NULL)
442                 return FALSE;
443         
444 #ifdef MODEST_HAVE_OSSO_MIME
445         scheme = osso_uri_get_scheme_from_uri (uri, NULL);
446         actions_list = osso_uri_get_actions (scheme, NULL);
447 #else
448         scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
449         actions_list = hildon_uri_get_actions (scheme, NULL);
450 #endif /* MODEST_HAVE_OSSO_MIME */
451         if (actions_list != NULL) {
452                 GSList *node;
453                 GtkWidget *menu = gtk_menu_new ();
454                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
455
456                 popup_info->actions = actions_list;
457                 popup_info->uri = g_strdup (uri);
458               
459                 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
460                         GtkWidget *menu_item;
461                         const gchar *action_name;
462                         const gchar *translation_domain;
463 #ifdef MODEST_HAVE_OSSO_MIME
464                         OssoURIAction *action = (OssoURIAction *) node->data;
465                         action_name = osso_uri_action_get_name (action);
466                         translation_domain = osso_uri_action_get_translation_domain (action);
467                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain,action_name));
468                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);
469                         /* hack, we add it as a gobject property*/
470                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
471                                           popup_info);
472                         
473                         if (osso_uri_is_default_action (action, NULL)) {
474                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
475                         } else {
476                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
477                         }
478 #else
479                         HildonURIAction *action = (HildonURIAction *) node->data;
480                         action_name = hildon_uri_action_get_name (action);
481                         translation_domain = hildon_uri_action_get_translation_domain (action);
482                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
483                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
484                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
485                                           popup_info);
486                                                                   
487                         if (hildon_uri_is_default_action (action, NULL)) {
488                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
489                         } else {
490                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
491                         }
492 #endif /*MODEST_HAVE_OSSO_MIME*/
493                         gtk_widget_show (menu_item);
494                 }
495
496                 /* always add the copy item */
497                 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
498                 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
499                                         g_strconcat (URI_ACTION_COPY, uri, NULL),
500                                         g_free);
501                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
502                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
503                 gtk_widget_show (menu_item);
504
505                 
506                 /* and what to do when the link is deleted */
507                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
508                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
509                                                   
510         } else {
511                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
512         }
513         
514         g_free (scheme);
515         return TRUE;
516 }
517
518
519 GdkPixbuf*
520 modest_platform_get_icon (const gchar *name)
521 {
522         GError *err = NULL;
523         GdkPixbuf* pixbuf = NULL;
524         GtkIconTheme *current_theme = NULL;
525
526         g_return_val_if_fail (name, NULL);
527
528         /* strlen == 0 is not really an error; it just
529          * means the icon is not available
530          */
531         if (!name || strlen(name) == 0)
532                 return NULL;
533         
534 #if 0 /* do we still need this? */
535         if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
536                 pixbuf = gdk_pixbuf_new_from_file (name, &err);
537                 if (!pixbuf) {
538                         g_printerr ("modest: error loading icon '%s': %s\n",
539                                     name, err->message);
540                         g_error_free (err);
541                         return NULL;
542                 }
543                 return pixbuf;
544         }
545 #endif /* */
546         current_theme = gtk_icon_theme_get_default ();
547         pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
548                                            GTK_ICON_LOOKUP_NO_SVG,
549                                            &err);
550         if (!pixbuf) {
551                 g_printerr ("modest: error loading theme icon '%s': %s\n",
552                             name, err->message);
553                 g_error_free (err);
554         } 
555         return pixbuf;
556 }
557
558 const gchar*
559 modest_platform_get_app_name (void)
560 {
561         return _("mcen_ap_name");
562 }
563
564 static void 
565 entry_insert_text (GtkEditable *editable,
566                    const gchar *text,
567                    gint         length,
568                    gint        *position,
569                    gpointer     data)
570 {
571         gchar *chars;
572         gint chars_length;
573
574         chars = gtk_editable_get_chars (editable, 0, -1);
575         chars_length = g_utf8_strlen (chars, -1);
576
577         /* Show WID-INF036 */
578         if (chars_length >= 20) {
579                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
580                                                  _CS("ckdg_ib_maximum_characters_reached"));
581         } else {
582                 GtkWidget *ok_button;
583                 GList *buttons;
584                 
585                 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
586                 ok_button = GTK_WIDGET (buttons->next->data);
587                 
588                 gtk_widget_set_sensitive (ok_button,
589                                           modest_text_utils_validate_folder_name (chars));      
590                 g_list_free (buttons);
591                 
592                 /* Write the text in the entry */
593                 g_signal_handlers_block_by_func (editable,
594                                                  (gpointer) entry_insert_text, data);
595                 gtk_editable_insert_text (editable, text, length, position);
596                 g_signal_handlers_unblock_by_func (editable,
597                                                    (gpointer) entry_insert_text, data);
598         }
599         /* Do not allow further processing */
600         g_signal_stop_emission_by_name (editable, "insert_text");
601 }
602
603 static void
604 entry_changed (GtkEditable *editable,
605                gpointer     user_data)
606 {
607         gchar *chars;
608         GtkWidget *ok_button;
609         GList *buttons;
610
611         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
612         ok_button = GTK_WIDGET (buttons->next->data);
613         
614         chars = gtk_editable_get_chars (editable, 0, -1);
615         g_return_if_fail (chars != NULL);
616
617         
618         if (g_utf8_strlen (chars,-1) >= 21)
619                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
620                                                  _CS("ckdg_ib_maximum_characters_reached"));
621         else
622                 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
623                 
624         /* Free */
625         g_list_free (buttons);
626         g_free (chars);
627 }
628
629 static void
630 launch_sort_headers_dialog (GtkWindow *parent_window,
631                             HildonSortDialog *dialog)
632 {
633         ModestHeaderView *header_view = NULL;
634         GList *cols = NULL;
635         GtkSortType sort_type;
636         gint sort_key;
637         gint default_key = 0;
638         gint result;
639         gboolean outgoing = FALSE;
640         gint current_sort_colid = -1;
641         GtkSortType current_sort_type;
642         gint attachments_sort_id;
643         gint priority_sort_id;
644         GtkTreeSortable *sortable;
645         
646         /* Get header window */
647         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
648                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
649                                                                                       MODEST_WIDGET_TYPE_HEADER_VIEW));
650         }
651         if (!header_view) return;
652
653         /* Add sorting keys */
654         cols = modest_header_view_get_columns (header_view);
655         if (cols == NULL) return;
656         int sort_model_ids[6];
657         int sort_ids[6];
658
659
660         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
661                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
662
663         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
664         if (outgoing) {
665                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
666                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
667         } else {
668                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
669                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
670         }
671
672         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
673         if (outgoing) {
674                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
675                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
676         } else {
677                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
678                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
679         }
680         default_key = sort_key;
681
682         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
683         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
684         if (outgoing)
685                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
686         else
687                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
688
689         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
690         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
691         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
692         attachments_sort_id = sort_key;
693
694         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
695         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
696         sort_ids[sort_key] = 0;
697
698         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
699         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
700         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
701         priority_sort_id = sort_key;
702
703         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
704         /* Launch dialogs */
705         if (!gtk_tree_sortable_get_sort_column_id (sortable,
706                                                    &current_sort_colid, &current_sort_type)) {
707                 hildon_sort_dialog_set_sort_key (dialog, default_key);
708                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
709         } else {
710                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
711                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
712                         gpointer flags_sort_type_pointer;
713                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
714                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
715                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
716                         else
717                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
718                 } else {
719                         gint current_sort_keyid = 0;
720                         while (current_sort_keyid < 6) {
721                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
722                                         break;
723                                 else 
724                                         current_sort_keyid++;
725                         }
726                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
727                 }
728         }
729
730         result = gtk_dialog_run (GTK_DIALOG (dialog));
731         if (result == GTK_RESPONSE_OK) {
732                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
733                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
734                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
735                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
736                                            GINT_TO_POINTER (sort_ids[sort_key]));
737                         /* This is a hack to make it resort rows always when flag fields are
738                          * selected. If we do not do this, changing sort field from priority to
739                          * attachments does not work */
740                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
741                 } else {
742                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
743                                                                  sort_model_ids[sort_key]);
744                 }
745
746                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
747                 gtk_tree_sortable_sort_column_changed (sortable);
748         }
749
750         modest_widget_memory_save (modest_runtime_get_conf (),
751                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
752         
753 /*      while (gtk_events_pending ()) */
754 /*              gtk_main_iteration (); */
755
756         /* free */
757         g_list_free(cols);      
758 }
759
760 static gint
761 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
762                                         const gchar *dialog_title,
763                                         const gchar *label_text,
764                                         const gchar *suggested_name,
765                                         gchar **folder_name)
766 {
767         GtkWidget *accept_btn = NULL; 
768         GtkWidget *dialog, *entry, *label, *hbox;
769         GList *buttons = NULL;
770         gint result;
771
772         /* Ask the user for the folder name */
773         dialog = gtk_dialog_new_with_buttons (dialog_title,
774                                               parent_window,
775                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
776                                               GTK_STOCK_OK,
777                                               GTK_RESPONSE_ACCEPT,
778                                               GTK_STOCK_CANCEL,
779                                               GTK_RESPONSE_REJECT,
780                                               NULL);
781
782         /* Add accept button (with unsensitive handler) */
783         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
784         accept_btn = GTK_WIDGET (buttons->next->data);
785         /* Create label and entry */
786         label = gtk_label_new (label_text);
787         /* TODO: check that the suggested name does not exist */
788         /* We set 21 as maximum because we want to show WID-INF036
789            when the user inputs more that 20 */
790         entry = gtk_entry_new_with_max_length (21);
791         if (suggested_name)
792                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
793         else
794                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
795         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
796
797         /* Track entry changes */
798         g_signal_connect (entry,
799                           "insert-text",
800                           G_CALLBACK (entry_insert_text),
801                           dialog);
802         g_signal_connect (entry,
803                           "changed",
804                           G_CALLBACK (entry_changed),
805                           dialog);
806
807         /* Create the hbox */
808         hbox = gtk_hbox_new (FALSE, 12);
809         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
810         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
811
812         /* Add hbox to dialog */
813         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
814                             hbox, FALSE, FALSE, 0);
815         
816         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
817         
818         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
819         result = gtk_dialog_run (GTK_DIALOG(dialog));
820         if (result == GTK_RESPONSE_ACCEPT)
821                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
822
823         gtk_widget_destroy (dialog);
824
825         while (gtk_events_pending ())
826                 gtk_main_iteration ();
827
828         return result;
829 }
830
831 gint
832 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
833                                        TnyFolderStore *parent_folder,
834                                        gchar *suggested_name,
835                                        gchar **folder_name)
836 {
837         gchar *real_suggested_name = NULL;
838         gint result;
839
840         if(suggested_name == NULL)
841         {
842                 const gchar *default_name = _("mcen_ia_default_folder_name");
843                 unsigned int i;
844                 gchar num_str[3];
845
846                 for(i = 0; i < 100; ++ i)
847                 {
848                         TnyList *list = tny_simple_list_new ();
849                         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
850                         guint length;
851
852                         sprintf(num_str, "%.2u", i);
853
854                         if (i == 0)
855                                 real_suggested_name = g_strdup (default_name);
856                         else
857                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
858                                                                        num_str);
859
860                         tny_folder_store_query_add_item (query, real_suggested_name,
861                                                          TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
862
863                         tny_folder_store_get_folders (parent_folder, list, query, NULL);
864
865                         length = tny_list_get_length (list);
866                         g_object_unref (query);
867                         g_object_unref (list);
868
869                         if (length == 0)
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         else
880         {
881                 real_suggested_name = suggested_name;
882         }
883
884         result = modest_platform_run_folder_name_dialog (parent_window, 
885                                                          _("mcen_ti_new_folder"),
886                                                          _("mcen_fi_new_folder_name"),
887                                                          real_suggested_name,
888                                                          folder_name);
889         if (suggested_name == NULL)
890                 g_free(real_suggested_name);
891
892         return result;
893 }
894
895 gint
896 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
897                                           TnyFolderStore *parent_folder,
898                                           const gchar *suggested_name,
899                                           gchar **folder_name)
900 {
901         return modest_platform_run_folder_name_dialog (parent_window, 
902                                                        _("New folder name"),
903                                                        _("Enter new folder name:"),
904                                                        suggested_name,
905                                                        folder_name);
906 }
907
908 gint
909 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
910                                          const gchar *message)
911 {
912         GtkWidget *dialog;
913         gint response;
914
915         dialog = hildon_note_new_confirmation (parent_window, message);
916         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
917
918         response = gtk_dialog_run (GTK_DIALOG (dialog));
919
920         gtk_widget_destroy (GTK_WIDGET (dialog));
921
922         while (gtk_events_pending ())
923                 gtk_main_iteration ();
924
925         return response;
926 }
927
928 gint
929 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
930                                    const gchar *message)
931 {
932         GtkWidget *dialog;
933         gint response;
934
935         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
936                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
937                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
938                                                            NULL);
939         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
940
941         response = gtk_dialog_run (GTK_DIALOG (dialog));
942
943         gtk_widget_destroy (GTK_WIDGET (dialog));
944
945         while (gtk_events_pending ())
946                 gtk_main_iteration ();
947
948         return response;
949 }
950
951 void
952 modest_platform_run_information_dialog (GtkWindow *parent_window,
953                                         const gchar *message)
954 {
955         GtkWidget *dialog;
956
957         dialog = hildon_note_new_information (parent_window, message);
958
959         g_signal_connect_swapped (dialog,
960                                   "response", 
961                                   G_CALLBACK (gtk_widget_destroy),
962                                   dialog);
963
964         gtk_widget_show_all (dialog);
965 }
966
967
968
969 typedef struct
970 {
971         GMainLoop* loop;
972 } UtilIdleData;
973
974 static gboolean 
975 on_idle_connect_and_wait(gpointer user_data)
976 {
977         printf ("DEBUG: %s:\n", __FUNCTION__);
978         TnyDevice *device = modest_runtime_get_device();
979         if (!tny_device_is_online (device)) {
980
981                 /* This is a GDK lock because we are an idle callback and
982                  * tny_maemo_conic_device_connect can contain Gtk+ code */
983
984                 gdk_threads_enter(); /* CHECKED */
985                 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
986                 gdk_threads_leave(); /* CHECKED */
987         }
988         
989         /* Allow the function that requested this idle callback to continue: */
990         UtilIdleData *data = (UtilIdleData*)user_data;
991         if (data->loop)
992                 g_main_loop_quit (data->loop);
993         
994         return FALSE; /* Don't call this again. */
995 }
996
997 static gboolean connect_request_in_progress = FALSE;
998
999 /* This callback is used when connect_and_wait() is already queued as an idle callback.
1000  * This can happen because the gtk_dialog_run() for the connection dialog 
1001  * (at least in the fake scratchbox version) allows idle handlers to keep running.
1002  */
1003 static gboolean 
1004 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
1005 {
1006         gboolean result = FALSE;
1007         TnyDevice *device = modest_runtime_get_device();
1008         if (tny_device_is_online (device))
1009                 result = FALSE; /* Stop trying. */
1010         else {
1011                 /* Keep trying until connect_request_in_progress is FALSE. */
1012                 if (connect_request_in_progress)
1013                         result = TRUE; /* Keep trying */
1014                 else {
1015                         printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
1016                                                 
1017                         result = FALSE; /* Stop trying, now that a result should be available. */
1018                 }
1019         }
1020         
1021         if (result == FALSE) {
1022                 /* Allow the function that requested this idle callback to continue: */
1023                 UtilIdleData *data = (UtilIdleData*)user_data;
1024                 if (data->loop)
1025                         g_main_loop_quit (data->loop);  
1026         }
1027                 
1028         return result;
1029 }
1030
1031 static void 
1032 set_account_to_online (TnyAccount *account)
1033 {
1034         /* TODO: This is necessary to prevent a cancel of the password dialog 
1035          * from making a restart necessary to be asked the password again,
1036          * but it causes a hang:
1037          */
1038         #if 0
1039         if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
1040                 /* Make sure that store accounts are online too, 
1041                  * because tinymail sets accounts to offline if 
1042                  * a password dialog is ever cancelled.
1043                  * We don't do this for transport accounts because 
1044                  * a) They fundamentally need network access, so they can't really be offline.
1045                  * b) That might cause a transport connection to happen too early.
1046                  */
1047                 GError *error = NULL;
1048                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1049                 if (error) {
1050                         g_warning ("%s: tny_camel_account_set_online() returned a GError:\n  %s\n", 
1051                                 __FUNCTION__, error->message);
1052                         g_error_free (error);   
1053                 }
1054         }
1055         #endif
1056 }
1057
1058 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1059 {
1060         if (connect_request_in_progress)
1061                 return FALSE;
1062                 
1063         printf ("DEBUG: %s:\n", __FUNCTION__);
1064         TnyDevice *device = modest_runtime_get_device();
1065         
1066         if (tny_device_is_online (device)) {
1067                 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1068                 set_account_to_online (account);
1069                 return TRUE;
1070         } else
1071         {
1072                 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1073         }
1074                 
1075         /* This blocks on the result: */
1076         UtilIdleData *data = g_slice_new0 (UtilIdleData);
1077         
1078         GMainContext *context = NULL; /* g_main_context_new (); */
1079         data->loop = g_main_loop_new (context, FALSE /* not running */);
1080         
1081         /* Cause the function to be run in an idle-handler, which is always 
1082          * in the main thread:
1083          */
1084         if (!connect_request_in_progress) {
1085                 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1086                 connect_request_in_progress = TRUE;
1087                 g_idle_add (&on_idle_connect_and_wait, data);
1088         }
1089         else {
1090                 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1091                 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1092         }
1093
1094         /* This main loop will run until the idle handler has stopped it: */
1095         printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1096         GDK_THREADS_LEAVE();
1097         g_main_loop_run (data->loop);
1098         GDK_THREADS_ENTER();
1099         printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1100         connect_request_in_progress = FALSE;
1101         printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1102         g_main_loop_unref (data->loop);
1103         /* g_main_context_unref (context); */
1104
1105         g_slice_free (UtilIdleData, data);
1106
1107         gboolean result = tny_device_is_online (device);
1108
1109         if (result)
1110                 set_account_to_online (account);
1111
1112         return result;
1113 }
1114
1115 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1116 {
1117         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1118                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1119                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1120                         /* This must be a maildir account, which does not require a connection: */
1121                         return TRUE;
1122                 }
1123         }
1124
1125         return modest_platform_connect_and_wait (parent_window, account);
1126 }
1127
1128 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1129 {
1130         if (!folder_store)
1131                 return TRUE; /* Maybe it is something local. */
1132                 
1133         gboolean result = TRUE;
1134         if (TNY_IS_FOLDER (folder_store)) {
1135                 /* Get the folder's parent account: */
1136                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1137                 if (account != NULL) {
1138                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1139                         g_object_unref (account);
1140                 }
1141         } else if (TNY_IS_ACCOUNT (folder_store)) {
1142                 /* Use the folder store as an account: */
1143                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1144         }
1145
1146         return result;
1147 }
1148
1149 void
1150 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1151                                  ModestSortDialogType type)
1152 {
1153         GtkWidget *dialog = NULL;
1154
1155         /* Build dialog */
1156         dialog = hildon_sort_dialog_new (parent_window);
1157         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1158         
1159         /* Fill sort keys */
1160         switch (type) {
1161         case MODEST_SORT_HEADERS:
1162                 launch_sort_headers_dialog (parent_window, 
1163                                             HILDON_SORT_DIALOG(dialog));
1164                 break;
1165         }
1166         
1167         /* Free */
1168         gtk_widget_destroy (GTK_WIDGET (dialog));
1169 }
1170
1171
1172 gboolean modest_platform_set_update_interval (guint minutes)
1173 {
1174         ModestConf *conf = modest_runtime_get_conf ();
1175         if (!conf)
1176                 return FALSE;
1177                 
1178         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1179
1180         /* Delete any existing alarm,
1181          * because we will replace it: */
1182         if (alarm_cookie) {
1183                 /* TODO: What does the alarm_event_del() return value mean? */
1184                 alarm_event_del(alarm_cookie);
1185                 alarm_cookie = 0;
1186                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1187         }
1188         
1189         /* 0 means no updates: */
1190         if (minutes == 0)
1191                 return TRUE;
1192                 
1193      
1194         /* Register alarm: */
1195         
1196         /* Set the interval in alarm_event_t structure: */
1197         alarm_event_t *event = g_new0(alarm_event_t, 1);
1198         event->alarm_time = minutes * 60; /* seconds */
1199         
1200         /* Set recurrence every few minutes: */
1201         event->recurrence = minutes;
1202         event->recurrence_count = -1; /* Means infinite */
1203
1204         /* Specify what should happen when the alarm happens:
1205          * It should call this D-Bus method: */
1206          
1207         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1208         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1209         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1210         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1211
1212         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1213          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1214          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1215          * This is why we want to use the Alarm API instead of just g_timeout_add().
1216          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1217          */
1218         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1219         
1220         alarm_cookie = alarm_event_add (event);
1221
1222         /* now, free it */
1223         alarm_event_free (event);
1224         
1225         /* Store the alarm ID in GConf, so we can remove it later:
1226          * This is apparently valid between application instances. */
1227         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1228         
1229         if (!alarm_cookie) {
1230             /* Error */
1231             const alarm_error_t alarm_error = alarmd_get_error ();
1232             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1233             
1234             /* Give people some clue: */
1235             /* The alarm API should have a function for this: */
1236             if (alarm_error == ALARMD_ERROR_DBUS) {
1237                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1238             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1239                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1240             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1241                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1242             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1243                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1244             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1245                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1246             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1247                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1248             }
1249             
1250             return FALSE;
1251         }
1252         
1253         return TRUE;
1254 }
1255
1256 GtkWidget * 
1257 modest_platform_get_global_settings_dialog ()
1258 {
1259         return modest_maemo_global_settings_dialog_new ();
1260 }
1261
1262 void 
1263 modest_platform_on_new_msg (void)
1264 {
1265 #ifdef MODEST_HAVE_HILDON_NOTIFY
1266         HildonNotification *not;
1267
1268         /* Create a new notification. FIXME put the right values, need
1269            some more specs */
1270         not = hildon_notification_new ("TODO: (new email) Summary",
1271                                        "TODO: (new email) Description",
1272                                        "qgn_contact_group_chat_invitation",
1273                                        "system.note.dialog");
1274
1275         /* Play sound SR-SND-18. TODO: play the right file */
1276         /* TODO: Where is this declared? hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav"); */
1277
1278         /* Set the led pattern */
1279         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "led-pattern", 3);
1280
1281         /* Notify. We need to do this in an idle because this function
1282            could be called from a thread */
1283         if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1284                 g_error ("Failed to send notification");
1285                 
1286         g_object_unref (not);
1287 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1288 }
1289
1290
1291 void
1292 modest_platform_show_help (GtkWindow *parent_window, 
1293                            const gchar *help_id)
1294 {
1295         osso_return_t result;
1296
1297         g_return_if_fail (help_id);
1298         g_return_if_fail (osso_context);
1299
1300         /* Show help */
1301 #ifdef MODEST_HAVE_OSSO_HELP
1302         result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1303 #else
1304         result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1305 #endif
1306
1307         if (result != OSSO_OK) {
1308                 gchar *error_msg;
1309                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1310                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1311                                                 NULL,
1312                                                 error_msg);
1313                 g_free (error_msg);
1314         }
1315 }
1316
1317 void 
1318 modest_platform_show_search_messages (GtkWindow *parent_window)
1319 {
1320         osso_return_t result = OSSO_ERROR;
1321         
1322         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1323
1324         if (result != OSSO_OK) {
1325                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1326         }
1327 }
1328
1329 void 
1330 modest_platform_show_addressbook (GtkWindow *parent_window)
1331 {
1332         osso_return_t result = OSSO_ERROR;
1333         
1334         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1335
1336         if (result != OSSO_OK) {
1337                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1338         }
1339 }
1340
1341 GtkWidget *
1342 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1343 {
1344         GtkWidget *widget = modest_folder_view_new (query);
1345
1346         /* Show one account by default */
1347         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1348                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1349
1350
1351         /* Restore settings */
1352         modest_widget_memory_restore (modest_runtime_get_conf(), 
1353                                       G_OBJECT (widget),
1354                                       MODEST_CONF_FOLDER_VIEW_KEY);
1355
1356         return widget;
1357 }
1358
1359 void 
1360 modest_platform_information_banner (GtkWidget *parent,
1361                                     const gchar *icon_name,
1362                                     const gchar *text)
1363 {
1364         hildon_banner_show_information (parent, icon_name, text);
1365 }
1366
1367 GtkWidget *
1368 modest_platform_animation_banner (GtkWidget *parent,
1369                                   const gchar *animation_name,
1370                                   const gchar *text)
1371 {
1372         GtkWidget *inf_note = NULL;
1373
1374         g_return_val_if_fail (text != NULL, NULL);
1375
1376         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1377
1378         return inf_note;
1379 }