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