* explicitely undim the Ok-button after an entry_changed, so Ok is enabled
[modest] / src / maemo / modest-platform.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <config.h>
31 #include <glib/gi18n.h>
32 #include <modest-platform.h>
33 #include <modest-runtime.h>
34 #include <modest-main-window.h>
35 #include <modest-header-view.h>
36 #include "maemo/modest-maemo-global-settings-dialog.h"
37 #include "modest-widget-memory.h"
38 #include <modest-hildon-includes.h>
39 #include <osso-helplib.h>
40 #include <dbus_api/modest-dbus-callbacks.h>
41 #include <libosso-abook/osso-abook.h>
42 #include <maemo/modest-osso-autosave-callbacks.h>
43 #include <libosso.h>
44 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
45 #include <tny-maemo-conic-device.h>
46 #include <tny-simple-list.h>
47 #include <tny-folder.h>
48 #include <tny-camel-imap-store-account.h>
49 #include <tny-camel-pop-store-account.h>
50 #include <gtk/gtkicontheme.h>
51 #include <gtk/gtkmenuitem.h>
52 #include <gtk/gtkmain.h>
53 #include <modest-text-utils.h>
54 #include <string.h>
55
56
57 #define HILDON_OSSO_URI_ACTION "uri-action"
58 #define URI_ACTION_COPY "copy:"
59
60 static osso_context_t *osso_context = NULL;
61
62 static void     
63 on_modest_conf_update_interval_changed (ModestConf* self, const gchar *key, 
64         ModestConfEvent event, gpointer user_data)
65 {
66         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
67                 const guint update_interval_minutes = 
68                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
69                 modest_platform_set_update_interval (update_interval_minutes);
70         }
71 }
72
73 gboolean
74 modest_platform_init (int argc, char *argv[])
75 {
76         osso_hw_state_t hw_state = { 0 };
77         DBusConnection *con;    
78
79         osso_context =
80                 osso_initialize(PACKAGE,PACKAGE_VERSION,
81                                 FALSE, NULL);   
82         if (!osso_context) {
83                 g_printerr ("modest: failed to acquire osso context\n");
84                 return FALSE;
85         }
86
87         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
88                 g_printerr ("modest: could not get dbus connection\n");
89                 return FALSE;
90
91         }
92         
93         /* Add a D-Bus handler to be used when the main osso-rpc 
94          * D-Bus handler has not handled something.
95          * We use this for D-Bus methods that need to use more complex types 
96          * than osso-rpc supports. 
97          */
98         if (!dbus_connection_add_filter (con,
99                                          modest_dbus_req_filter,
100                                          NULL,
101                                          NULL)) {
102
103                 g_printerr ("modest: Could not add D-Bus filter\n");
104                 return FALSE;
105         }
106
107         /* Register our simple D-Bus callbacks, via the osso API: */
108         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
109                                MODEST_DBUS_SERVICE, 
110                                MODEST_DBUS_OBJECT, 
111                                MODEST_DBUS_IFACE,
112                                modest_dbus_req_handler, NULL /* user_data */);
113         if (result != OSSO_OK) {
114                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
115                 return FALSE;
116         }
117
118         /* Add handler for Exit D-BUS messages.
119          * Not used because osso_application_set_exit_cb() is deprecated and obsolete:
120         result = osso_application_set_exit_cb(osso_context,
121                                           modest_dbus_exit_event_handler,
122                                           (gpointer) NULL);
123         if (result != OSSO_OK) {
124                 g_print("Error setting exit callback (%d)\n", result);
125                 return OSSO_ERROR;
126         }
127         */
128
129         /* Register hardware event dbus callback: */
130         hw_state.shutdown_ind = TRUE;
131         osso_hw_set_event_cb(osso_context, NULL,/*&hw_state*/ modest_osso_cb_hw_state_handler, NULL);
132
133         /* Register osso auto-save callbacks: */
134         result = osso_application_set_autosave_cb (osso_context, 
135                 modest_on_osso_application_autosave, NULL /* user_data */);
136         if (result != OSSO_OK) {
137                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
138                 return FALSE;
139         }
140         
141
142         /* Make sure that the update interval is changed whenever its gconf key 
143          * is changed: */
144         ModestConf *conf = modest_runtime_get_conf ();
145         g_signal_connect (G_OBJECT(conf),
146                           "key_changed",
147                           G_CALLBACK (on_modest_conf_update_interval_changed), 
148                           NULL);
149                           
150         /* Get the initial update interval from gconf: */
151         on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
152                 MODEST_CONF_EVENT_KEY_CHANGED, NULL);
153
154         /* initialize the addressbook */
155         if (!osso_abook_init (&argc, &argv, osso_context)) {
156                 g_printerr ("modest: failed to initialized addressbook\n");
157                 return FALSE;
158         }
159                 
160         return TRUE;
161 }
162
163 TnyDevice*
164 modest_platform_get_new_device (void)
165 {
166         return TNY_DEVICE (tny_maemo_conic_device_new ());
167 }
168
169
170 const gchar*
171 guess_mime_type_from_name (const gchar* name)
172 {
173         int i;
174         const gchar* ext;
175         const static gchar* octet_stream= "application/octet-stream";
176         const static gchar* mime_map[][2] = {
177                 { "pdf",  "application/pdf"},
178                 { "doc",  "application/msword"},
179                 { "xls",  "application/excel"},
180                 { "png",  "image/png" },
181                 { "gif",  "image/gif" },
182                 { "jpg",  "image/jpeg"},
183                 { "jpeg", "image/jpeg"},
184                 { "mp3",  "audio/mp3" }
185         };
186
187         if (!name)
188                 return octet_stream;
189         
190         ext = g_strrstr (name, ".");
191         if (!ext)
192                 return octet_stream;
193         
194         for (i = 0; i != G_N_ELEMENTS(mime_map); ++i) {
195                 if (!g_ascii_strcasecmp (mime_map[i][0], ext + 1)) /* +1: ignore '.'*/
196                         return mime_map[i][1];
197         }
198         return octet_stream;
199 }
200
201
202 gchar*
203 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
204                                     gchar **effective_mime_type)
205 {
206         GString *mime_str = NULL;
207         gchar *icon_name  = NULL;
208         gchar **icons, **cursor;
209
210         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
211                 mime_str = g_string_new (guess_mime_type_from_name(name));
212         else {
213                 mime_str = g_string_new (mime_type);
214                 g_string_ascii_down (mime_str);
215         }
216
217 #ifdef MODEST_HAVE_OSSO_MIME
218         icons = osso_mime_get_icon_names (mime_str->str, NULL);
219 #else
220         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
221 #endif /*MODEST_HAVE_OSSO_MIME*/
222         for (cursor = icons; cursor; ++cursor) {
223                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
224                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
225                         icon_name = g_strdup ("qgn_list_messagin");
226                         break;
227                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
228                         icon_name = g_strdup (*cursor);
229                         break;
230                 }
231         }
232         g_strfreev (icons);
233
234         if (effective_mime_type)
235                 *effective_mime_type = g_string_free (mime_str, FALSE);
236         else
237                 g_string_free (mime_str, TRUE);
238
239         return icon_name;
240 }
241
242
243
244
245 #ifdef MODEST_HAVE_OSSO_MIME
246 gboolean 
247 modest_platform_activate_uri (const gchar *uri)
248 {
249         OssoURIAction *action;
250         gboolean result = FALSE;
251         GSList *actions, *iter = NULL;
252         const gchar *scheme;
253         
254         g_return_val_if_fail (uri, FALSE);
255         if (!uri)
256                 return FALSE;
257
258         /* the default action should be email */
259         scheme = osso_uri_get_scheme_from_uri (uri, NULL);
260         actions = osso_uri_get_actions (scheme, NULL);
261         
262         for (iter = actions; iter; iter = g_slist_next (iter)) {
263                 action = (OssoURIAction*) iter->data;
264                 if (action && strcmp (osso_uri_action_get_name (action), "uri_link_compose_email") == 0) {
265                         GError *err = NULL;
266                         result = osso_uri_open (uri, action, &err);
267                         if (!result && err) {
268                                 g_printerr ("modest: modest_platform_activate_uri : %s",
269                                             err->message ? err->message : "unknown error");
270                                 g_error_free (err);
271                         }
272                         break;
273                 }
274         }
275
276         /* if we could open it with email, try something else */
277         if (!result)
278                 result = osso_uri_open (uri, NULL, NULL);       
279         
280                         
281         if (!result)
282                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
283         return result;
284 }
285
286 #else /* !MODEST_HAVE_OSSO_MIME*/
287
288 gboolean 
289 modest_platform_activate_uri (const gchar *uri)
290 {
291         HildonURIAction *action;
292         gboolean result = FALSE;
293         GSList *actions, *iter = NULL;
294         const gchar *scheme;
295         
296         g_return_val_if_fail (uri, FALSE);
297         if (!uri)
298                 return FALSE;
299
300         scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
301         actions = hildon_uri_get_actions (scheme, NULL);
302         
303         for (iter = actions; iter; iter = g_slist_next (iter)) {
304                 action = (HildonURIAction*) iter->data;
305                 if (action && strcmp (hildon_uri_action_get_service (action), "com.nokia.modest") == 0) {
306                         GError *err = NULL;
307                         result = hildon_uri_open (uri, action, &err);
308                         if (!result && err) {
309                                 g_printerr ("modest: modest_platform_activate_uri : %s",
310                                             err->message ? err->message : "unknown error");
311                                 g_error_free (err);
312                         }
313                         break;
314                 }
315         }
316         
317         /* if we could open it with email, try something else */
318         if (!result)
319                 result = hildon_uri_open (uri, NULL, NULL);     
320                 
321         if (!result)
322                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
323         
324         return result;
325 }
326
327
328 #endif /* MODEST_HAVE_OSSO_MIME*/
329
330 gboolean 
331 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
332 {
333         gint result;
334         DBusConnection *con;
335         gchar *uri_path = NULL;
336         GString *mime_str = NULL;
337
338         if (!mime_type || !g_ascii_strcasecmp (mime_type, "application/octet-stream")) 
339                 mime_str = g_string_new (guess_mime_type_from_name(path));
340         else {
341                 mime_str = g_string_new (mime_type);
342                 g_string_ascii_down (mime_str);
343         }
344
345         uri_path = g_strconcat ("file://", path, NULL);
346         
347         con = osso_get_dbus_connection (osso_context);
348 #ifdef MODEST_HAVE_OSSO_MIME
349         result = osso_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
350 #else
351         result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_str->str);
352 #endif /*MODEST_HAVE_OSSO_MIME*/
353         g_string_free (mime_str, TRUE);
354
355         if (result != 1)
356                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
357         return result != 1;
358 }
359
360 typedef struct  {
361         GSList *actions;
362         gchar  *uri;
363 } ModestPlatformPopupInfo;
364
365 static gboolean
366 delete_uri_popup (GtkWidget *menu,
367                   GdkEvent *event,
368                   gpointer userdata)
369 {
370         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
371
372         g_free (popup_info->uri);
373 #ifdef MODEST_HAVE_OSSO_MIME
374         osso_uri_free_actions (popup_info->actions);
375 #else
376         hildon_uri_free_actions (popup_info->actions);
377 #endif /*MODEST_HAVE_OSSO_MIME*/
378         return FALSE;
379 }
380
381 static void
382 activate_uri_popup_item (GtkMenuItem *menu_item,
383                          gpointer userdata)
384 {
385         GSList *node;
386         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
387         const gchar* action_name;
388
389         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
390         if (!action_name) {
391                 g_printerr ("modest: no action name defined\n");
392                 return;
393         }
394
395         /* special handling for the copy menu item -- copy the uri to the clipboard */
396         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
397         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
398                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
399                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
400
401                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
402                         action_name += strlen ("mailto:");
403                 
404                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
405                 return; /* we're done */
406         }
407         
408         /* now, the real uri-actions... */
409         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
410 #ifdef MODEST_HAVE_OSSO_MIME
411                 OssoURIAction *action = (OssoURIAction *) node->data;
412                 if (strcmp (action_name, osso_uri_action_get_name (action))==0) {
413                         osso_uri_open (popup_info->uri, action, NULL);
414                         break;
415                 }
416 #else
417                 HildonURIAction *action = (HildonURIAction *) node->data;
418                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
419                         hildon_uri_open (popup_info->uri, action, NULL);
420                         break;
421                 }
422 #endif /*MODEST_HAVE_OSSO_MIME*/
423         }
424 }
425
426 gboolean 
427 modest_platform_show_uri_popup (const gchar *uri)
428 {
429         gchar *scheme;
430         GSList *actions_list;
431
432         if (uri == NULL)
433                 return FALSE;
434         
435 #ifdef MODEST_HAVE_OSSO_MIME
436         scheme = osso_uri_get_scheme_from_uri (uri, NULL);
437         actions_list = osso_uri_get_actions (scheme, NULL);
438 #else
439         scheme = hildon_uri_get_scheme_from_uri (uri, NULL);
440         actions_list = hildon_uri_get_actions (scheme, NULL);
441 #endif /* MODEST_HAVE_OSSO_MIME */
442         if (actions_list != NULL) {
443                 GSList *node;
444                 GtkWidget *menu = gtk_menu_new ();
445                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
446
447                 popup_info->actions = actions_list;
448                 popup_info->uri = g_strdup (uri);
449               
450                 for (node = actions_list; node != NULL; node = g_slist_next (node)) {
451                         GtkWidget *menu_item;
452                         const gchar *action_name;
453                         const gchar *translation_domain;
454 #ifdef MODEST_HAVE_OSSO_MIME
455                         OssoURIAction *action = (OssoURIAction *) node->data;
456                         action_name = osso_uri_action_get_name (action);
457                         translation_domain = osso_uri_action_get_translation_domain (action);
458                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain,action_name));
459                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);
460                         /* hack, we add it as a gobject property*/
461                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
462                                           popup_info);
463                         
464                         if (osso_uri_is_default_action (action, NULL)) {
465                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
466                         } else {
467                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
468                         }
469 #else
470                         HildonURIAction *action = (HildonURIAction *) node->data;
471                         action_name = hildon_uri_action_get_name (action);
472                         translation_domain = hildon_uri_action_get_translation_domain (action);
473                         menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
474                         g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
475                         g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
476                                           popup_info);
477                                                                   
478                         if (hildon_uri_is_default_action (action, NULL)) {
479                                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
480                         } else {
481                                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
482                         }
483 #endif /*MODEST_HAVE_OSSO_MIME*/
484                         gtk_widget_show (menu_item);
485                 }
486
487                 /* always add the copy item */
488                 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", "uri_link_copy_link_location"));
489                 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
490                                         g_strconcat (URI_ACTION_COPY, uri, NULL),
491                                         g_free);
492                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
493                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
494                 gtk_widget_show (menu_item);
495
496                 
497                 /* and what to do when the link is deleted */
498                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
499                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
500                                                   
501         } else {
502                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
503         }
504         
505         g_free (scheme);
506         return TRUE;
507 }
508
509
510 GdkPixbuf*
511 modest_platform_get_icon (const gchar *name)
512 {
513         GError *err = NULL;
514         GdkPixbuf* pixbuf = NULL;
515         GtkIconTheme *current_theme = NULL;
516
517         g_return_val_if_fail (name, NULL);
518
519 #if 0 /* do we still need this? */
520         if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
521                 pixbuf = gdk_pixbuf_new_from_file (name, &err);
522                 if (!pixbuf) {
523                         g_printerr ("modest: error loading icon '%s': %s\n",
524                                     name, err->message);
525                         g_error_free (err);
526                         return NULL;
527                 }
528                 return pixbuf;
529         }
530 #endif /* */
531         current_theme = gtk_icon_theme_get_default ();
532         pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
533                                            GTK_ICON_LOOKUP_NO_SVG,
534                                            &err);
535         if (!pixbuf) {
536                 g_printerr ("modest: error loading theme icon '%s': %s\n",
537                             name, err->message);
538                 g_error_free (err);
539         } 
540         return pixbuf;
541 }
542
543 const gchar*
544 modest_platform_get_app_name (void)
545 {
546         return _("mcen_ap_name");
547 }
548
549 static void 
550 entry_insert_text (GtkEditable *editable,
551                    const gchar *text,
552                    gint         length,
553                    gint        *position,
554                    gpointer     data)
555 {
556         gchar *chars;
557         gint chars_length;
558
559         chars = gtk_editable_get_chars (editable, 0, -1);
560         chars_length = g_utf8_strlen (chars, -1);
561
562         /* Show WID-INF036 */
563         if (chars_length >= 20) {
564                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
565                                                  _CS("ckdg_ib_maximum_characters_reached"));
566         } else {
567                 gboolean is_valid = FALSE;
568
569                 if (!text)
570                         is_valid = FALSE;
571                 else if (strlen(text) == 0 && g_str_has_prefix (chars, " "))
572                         is_valid = FALSE;
573                 else
574                         is_valid = !g_str_has_prefix(text, " ");
575                 
576                 /* A blank space is not valid as first character */
577                 if (is_valid) {
578                         GtkWidget *ok_button;
579                         GList *buttons;
580                         
581                         /* Show OK button */
582                         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (data)->action_area));
583                         ok_button = GTK_WIDGET (buttons->next->data);
584                                 gtk_widget_set_sensitive (ok_button, TRUE);
585                                 g_list_free (buttons);
586                 
587                 }
588
589                 /* Write the text in the entry */
590                 g_signal_handlers_block_by_func (editable,
591                                                  (gpointer) entry_insert_text, data);
592                 gtk_editable_insert_text (editable, text, length, position);
593                 g_signal_handlers_unblock_by_func (editable,
594                                                    (gpointer) entry_insert_text, data);
595         }
596         /* Do not allow further processing */
597         g_signal_stop_emission_by_name (editable, "insert_text");
598 }
599
600 static void
601 entry_changed (GtkEditable *editable,
602                gpointer     user_data)
603 {
604         gchar *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         
611         chars = gtk_editable_get_chars (editable, 0, -1);
612         g_return_if_fail (chars != NULL);
613
614         
615         /* Dimm OK button. Do not allow also the "/" */
616         if (strlen (chars) == 0 || strchr (chars, '/')) 
617                 gtk_widget_set_sensitive (ok_button, FALSE);
618                 
619         else if (g_utf8_strlen (chars,-1) >= 21)
620                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
621                                                  _CS("ckdg_ib_maximum_characters_reached"));
622         else /* explicitely enable it, because with a previous entry_changed, it might have been turned off */
623                 gtk_widget_set_sensitive (ok_button, TRUE);
624                 
625         /* Free */
626         g_list_free (buttons);
627         g_free (chars);
628 }
629
630 static void
631 launch_sort_headers_dialog (GtkWindow *parent_window,
632                             HildonSortDialog *dialog)
633 {
634         ModestHeaderView *header_view = NULL;
635         GList *cols = NULL;
636         GtkSortType sort_type;
637         gint sort_key;
638         gint default_key = 0;
639         gint result;
640         gboolean outgoing = FALSE;
641         gint current_sort_colid = -1;
642         GtkSortType current_sort_type;
643         gint attachments_sort_id;
644         gint priority_sort_id;
645         GtkTreeSortable *sortable;
646         
647         /* Get header window */
648         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
649                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
650                                                                                       MODEST_WIDGET_TYPE_HEADER_VIEW));
651         }
652         if (!header_view) return;
653
654         /* Add sorting keys */
655         cols = modest_header_view_get_columns (header_view);
656         if (cols == NULL) return;
657         int sort_model_ids[6];
658         int sort_ids[6];
659
660
661         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
662                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
663
664         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"));
665         if (outgoing) {
666                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
667                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
668         } else {
669                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
670                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
671         }
672
673         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"));
674         if (outgoing) {
675                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
676                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
677         } else {
678                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
679                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
680         }
681         default_key = sort_key;
682
683         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"));
684         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
685         if (outgoing)
686                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
687         else
688                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
689
690         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"));
691         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
692         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
693         attachments_sort_id = sort_key;
694
695         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"));
696         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
697         sort_ids[sort_key] = 0;
698
699         sort_key = hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"));
700         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
701         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY;
702         priority_sort_id = sort_key;
703
704         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
705         /* Launch dialogs */
706         if (!gtk_tree_sortable_get_sort_column_id (sortable,
707                                                    &current_sort_colid, &current_sort_type)) {
708                 hildon_sort_dialog_set_sort_key (dialog, default_key);
709                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
710         } else {
711                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
712                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
713                         gpointer flags_sort_type_pointer;
714                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
715                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY)
716                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
717                         else
718                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
719                 } else {
720                         gint current_sort_keyid = 0;
721                         while (current_sort_keyid < 6) {
722                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
723                                         break;
724                                 else 
725                                         current_sort_keyid++;
726                         }
727                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
728                 }
729         }
730
731         result = gtk_dialog_run (GTK_DIALOG (dialog));
732         if (result == GTK_RESPONSE_OK) {
733                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
734                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
735                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
736                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
737                                            GINT_TO_POINTER (sort_ids[sort_key]));
738                         /* This is a hack to make it resort rows always when flag fields are
739                          * selected. If we do not do this, changing sort field from priority to
740                          * attachments does not work */
741                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
742                 } else {
743                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
744                                                                  sort_model_ids[sort_key]);
745                 }
746
747                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
748                 gtk_tree_sortable_sort_column_changed (sortable);
749         }
750
751         modest_widget_memory_save (modest_runtime_get_conf (),
752                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
753         
754         while (gtk_events_pending ())
755                 gtk_main_iteration ();
756
757         /* free */
758         g_list_free(cols);      
759 }
760
761 static gint
762 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
763                                         const gchar *dialog_title,
764                                         const gchar *label_text,
765                                         const gchar *suggested_name,
766                                         gchar **folder_name)
767 {
768         GtkWidget *accept_btn = NULL; 
769         GtkWidget *dialog, *entry, *label, *hbox;
770         GList *buttons = NULL;
771         gint result;
772
773         /* Ask the user for the folder name */
774         dialog = gtk_dialog_new_with_buttons (dialog_title,
775                                               parent_window,
776                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
777                                               GTK_STOCK_OK,
778                                               GTK_RESPONSE_ACCEPT,
779                                               GTK_STOCK_CANCEL,
780                                               GTK_RESPONSE_REJECT,
781                                               NULL);
782
783         /* Add accept button (with unsensitive handler) */
784         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
785         accept_btn = GTK_WIDGET (buttons->next->data);
786         /* Create label and entry */
787         label = gtk_label_new (label_text);
788         /* TODO: check that the suggested name does not exist */
789         /* We set 21 as maximum because we want to show WID-INF036
790            when the user inputs more that 20 */
791         entry = gtk_entry_new_with_max_length (21);
792         if (suggested_name)
793                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
794         else
795                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
796         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
797
798         /* Track entry changes */
799         g_signal_connect (entry,
800                           "insert-text",
801                           G_CALLBACK (entry_insert_text),
802                           dialog);
803         g_signal_connect (entry,
804                           "changed",
805                           G_CALLBACK (entry_changed),
806                           dialog);
807
808         /* Create the hbox */
809         hbox = gtk_hbox_new (FALSE, 12);
810         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
811         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
812
813         /* Add hbox to dialog */
814         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
815                             hbox, FALSE, FALSE, 0);
816         
817         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
818         
819         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
820         result = gtk_dialog_run (GTK_DIALOG(dialog));
821         if (result == GTK_RESPONSE_ACCEPT)
822                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
823
824         gtk_widget_destroy (dialog);
825
826         while (gtk_events_pending ())
827                 gtk_main_iteration ();
828
829         return result;
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         /* Set the interval in alarm_event_t structure: */
1194         alarm_event_t *event = g_new0(alarm_event_t, 1);
1195         event->alarm_time = minutes * 60; /* seconds */
1196         
1197         /* Set recurrence every few minutes: */
1198         event->recurrence = minutes;
1199         event->recurrence_count = -1; /* Means infinite */
1200
1201         /* Specify what should happen when the alarm happens:
1202          * It should call this D-Bus method: */
1203          
1204         /* Note: I am surpised that alarmd can't just use the modest.service file
1205          * for this. murrayc. */
1206         event->dbus_path = g_strdup(PREFIX "/bin/modest");
1207         
1208         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1209         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1210         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1211
1212         /* Otherwise, a dialog will be shown if exect_name or dbus_path is NULL,
1213         even though we have specified no dialog text: */
1214         event->flags = ALARM_EVENT_NO_DIALOG;
1215         
1216         alarm_cookie = alarm_event_add (event);
1217
1218         /* now, free it */
1219         alarm_event_free (event);
1220         
1221         /* Store the alarm ID in GConf, so we can remove it later:
1222          * This is apparently valid between application instances. */
1223         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1224         
1225         if (!alarm_cookie) {
1226             /* Error */
1227             const alarm_error_t alarm_error = alarmd_get_error ();
1228             printf ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1229             
1230             /* Give people some clue: */
1231             /* The alarm API should have a function for this: */
1232             if (alarm_error == ALARMD_ERROR_DBUS) {
1233                 printf ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1234             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1235                 printf ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1236             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1237                 printf ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1238             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1239                 printf ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1240             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1241                 printf ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1242             }
1243             
1244             return FALSE;
1245         }
1246         
1247         return TRUE;
1248 }
1249
1250 GtkWidget * 
1251 modest_platform_get_global_settings_dialog ()
1252 {
1253         return modest_maemo_global_settings_dialog_new ();
1254 }
1255
1256 void 
1257 modest_platform_on_new_msg (void)
1258 {
1259 #ifdef MODEST_HAVE_HILDON_NOTIFY
1260         HildonNotification *not;
1261
1262         /* Create a new notification. FIXME put the right values, need
1263            some more specs */
1264         not = hildon_notification_new ("TODO: (new email) Summary",
1265                                        "TODO: (new email) Description",
1266                                        "qgn_contact_group_chat_invitation",
1267                                        "system.note.dialog");
1268
1269         /* Play sound SR-SND-18. TODO: play the right file */
1270         /* TODO: Where is this declared? hildon_notification_set_sound (not, "/usr/share/sounds/ui-new_email.wav"); */
1271
1272         /* Set the led pattern */
1273         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "led-pattern", 3);
1274
1275         /* Notify. We need to do this in an idle because this function
1276            could be called from a thread */
1277         if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1278                 g_error ("Failed to send notification");
1279                 
1280         g_object_unref (not);
1281 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1282 }
1283
1284
1285 void
1286 modest_platform_show_help (GtkWindow *parent_window, 
1287                            const gchar *help_id)
1288 {
1289         osso_return_t result;
1290
1291         g_return_if_fail (help_id);
1292         g_return_if_fail (osso_context);
1293
1294         /* Show help */
1295 #ifdef MODEST_HAVE_OSSO_HELP
1296         result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1297 #else
1298         result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1299 #endif
1300
1301         if (result != OSSO_OK) {
1302                 gchar *error_msg;
1303                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1304                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1305                                                 NULL,
1306                                                 error_msg);
1307                 g_free (error_msg);
1308         }
1309 }
1310
1311 void 
1312 modest_platform_show_search_messages (GtkWindow *parent_window)
1313 {
1314         osso_return_t result = OSSO_ERROR;
1315         
1316         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1317
1318         if (result != OSSO_OK) {
1319                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1320         }
1321 }
1322
1323 void 
1324 modest_platform_show_addressbook (GtkWindow *parent_window)
1325 {
1326         osso_return_t result = OSSO_ERROR;
1327         
1328         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1329
1330         if (result != OSSO_OK) {
1331                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1332         }
1333 }
1334
1335 GtkWidget *
1336 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1337 {
1338         GtkWidget *widget = modest_folder_view_new (query);
1339
1340         /* Show all accounts by default */
1341         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1342                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1343
1344         /* Restore settings */
1345         modest_widget_memory_restore (modest_runtime_get_conf(), 
1346                                       G_OBJECT (widget),
1347                                       MODEST_CONF_FOLDER_VIEW_KEY);
1348
1349         return widget;
1350 }
1351
1352 void 
1353 modest_platform_information_banner (GtkWidget *parent,
1354                                     const gchar *icon_name,
1355                                     const gchar *text)
1356 {
1357         hildon_banner_show_information (parent, icon_name, text);
1358 }
1359
1360 GtkWidget *
1361 modest_platform_animation_banner (GtkWidget *parent,
1362                                   const gchar *animation_name,
1363                                   const gchar *text)
1364 {
1365         GtkWidget *inf_note = NULL;
1366
1367         g_return_val_if_fail (text != NULL, NULL);
1368
1369         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1370
1371         return inf_note;
1372 }