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