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