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