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