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