* Added some missing signal disconnections
[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, 
67                                         const gchar *key, 
68                                         ModestConfEvent event,
69                                         ModestConfNotificationId id, 
70                                         gpointer user_data)
71 {
72         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
73                 const guint update_interval_minutes = 
74                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
75                 modest_platform_set_update_interval (update_interval_minutes);
76         }
77 }
78
79 gboolean
80 modest_platform_init (int argc, char *argv[])
81 {
82         osso_hw_state_t hw_state = { 0 };
83         DBusConnection *con;    
84
85         osso_context =
86                 osso_initialize(PACKAGE,PACKAGE_VERSION,
87                                 FALSE, NULL);   
88         if (!osso_context) {
89                 g_printerr ("modest: failed to acquire osso context\n");
90                 return FALSE;
91         }
92
93         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
94                 g_printerr ("modest: could not get dbus connection\n");
95                 return FALSE;
96
97         }
98         
99         /* Add a D-Bus handler to be used when the main osso-rpc 
100          * D-Bus handler has not handled something.
101          * We use this for D-Bus methods that need to use more complex types 
102          * than osso-rpc supports. 
103          */
104         if (!dbus_connection_add_filter (con,
105                                          modest_dbus_req_filter,
106                                          NULL,
107                                          NULL)) {
108
109                 g_printerr ("modest: Could not add D-Bus filter\n");
110                 return FALSE;
111         }
112
113         /* Register our simple D-Bus callbacks, via the osso API: */
114         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
115                                MODEST_DBUS_SERVICE, 
116                                MODEST_DBUS_OBJECT, 
117                                MODEST_DBUS_IFACE,
118                                modest_dbus_req_handler, NULL /* user_data */);
119         if (result != OSSO_OK) {
120                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
121                 return FALSE;
122         }
123
124         /* Add handler for Exit D-BUS messages.
125          * Not used because osso_application_set_exit_cb() is deprecated and obsolete:
126         result = osso_application_set_exit_cb(osso_context,
127                                           modest_dbus_exit_event_handler,
128                                           (gpointer) NULL);
129         if (result != OSSO_OK) {
130                 g_print("Error setting exit callback (%d)\n", result);
131                 return OSSO_ERROR;
132         }
133         */
134
135         /* Register hardware event dbus callback: */
136         hw_state.shutdown_ind = TRUE;
137         osso_hw_set_event_cb(osso_context, NULL,/*&hw_state*/ modest_osso_cb_hw_state_handler, NULL);
138
139         /* Register osso auto-save callbacks: */
140         result = osso_application_set_autosave_cb (osso_context, 
141                 modest_on_osso_application_autosave, NULL /* user_data */);
142         if (result != OSSO_OK) {
143                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
144                 return FALSE;
145         }
146         
147
148         /* Make sure that the update interval is changed whenever its gconf key 
149          * is changed */
150         /* CAUTION: we're not using here the
151            modest_conf_listen_to_namespace because we know that there
152            are other parts of Modest listening for this namespace, so
153            we'll receive the notifications anyway. We basically do not
154            use it because there is no easy way to do the
155            modest_conf_forget_namespace */
156         ModestConf *conf = modest_runtime_get_conf ();
157         g_signal_connect (G_OBJECT(conf),
158                           "key_changed",
159                           G_CALLBACK (on_modest_conf_update_interval_changed), 
160                           NULL);
161                           
162         /* Get the initial update interval from gconf: */
163         on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
164                                                MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
165
166         /* initialize the addressbook */
167         if (!osso_abook_init (&argc, &argv, osso_context)) {
168                 g_printerr ("modest: failed to initialized addressbook\n");
169                 return FALSE;
170         }
171                 
172         return TRUE;
173 }
174
175 TnyDevice*
176 modest_platform_get_new_device (void)
177 {
178         return TNY_DEVICE (tny_maemo_conic_device_new ());
179 }
180
181
182 const gchar*
183 guess_mime_type_from_name (const gchar* name)
184 {
185         int i;
186         const gchar* ext;
187         const static gchar* octet_stream= "application/octet-stream";
188         const static gchar* mime_map[][2] = {
189                 { "pdf",  "application/pdf"},
190                 { "doc",  "application/msword"},
191                 { "xls",  "application/excel"},
192                 { "png",  "image/png" },
193                 { "gif",  "image/gif" },
194                 { "jpg",  "image/jpeg"},
195                 { "jpeg", "image/jpeg"},
196                 { "mp3",  "audio/mp3" }
197         };
198
199         if (!name)
200                 return octet_stream;
201         
202         ext = g_strrstr (name, ".");
203         if (!ext)
204                 return octet_stream;
205         
206         for (i = 0; i != G_N_ELEMENTS(mime_map); ++i) {
207                 if (!g_ascii_strcasecmp (mime_map[i][0], ext + 1)) /* +1: ignore '.'*/
208                         return mime_map[i][1];
209         }
210         return octet_stream;
211 }
212
213
214 gchar*
215 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
216                                     gchar **effective_mime_type)
217 {
218         GString *mime_str = NULL;
219         gchar *icon_name  = NULL;
220         gchar **icons, **cursor;
221
222         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
223                 mime_str = g_string_new (guess_mime_type_from_name(name));
224         else {
225                 mime_str = g_string_new (mime_type);
226                 g_string_ascii_down (mime_str);
227         }
228
229 #ifdef MODEST_HAVE_OSSO_MIME
230         icons = osso_mime_get_icon_names (mime_str->str, NULL);
231 #else
232         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
233 #endif /*MODEST_HAVE_OSSO_MIME*/
234         for (cursor = icons; cursor; ++cursor) {
235                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
236                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
237                         icon_name = g_strdup ("qgn_list_messagin");
238                         break;
239                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
240                         icon_name = g_strdup (*cursor);
241                         break;
242                 }
243         }
244         g_strfreev (icons);
245
246         if (effective_mime_type)
247                 *effective_mime_type = g_string_free (mime_str, FALSE);
248         else
249                 g_string_free (mime_str, TRUE);
250
251         return icon_name;
252 }
253
254
255
256
257 #ifdef MODEST_HAVE_OSSO_MIME
258 gboolean 
259 modest_platform_activate_uri (const gchar *uri)
260 {
261         OssoURIAction *action;
262         gboolean result = FALSE;
263         GSList *actions, *iter = NULL;
264         const gchar *scheme;
265         
266         g_return_val_if_fail (uri, FALSE);
267         if (!uri)
268                 return FALSE;
269
270         /* the default action should be email */
271         scheme = osso_uri_get_scheme_from_uri (uri, NULL);
272         actions = osso_uri_get_actions (scheme, NULL);
273         
274         for (iter = actions; iter; iter = g_slist_next (iter)) {
275                 action = (OssoURIAction*) iter->data;
276                 if (action && strcmp (osso_uri_action_get_name (action), "uri_link_compose_email") == 0) {
277                         GError *err = NULL;
278                         result = osso_uri_open (uri, action, &err);
279                         if (!result && err) {
280                                 g_printerr ("modest: modest_platform_activate_uri : %s",
281                                             err->message ? err->message : "unknown error");
282                                 g_error_free (err);
283                         }
284                         break;
285                 }
286         }
287
288         /* if we could open it with email, try something else */
289         if (!result)
290                 result = osso_uri_open (uri, NULL, NULL);       
291         
292                         
293         if (!result)
294                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
295         return result;
296 }
297
298 #else /* !MODEST_HAVE_OSSO_MIME*/
299
300 gboolean 
301 modest_platform_activate_uri (const gchar *uri)
302 {
303         HildonURIAction *action;
304         gboolean result = FALSE;
305         GSList *actions, *iter = NULL;
306         const gchar *scheme;
307         
308         g_return_val_if_fail (uri, FALSE);
309         if (!uri)
310                 return FALSE;
311
312         scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
313         actions = hildon_uri_get_actions (scheme, NULL);
314         
315         for (iter = actions; iter; iter = g_slist_next (iter)) {
316                 action = (HildonURIAction*) iter->data;
317                 if (action && strcmp (hildon_uri_action_get_service (action), "com.nokia.modest") == 0) {
318                         GError *err = NULL;
319                         result = hildon_uri_open (uri, action, &err);
320                         if (!result && err) {
321                                 g_printerr ("modest: modest_platform_activate_uri : %s",
322                                             err->message ? err->message : "unknown error");
323                                 g_error_free (err);
324                         }
325                         break;
326                 }
327         }
328         
329         /* if we could open it with email, try something else */
330         if (!result)
331                 result = hildon_uri_open (uri, NULL, NULL);     
332                 
333         if (!result)
334                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
335         
336         return result;
337 }
338
339
340 #endif /* MODEST_HAVE_OSSO_MIME*/
341
342 gboolean 
343 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
344 {
345         gint result;
346         DBusConnection *con;
347         gchar *uri_path = NULL;
348         GString *mime_str = NULL;
349
350         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
351                 mime_str = g_string_new (guess_mime_type_from_name(path));
352         else {
353                 mime_str = g_string_new (mime_type);
354                 g_string_ascii_down (mime_str);
355         }
356
357         uri_path = g_strconcat ("file://", path, NULL);
358         
359         con = osso_get_dbus_connection (osso_context);
360 #ifdef MODEST_HAVE_OSSO_MIME
361         result = osso_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
362 #else
363         result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
364 #endif /*MODEST_HAVE_OSSO_MIME*/
365         g_string_free (mime_str, TRUE);
366
367         if (result != 1)
368                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
369         return result != 1;
370 }
371
372 typedef struct  {
373         GSList *actions;
374         gchar  *uri;
375 } ModestPlatformPopupInfo;
376
377 static gboolean
378 delete_uri_popup (GtkWidget *menu,
379                   GdkEvent *event,
380                   gpointer userdata)
381 {
382         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
383
384         g_free (popup_info->uri);
385 #ifdef MODEST_HAVE_OSSO_MIME
386         osso_uri_free_actions (popup_info->actions);
387 #else
388         hildon_uri_free_actions (popup_info->actions);
389 #endif /*MODEST_HAVE_OSSO_MIME*/
390         return FALSE;
391 }
392
393 static void
394 activate_uri_popup_item (GtkMenuItem *menu_item,
395                          gpointer userdata)
396 {
397         GSList *node;
398         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
399         const gchar* action_name;
400
401         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
402         if (!action_name) {
403                 g_printerr ("modest: no action name defined\n");
404                 return;
405         }
406
407         /* special handling for the copy menu item -- copy the uri to the clipboard */
408         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
409         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
410                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
411                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
412
413                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
414                         action_name += strlen ("mailto:");
415                 
416                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
417                 return; /* we're done */
418         }
419         
420         /* now, the real uri-actions... */
421         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
422 #ifdef MODEST_HAVE_OSSO_MIME
423                 OssoURIAction *action = (OssoURIAction *) node->data;
424                 if (strcmp (action_name, osso_uri_action_get_name (action))==0) {
425                         osso_uri_open (popup_info->uri, action, NULL);
426                         break;
427                 }
428 #else
429                 HildonURIAction *action = (HildonURIAction *) node->data;
430                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
431                         hildon_uri_open (popup_info->uri, action, NULL);
432                         break;
433                 }
434 #endif /*MODEST_HAVE_OSSO_MIME*/
435         }
436 }
437
438 gboolean 
439 modest_platform_show_uri_popup (const gchar *uri)
440 {
441         gchar *scheme;
442         GSList *actions_list;
443
444         if (uri == NULL)
445                 return FALSE;
446         
447 #ifdef MODEST_HAVE_OSSO_MIME
448         scheme = osso_uri_get_scheme_from_uri (uri, NULL);
449         actions_list = osso_uri_get_actions (scheme, NULL);
450 #else
451         scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
452         actions_list = hildon_uri_get_actions (scheme, NULL);
453 #endif /* MODEST_HAVE_OSSO_MIME */
454         if (actions_list != NULL) {
455                 GSList *node;
456                 GtkWidget *menu = gtk_menu_new ();
457                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
458
459                 popup_info->actions = actions_list;
460                 popup_info->uri = g_strdup (uri);
461               
462                 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
463                         GtkWidget *menu_item;
464                         const gchar *action_name;
465                         const gchar *translation_domain;
466 #ifdef MODEST_HAVE_OSSO_MIME
467                         OssoURIAction *action = (OssoURIAction *) node->data;
468                         action_name = osso_uri_action_get_name (action);
469                         translation_domain = osso_uri_action_get_translation_domain (action);
470                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain,action_name));
471                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);
472                         /* hack, we add it as a gobject property*/
473                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
474                                           popup_info);
475                         
476                         if (osso_uri_is_default_action (action, NULL)) {
477                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
478                         } else {
479                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
480                         }
481 #else
482                         HildonURIAction *action = (HildonURIAction *) node->data;
483                         action_name = hildon_uri_action_get_name (action);
484                         translation_domain = hildon_uri_action_get_translation_domain (action);
485                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
486                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
487                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
488                                           popup_info);
489                                                                   
490                         if (hildon_uri_is_default_action (action, NULL)) {
491                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
492                         } else {
493                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
494                         }
495 #endif /*MODEST_HAVE_OSSO_MIME*/
496                         gtk_widget_show (menu_item);
497                 }
498
499                 /* always add the copy item */
500                 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
501                 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
502                                         g_strconcat (URI_ACTION_COPY, uri, NULL),
503                                         g_free);
504                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
505                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
506                 gtk_widget_show (menu_item);
507
508                 
509                 /* and what to do when the link is deleted */
510                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
511                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
512                                                   
513         } else {
514                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
515         }
516         
517         g_free (scheme);
518         return TRUE;
519 }
520
521
522 GdkPixbuf*
523 modest_platform_get_icon (const gchar *name)
524 {
525         GError *err = NULL;
526         GdkPixbuf* pixbuf = NULL;
527         GtkIconTheme *current_theme = NULL;
528
529         g_return_val_if_fail (name, NULL);
530         
531         if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
532                 pixbuf = gdk_pixbuf_new_from_file (name, &err);
533                 if (!pixbuf) {
534                         g_printerr ("modest: error loading icon '%s': %s\n",
535                                     name, err->message);
536                         g_error_free (err);
537                         return NULL;
538                 }
539                 return pixbuf;
540         }
541
542         current_theme = gtk_icon_theme_get_default ();
543         pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
544                                            GTK_ICON_LOOKUP_NO_SVG,
545                                            &err);
546         if (!pixbuf) {
547                 g_printerr ("modest: error loading theme icon '%s': %s\n",
548                             name, err->message);
549                 g_error_free (err);
550         } 
551         return pixbuf;
552 }
553
554 const gchar*
555 modest_platform_get_app_name (void)
556 {
557         return _("mcen_ap_name");
558 }
559
560 static void 
561 entry_insert_text (GtkEditable *editable,
562                    const gchar *text,
563                    gint         length,
564                    gint        *position,
565                    gpointer     data)
566 {
567         gchar *chars;
568         gint chars_length;
569
570         chars = gtk_editable_get_chars (editable, 0, -1);
571         chars_length = g_utf8_strlen (chars, -1);
572
573         /* Show WID-INF036 */
574         if (chars_length >= 20) {
575                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
576                                                  _CS("ckdg_ib_maximum_characters_reached"));
577         } else {
578                 if (chars_length == 0) {
579                         /* A blank space is not valid as first character */
580                         if (strcmp (text, " ")) {
581                                 GtkWidget *ok_button;
582                                 GList *buttons;
583
584                                 /* Show OK button */
585                                 buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
586                                 ok_button = GTK_WIDGET (buttons->next->data);
587                                 gtk_widget_set_sensitive (ok_button, TRUE);
588                                 g_list_free (buttons);
589                         }
590                 }
591
592                 /* Write the text in the entry */
593                 g_signal_handlers_block_by_func (editable,
594                                                  (gpointer) entry_insert_text, data);
595                 gtk_editable_insert_text (editable, text, length, position);
596                 g_signal_handlers_unblock_by_func (editable,
597                                                    (gpointer) entry_insert_text, data);
598         }
599         /* Do not allow further processing */
600         g_signal_stop_emission_by_name (editable, "insert_text");
601 }
602
603 static void
604 entry_changed (GtkEditable *editable,
605                gpointer     user_data)
606 {
607         gchar *chars;
608
609         chars = gtk_editable_get_chars (editable, 0, -1);
610         g_return_if_fail (chars != NULL);
611
612         /* Dimm OK button. Do not allow also the "/" */
613         if (strlen (chars) == 0 || strchr (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                 gtk_widget_set_sensitive (ok_button, FALSE);
620
621                 g_list_free (buttons);
622         } else if (g_utf8_strlen (chars,-1) >= 21)
623                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
624                                                  _CS("ckdg_ib_maximum_characters_reached"));            
625         /* Free */
626         g_free (chars);
627 }
628
629 static void
630 launch_sort_headers_dialog (GtkWindow *parent_window,
631                             HildonSortDialog *dialog)
632 {
633         ModestHeaderView *header_view = NULL;
634         GList *cols = NULL;
635         GtkSortType sort_type;
636         gint sort_key;
637         gint default_key = 0;
638         gint result;
639         gboolean outgoing = FALSE;
640         gint current_sort_colid = -1;
641         GtkSortType current_sort_type;
642         gint attachments_sort_id;
643         gint priority_sort_id;
644         GtkTreeSortable *sortable;
645         
646         /* Get header window */
647         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
648                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
649                                                                                       MODEST_WIDGET_TYPE_HEADER_VIEW));
650         }
651         if (!header_view) return;
652
653         /* Add sorting keys */
654         cols = modest_header_view_get_columns (header_view);
655         if (cols == NULL) return;
656         int sort_model_ids[6];
657         int sort_ids[6];
658
659
660         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
661                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
662
663         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
664         if (outgoing) {
665                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
666                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
667         } else {
668                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
669                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
670         }
671
672         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
673         if (outgoing) {
674                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
675                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
676         } else {
677                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
678                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
679         }
680         default_key = sort_key;
681
682         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
683         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
684         if (outgoing)
685                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
686         else
687                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
688
689         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
690         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
691         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
692         attachments_sort_id = sort_key;
693
694         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
695         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
696         sort_ids[sort_key] = 0;
697
698         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
699         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
700         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
701         priority_sort_id = sort_key;
702
703         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
704         /* Launch dialogs */
705         if (!gtk_tree_sortable_get_sort_column_id (sortable,
706                                                    &current_sort_colid, &current_sort_type)) {
707                 hildon_sort_dialog_set_sort_key (dialog, default_key);
708                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
709         } else {
710                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
711                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
712                         gpointer flags_sort_type_pointer;
713                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
714                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
715                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
716                         else
717                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
718                 } else {
719                         gint current_sort_keyid = 0;
720                         while (current_sort_keyid < 6) {
721                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
722                                         break;
723                                 else 
724                                         current_sort_keyid++;
725                         }
726                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
727                 }
728         }
729
730         result = gtk_dialog_run (GTK_DIALOG (dialog));
731         if (result == GTK_RESPONSE_OK) {
732                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
733                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
734                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
735                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
736                                            GINT_TO_POINTER (sort_ids[sort_key]));
737                         /* This is a hack to make it resort rows always when flag fields are
738                          * selected. If we do not do this, changing sort field from priority to
739                          * attachments does not work */
740                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
741                 } else {
742                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
743                                                                  sort_model_ids[sort_key]);
744                 }
745
746                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
747                 gtk_tree_sortable_sort_column_changed (sortable);
748         }
749
750         modest_widget_memory_save (modest_runtime_get_conf (),
751                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
752         
753         while (gtk_events_pending ())
754                 gtk_main_iteration ();
755
756         /* free */
757         g_list_free(cols);      
758 }
759
760 static gint
761 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
762                                         const gchar *dialog_title,
763                                         const gchar *label_text,
764                                         const gchar *suggested_name,
765                                         gchar **folder_name)
766 {
767         GtkWidget *accept_btn = NULL; 
768         GtkWidget *dialog, *entry, *label, *hbox;
769         GList *buttons = NULL;
770         gint result;
771
772         /* Ask the user for the folder name */
773         dialog = gtk_dialog_new_with_buttons (dialog_title,
774                                               parent_window,
775                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
776                                               GTK_STOCK_OK,
777                                               GTK_RESPONSE_ACCEPT,
778                                               GTK_STOCK_CANCEL,
779                                               GTK_RESPONSE_REJECT,
780                                               NULL);
781
782         /* Add accept button (with unsensitive handler) */
783         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
784         accept_btn = GTK_WIDGET (buttons->next->data);
785         g_signal_connect (G_OBJECT (accept_btn), "insensitive-press", 
786                           G_CALLBACK (folder_name_insensitive_press), 
787                           parent_window);
788
789         /* Create label and entry */
790         label = gtk_label_new (label_text);
791         /* TODO: check that the suggested name does not exist */
792         /* We set 21 as maximum because we want to show WID-INF036
793            when the user inputs more that 20 */
794         entry = gtk_entry_new_with_max_length (21);
795         if (suggested_name)
796                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
797         else
798                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
799         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
800
801         /* Track entry changes */
802         g_signal_connect (entry,
803                           "insert-text",
804                           G_CALLBACK (entry_insert_text),
805                           dialog);
806         g_signal_connect (entry,
807                           "changed",
808                           G_CALLBACK (entry_changed),
809                           dialog);
810
811         /* Create the hbox */
812         hbox = gtk_hbox_new (FALSE, 12);
813         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
814         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
815
816         /* Add hbox to dialog */
817         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
818                             hbox, FALSE, FALSE, 0);
819         
820         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
821         
822         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
823         result = gtk_dialog_run (GTK_DIALOG(dialog));
824         if (result == GTK_RESPONSE_ACCEPT)
825                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
826
827         gtk_widget_destroy (dialog);
828
829         while (gtk_events_pending ())
830                 gtk_main_iteration ();
831
832         return result;
833 }
834
835 static void  
836 folder_name_insensitive_press (GtkWidget *widget, ModestWindow *window)
837 {
838         hildon_banner_show_information (NULL, NULL, _CS("ckdg_ib_enter_name"));
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 }