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