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