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