2007-08-13 Murray Cumming <murrayc@murrayc.com>
[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 #if 0 /* do we still need this? */
529         if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
530                 pixbuf = gdk_pixbuf_new_from_file (name, &err);
531                 if (!pixbuf) {
532                         g_printerr ("modest: error loading icon '%s': %s\n",
533                                     name, err->message);
534                         g_error_free (err);
535                         return NULL;
536                 }
537                 return pixbuf;
538         }
539 #endif /* */
540         current_theme = gtk_icon_theme_get_default ();
541         pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
542                                            GTK_ICON_LOOKUP_NO_SVG,
543                                            &err);
544         if (!pixbuf) {
545                 g_printerr ("modest: error loading theme icon '%s': %s\n",
546                             name, err->message);
547                 g_error_free (err);
548         } 
549         return pixbuf;
550 }
551
552 const gchar*
553 modest_platform_get_app_name (void)
554 {
555         return _("mcen_ap_name");
556 }
557
558 static void 
559 entry_insert_text (GtkEditable *editable,
560                    const gchar *text,
561                    gint         length,
562                    gint        *position,
563                    gpointer     data)
564 {
565         gchar *chars;
566         gint chars_length;
567
568         chars = gtk_editable_get_chars (editable, 0, -1);
569         chars_length = g_utf8_strlen (chars, -1);
570
571         /* Show WID-INF036 */
572         if (chars_length >= 20) {
573                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
574                                                  _CS("ckdg_ib_maximum_characters_reached"));
575         } else {
576                 GtkWidget *ok_button;
577                 GList *buttons;
578                 
579                 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
580                 ok_button = GTK_WIDGET (buttons->next->data);
581                 
582                 gtk_widget_set_sensitive (ok_button,
583                                           modest_text_utils_validate_folder_name (chars));      
584                 g_list_free (buttons);
585                 
586                 /* Write the text in the entry */
587                 g_signal_handlers_block_by_func (editable,
588                                                  (gpointer) entry_insert_text, data);
589                 gtk_editable_insert_text (editable, text, length, position);
590                 g_signal_handlers_unblock_by_func (editable,
591                                                    (gpointer) entry_insert_text, data);
592         }
593         /* Do not allow further processing */
594         g_signal_stop_emission_by_name (editable, "insert_text");
595 }
596
597 static void
598 entry_changed (GtkEditable *editable,
599                gpointer     user_data)
600 {
601         gchar *chars;
602         GtkWidget *ok_button;
603         GList *buttons;
604
605         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
606         ok_button = GTK_WIDGET (buttons->next->data);
607         
608         chars = gtk_editable_get_chars (editable, 0, -1);
609         g_return_if_fail (chars != NULL);
610
611         
612         if (g_utf8_strlen (chars,-1) >= 21)
613                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
614                                                  _CS("ckdg_ib_maximum_characters_reached"));
615         else
616                 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
617                 
618         /* Free */
619         g_list_free (buttons);
620         g_free (chars);
621 }
622
623 static void
624 launch_sort_headers_dialog (GtkWindow *parent_window,
625                             HildonSortDialog *dialog)
626 {
627         ModestHeaderView *header_view = NULL;
628         GList *cols = NULL;
629         GtkSortType sort_type;
630         gint sort_key;
631         gint default_key = 0;
632         gint result;
633         gboolean outgoing = FALSE;
634         gint current_sort_colid = -1;
635         GtkSortType current_sort_type;
636         gint attachments_sort_id;
637         gint priority_sort_id;
638         GtkTreeSortable *sortable;
639         
640         /* Get header window */
641         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
642                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
643                                                                                       MODEST_WIDGET_TYPE_HEADER_VIEW));
644         }
645         if (!header_view) return;
646
647         /* Add sorting keys */
648         cols = modest_header_view_get_columns (header_view);
649         if (cols == NULL) return;
650         int sort_model_ids[6];
651         int sort_ids[6];
652
653
654         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
655                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
656
657         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
658         if (outgoing) {
659                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
660                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
661         } else {
662                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
663                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
664         }
665
666         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
667         if (outgoing) {
668                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
669                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
670         } else {
671                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
672                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
673         }
674         default_key = sort_key;
675
676         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
677         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
678         if (outgoing)
679                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
680         else
681                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
682
683         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
684         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
685         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
686         attachments_sort_id = sort_key;
687
688         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
689         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
690         sort_ids[sort_key] = 0;
691
692         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
693         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
694         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
695         priority_sort_id = sort_key;
696
697         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
698         /* Launch dialogs */
699         if (!gtk_tree_sortable_get_sort_column_id (sortable,
700                                                    &current_sort_colid, &current_sort_type)) {
701                 hildon_sort_dialog_set_sort_key (dialog, default_key);
702                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
703         } else {
704                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
705                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
706                         gpointer flags_sort_type_pointer;
707                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
708                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
709                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
710                         else
711                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
712                 } else {
713                         gint current_sort_keyid = 0;
714                         while (current_sort_keyid < 6) {
715                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
716                                         break;
717                                 else 
718                                         current_sort_keyid++;
719                         }
720                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
721                 }
722         }
723
724         result = gtk_dialog_run (GTK_DIALOG (dialog));
725         if (result == GTK_RESPONSE_OK) {
726                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
727                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
728                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
729                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
730                                            GINT_TO_POINTER (sort_ids[sort_key]));
731                         /* This is a hack to make it resort rows always when flag fields are
732                          * selected. If we do not do this, changing sort field from priority to
733                          * attachments does not work */
734                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
735                 } else {
736                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
737                                                                  sort_model_ids[sort_key]);
738                 }
739
740                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
741                 gtk_tree_sortable_sort_column_changed (sortable);
742         }
743
744         modest_widget_memory_save (modest_runtime_get_conf (),
745                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
746         
747         while (gtk_events_pending ())
748                 gtk_main_iteration ();
749
750         /* free */
751         g_list_free(cols);      
752 }
753
754 static gint
755 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
756                                         const gchar *dialog_title,
757                                         const gchar *label_text,
758                                         const gchar *suggested_name,
759                                         gchar **folder_name)
760 {
761         GtkWidget *accept_btn = NULL; 
762         GtkWidget *dialog, *entry, *label, *hbox;
763         GList *buttons = NULL;
764         gint result;
765
766         /* Ask the user for the folder name */
767         dialog = gtk_dialog_new_with_buttons (dialog_title,
768                                               parent_window,
769                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
770                                               GTK_STOCK_OK,
771                                               GTK_RESPONSE_ACCEPT,
772                                               GTK_STOCK_CANCEL,
773                                               GTK_RESPONSE_REJECT,
774                                               NULL);
775
776         /* Add accept button (with unsensitive handler) */
777         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
778         accept_btn = GTK_WIDGET (buttons->next->data);
779         /* Create label and entry */
780         label = gtk_label_new (label_text);
781         /* TODO: check that the suggested name does not exist */
782         /* We set 21 as maximum because we want to show WID-INF036
783            when the user inputs more that 20 */
784         entry = gtk_entry_new_with_max_length (21);
785         if (suggested_name)
786                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
787         else
788                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
789         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
790
791         /* Track entry changes */
792         g_signal_connect (entry,
793                           "insert-text",
794                           G_CALLBACK (entry_insert_text),
795                           dialog);
796         g_signal_connect (entry,
797                           "changed",
798                           G_CALLBACK (entry_changed),
799                           dialog);
800
801         /* Create the hbox */
802         hbox = gtk_hbox_new (FALSE, 12);
803         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
804         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
805
806         /* Add hbox to dialog */
807         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
808                             hbox, FALSE, FALSE, 0);
809         
810         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
811         
812         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
813         result = gtk_dialog_run (GTK_DIALOG(dialog));
814         if (result == GTK_RESPONSE_ACCEPT)
815                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
816
817         gtk_widget_destroy (dialog);
818
819         while (gtk_events_pending ())
820                 gtk_main_iteration ();
821
822         return result;
823 }
824
825 gint
826 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
827                                        TnyFolderStore *parent_folder,
828                                        gchar *suggested_name,
829                                        gchar **folder_name)
830 {
831         gchar *real_suggested_name = NULL;
832         gint result;
833
834         if(suggested_name == NULL)
835         {
836                 const gchar *default_name = _("mcen_ia_default_folder_name");
837                 unsigned int i;
838                 gchar num_str[3];
839
840                 for(i = 0; i < 100; ++ i)
841                 {
842                         TnyList *list = tny_simple_list_new ();
843                         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
844                         guint length;
845
846                         sprintf(num_str, "%.2u", i);
847
848                         if (i == 0)
849                                 real_suggested_name = g_strdup (default_name);
850                         else
851                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
852                                                                        num_str);
853
854                         tny_folder_store_query_add_item (query, real_suggested_name,
855                                                          TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
856
857                         tny_folder_store_get_folders (parent_folder, list, query, NULL);
858
859                         length = tny_list_get_length (list);
860                         g_object_unref (query);
861                         g_object_unref (list);
862
863                         if (length == 0)
864                                 break;
865
866                         g_free (real_suggested_name);
867                 }
868
869                 /* Didn't find a free number */
870                 if (i == 100)
871                         real_suggested_name = g_strdup (default_name);
872         }
873         else
874         {
875                 real_suggested_name = suggested_name;
876         }
877
878         result = modest_platform_run_folder_name_dialog (parent_window, 
879                                                          _("mcen_ti_new_folder"),
880                                                          _("mcen_fi_new_folder_name"),
881                                                          real_suggested_name,
882                                                          folder_name);
883         if (suggested_name == NULL)
884                 g_free(real_suggested_name);
885
886         return result;
887 }
888
889 gint
890 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
891                                           TnyFolderStore *parent_folder,
892                                           const gchar *suggested_name,
893                                           gchar **folder_name)
894 {
895         return modest_platform_run_folder_name_dialog (parent_window, 
896                                                        _("New folder name"),
897                                                        _("Enter new folder name:"),
898                                                        suggested_name,
899                                                        folder_name);
900 }
901
902 gint
903 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
904                                          const gchar *message)
905 {
906         GtkWidget *dialog;
907         gint response;
908
909         dialog = hildon_note_new_confirmation (parent_window, message);
910         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
911
912         response = gtk_dialog_run (GTK_DIALOG (dialog));
913
914         gtk_widget_destroy (GTK_WIDGET (dialog));
915
916         while (gtk_events_pending ())
917                 gtk_main_iteration ();
918
919         return response;
920 }
921
922 gint
923 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
924                                    const gchar *message)
925 {
926         GtkWidget *dialog;
927         gint response;
928
929         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
930                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
931                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
932                                                            NULL);
933         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
934
935         response = gtk_dialog_run (GTK_DIALOG (dialog));
936
937         gtk_widget_destroy (GTK_WIDGET (dialog));
938
939         while (gtk_events_pending ())
940                 gtk_main_iteration ();
941
942         return response;
943 }
944
945 void
946 modest_platform_run_information_dialog (GtkWindow *parent_window,
947                                         const gchar *message)
948 {
949         GtkWidget *dialog;
950
951         dialog = hildon_note_new_information (parent_window, message);
952
953         g_signal_connect_swapped (dialog,
954                                   "response", 
955                                   G_CALLBACK (gtk_widget_destroy),
956                                   dialog);
957
958         gtk_widget_show_all (dialog);
959 }
960
961
962
963 typedef struct
964 {
965         GMainLoop* loop;
966 } UtilIdleData;
967
968 static gboolean 
969 on_idle_connect_and_wait(gpointer user_data)
970 {
971         printf ("DEBUG: %s:\n", __FUNCTION__);
972         TnyDevice *device = modest_runtime_get_device();
973         if (!tny_device_is_online (device)) {
974
975                 /* This is a GDK lock because we are an idle callback and
976                  * tny_maemo_conic_device_connect can contain Gtk+ code */
977
978                 gdk_threads_enter(); /* CHECKED */
979                 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
980                 gdk_threads_leave(); /* CHECKED */
981         }
982         
983         /* Allow the function that requested this idle callback to continue: */
984         UtilIdleData *data = (UtilIdleData*)user_data;
985         if (data->loop)
986                 g_main_loop_quit (data->loop);
987         
988         return FALSE; /* Don't call this again. */
989 }
990
991 static gboolean connect_request_in_progress = FALSE;
992
993 /* This callback is used when connect_and_wait() is already queued as an idle callback.
994  * This can happen because the gtk_dialog_run() for the connection dialog 
995  * (at least in the fake scratchbox version) allows idle handlers to keep running.
996  */
997 static gboolean 
998 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
999 {
1000         gboolean result = FALSE;
1001         TnyDevice *device = modest_runtime_get_device();
1002         if (tny_device_is_online (device))
1003                 result = FALSE; /* Stop trying. */
1004         else {
1005                 /* Keep trying until connect_request_in_progress is FALSE. */
1006                 if (connect_request_in_progress)
1007                         result = TRUE; /* Keep trying */
1008                 else {
1009                         printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
1010                                                 
1011                         result = FALSE; /* Stop trying, now that a result should be available. */
1012                 }
1013         }
1014         
1015         if (result == FALSE) {
1016                 /* Allow the function that requested this idle callback to continue: */
1017                 UtilIdleData *data = (UtilIdleData*)user_data;
1018                 if (data->loop)
1019                         g_main_loop_quit (data->loop);  
1020         }
1021                 
1022         return result;
1023 }
1024
1025 static void 
1026 set_account_to_online (TnyAccount *account)
1027 {
1028         /* TODO: This is necessary to prevent a cancel of the password dialog 
1029          * from making a restart necessary to be asked the password again,
1030          * but it causes a hang:
1031          */
1032         #if 0
1033         if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
1034                 /* Make sure that store accounts are online too, 
1035                  * because tinymail sets accounts to offline if 
1036                  * a password dialog is ever cancelled.
1037                  * We don't do this for transport accounts because 
1038                  * a) They fundamentally need network access, so they can't really be offline.
1039                  * b) That might cause a transport connection to happen too early.
1040                  */
1041                 GError *error = NULL;
1042                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, &error);
1043                 if (error) {
1044                         g_warning ("%s: tny_camel_account_set_online() returned a GError:\n  %s\n", 
1045                                 __FUNCTION__, error->message);
1046                         g_error_free (error);   
1047                 }
1048         }
1049         #endif
1050 }
1051
1052 gboolean modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1053 {
1054         if (connect_request_in_progress)
1055                 return FALSE;
1056                 
1057         printf ("DEBUG: %s:\n", __FUNCTION__);
1058         TnyDevice *device = modest_runtime_get_device();
1059         
1060         if (tny_device_is_online (device)) {
1061                 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1062                 set_account_to_online (account);
1063                 return TRUE;
1064         } else
1065         {
1066                 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1067         }
1068                 
1069         /* This blocks on the result: */
1070         UtilIdleData *data = g_slice_new0 (UtilIdleData);
1071         
1072         GMainContext *context = NULL; /* g_main_context_new (); */
1073         data->loop = g_main_loop_new (context, FALSE /* not running */);
1074         
1075         /* Cause the function to be run in an idle-handler, which is always 
1076          * in the main thread:
1077          */
1078         if (!connect_request_in_progress) {
1079                 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1080                 connect_request_in_progress = TRUE;
1081                 g_idle_add (&on_idle_connect_and_wait, data);
1082         }
1083         else {
1084                 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1085                 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1086         }
1087
1088         /* This main loop will run until the idle handler has stopped it: */
1089         printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1090         GDK_THREADS_LEAVE();
1091         g_main_loop_run (data->loop);
1092         GDK_THREADS_ENTER();
1093         printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1094         connect_request_in_progress = FALSE;
1095         printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1096         g_main_loop_unref (data->loop);
1097         /* g_main_context_unref (context); */
1098
1099         g_slice_free (UtilIdleData, data);
1100
1101         gboolean result = tny_device_is_online (device);
1102
1103         if (result)
1104                 set_account_to_online (account);
1105
1106         return result;
1107 }
1108
1109 gboolean modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1110 {
1111         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1112                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1113                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1114                         /* This must be a maildir account, which does not require a connection: */
1115                         return TRUE;
1116                 }
1117         }
1118
1119         return modest_platform_connect_and_wait (parent_window, account);
1120 }
1121
1122 gboolean modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1123 {
1124         if (!folder_store)
1125                 return TRUE; /* Maybe it is something local. */
1126                 
1127         gboolean result = TRUE;
1128         if (TNY_IS_FOLDER (folder_store)) {
1129                 /* Get the folder's parent account: */
1130                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1131                 if (account != NULL) {
1132                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1133                         g_object_unref (account);
1134                 }
1135         } else if (TNY_IS_ACCOUNT (folder_store)) {
1136                 /* Use the folder store as an account: */
1137                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1138         }
1139
1140         return result;
1141 }
1142
1143 void
1144 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1145                                  ModestSortDialogType type)
1146 {
1147         GtkWidget *dialog = NULL;
1148
1149         /* Build dialog */
1150         dialog = hildon_sort_dialog_new (parent_window);
1151         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1152         
1153         /* Fill sort keys */
1154         switch (type) {
1155         case MODEST_SORT_HEADERS:
1156                 launch_sort_headers_dialog (parent_window, 
1157                                             HILDON_SORT_DIALOG(dialog));
1158                 break;
1159         }
1160         
1161         /* Free */
1162         gtk_widget_destroy (GTK_WIDGET (dialog));
1163 }
1164
1165
1166 gboolean modest_platform_set_update_interval (guint minutes)
1167 {
1168         ModestConf *conf = modest_runtime_get_conf ();
1169         if (!conf)
1170                 return FALSE;
1171                 
1172         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1173
1174         /* Delete any existing alarm,
1175          * because we will replace it: */
1176         if (alarm_cookie) {
1177                 /* TODO: What does the alarm_event_del() return value mean? */
1178                 alarm_event_del(alarm_cookie);
1179                 alarm_cookie = 0;
1180                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1181         }
1182         
1183         /* 0 means no updates: */
1184         if (minutes == 0)
1185                 return TRUE;
1186                 
1187      
1188         /* Register alarm: */
1189         
1190         /* Set the interval in alarm_event_t structure: */
1191         alarm_event_t *event = g_new0(alarm_event_t, 1);
1192         event->alarm_time = minutes * 60; /* seconds */
1193         
1194         /* Set recurrence every few minutes: */
1195         event->recurrence = minutes;
1196         event->recurrence_count = -1; /* Means infinite */
1197
1198         /* Specify what should happen when the alarm happens:
1199          * It should call this D-Bus method: */
1200          
1201         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1202         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1203         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1204         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1205
1206         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1207          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1208          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1209          * This is why we want to use the Alarm API instead of just g_timeout_add().
1210          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1211          */
1212         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1213         
1214         alarm_cookie = alarm_event_add (event);
1215
1216         /* now, free it */
1217         alarm_event_free (event);
1218         
1219         /* Store the alarm ID in GConf, so we can remove it later:
1220          * This is apparently valid between application instances. */
1221         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1222         
1223         if (!alarm_cookie) {
1224             /* Error */
1225             const alarm_error_t alarm_error = alarmd_get_error ();
1226             printf ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1227             
1228             /* Give people some clue: */
1229             /* The alarm API should have a function for this: */
1230             if (alarm_error == ALARMD_ERROR_DBUS) {
1231                 printf ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1232             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1233                 printf ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1234             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1235                 printf ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1236             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1237                 printf ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1238             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1239                 printf ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1240             }
1241             
1242             return FALSE;
1243         }
1244         
1245         return TRUE;
1246 }
1247
1248 GtkWidget * 
1249 modest_platform_get_global_settings_dialog ()
1250 {
1251         return modest_maemo_global_settings_dialog_new ();
1252 }
1253
1254 void 
1255 modest_platform_on_new_msg (void)
1256 {
1257 #ifdef MODEST_HAVE_HILDON_NOTIFY
1258         HildonNotification *not;
1259
1260         /* Create a new notification. FIXME put the right values, need
1261            some more specs */
1262         not = hildon_notification_new ("TODO: (new email) Summary",
1263                                        "TODO: (new email) Description",
1264                                        "qgn_contact_group_chat_invitation",
1265                                        "system.note.dialog");
1266
1267         /* Play sound SR-SND-18. TODO: play the right file */
1268         /* TODO: Where is this declared? hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav"); */
1269
1270         /* Set the led pattern */
1271         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "led-pattern", 3);
1272
1273         /* Notify. We need to do this in an idle because this function
1274            could be called from a thread */
1275         if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1276                 g_error ("Failed to send notification");
1277                 
1278         g_object_unref (not);
1279 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1280 }
1281
1282
1283 void
1284 modest_platform_show_help (GtkWindow *parent_window, 
1285                            const gchar *help_id)
1286 {
1287         osso_return_t result;
1288
1289         g_return_if_fail (help_id);
1290         g_return_if_fail (osso_context);
1291
1292         /* Show help */
1293 #ifdef MODEST_HAVE_OSSO_HELP
1294         result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1295 #else
1296         result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1297 #endif
1298
1299         if (result != OSSO_OK) {
1300                 gchar *error_msg;
1301                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1302                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1303                                                 NULL,
1304                                                 error_msg);
1305                 g_free (error_msg);
1306         }
1307 }
1308
1309 void 
1310 modest_platform_show_search_messages (GtkWindow *parent_window)
1311 {
1312         osso_return_t result = OSSO_ERROR;
1313         
1314         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1315
1316         if (result != OSSO_OK) {
1317                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1318         }
1319 }
1320
1321 void 
1322 modest_platform_show_addressbook (GtkWindow *parent_window)
1323 {
1324         osso_return_t result = OSSO_ERROR;
1325         
1326         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1327
1328         if (result != OSSO_OK) {
1329                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1330         }
1331 }
1332
1333 GtkWidget *
1334 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1335 {
1336         GtkWidget *widget = modest_folder_view_new (query);
1337
1338         /* Show one account by default */
1339         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1340                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1341
1342
1343         /* Restore settings */
1344         modest_widget_memory_restore (modest_runtime_get_conf(), 
1345                                       G_OBJECT (widget),
1346                                       MODEST_CONF_FOLDER_VIEW_KEY);
1347
1348         return widget;
1349 }
1350
1351 void 
1352 modest_platform_information_banner (GtkWidget *parent,
1353                                     const gchar *icon_name,
1354                                     const gchar *text)
1355 {
1356         hildon_banner_show_information (parent, icon_name, text);
1357 }
1358
1359 GtkWidget *
1360 modest_platform_animation_banner (GtkWidget *parent,
1361                                   const gchar *animation_name,
1362                                   const gchar *text)
1363 {
1364         GtkWidget *inf_note = NULL;
1365
1366         g_return_val_if_fail (text != NULL, NULL);
1367
1368         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1369
1370         return inf_note;
1371 }