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