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