* further optimization of parse_mcc_mapping_line, which still
[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         } else {
874                 /* un-modalize the old one; the one on top should be the
875                  * modal one */
876                 gtk_window_set_modal (GTK_WINDOW(old_modal), FALSE);    
877                 gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
878         }
879
880         /* this will be the new modal dialog */
881         modest_window_mgr_set_modal_dialog (modest_runtime_get_window_mgr(),
882                                             dialog);
883 }
884
885 gint
886 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
887                                          const gchar *message)
888 {
889         GtkWidget *dialog;
890         gint response;
891         
892         dialog = hildon_note_new_confirmation (parent_window, message);
893         check_modal_and_set_maybe (GTK_DIALOG(dialog));
894
895         response = gtk_dialog_run (GTK_DIALOG (dialog));
896
897         on_destroy_dialog (GTK_DIALOG(dialog));
898         
899         while (gtk_events_pending ())
900                 gtk_main_iteration ();
901
902         return response;
903 }
904         
905 gint
906 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
907                                    const gchar *message)
908 {
909         GtkWidget *dialog;
910         gint response;
911         
912         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
913                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
914                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
915                                                            NULL);
916         check_modal_and_set_maybe (GTK_DIALOG(dialog));
917         response = gtk_dialog_run (GTK_DIALOG (dialog));
918         
919         on_destroy_dialog (GTK_DIALOG(dialog));
920
921         while (gtk_events_pending ())
922                 gtk_main_iteration ();
923
924         return response;
925 }
926
927
928
929 void
930 modest_platform_run_information_dialog (GtkWindow *parent_window,
931                                         const gchar *message)
932 {
933         GtkWidget *note;
934         
935         note = hildon_note_new_information (parent_window, message);
936         check_modal_and_set_maybe (GTK_DIALOG(note));
937         
938         g_signal_connect_swapped (note,
939                                   "response", 
940                                   G_CALLBACK (on_destroy_dialog),
941                                   note);
942
943         gtk_widget_show_all (note);
944 }
945
946
947
948 typedef struct
949 {
950         GMainLoop* loop;
951 } UtilIdleData;
952
953 static gboolean 
954 on_idle_connect_and_wait(gpointer user_data)
955 {
956         printf ("DEBUG: %s:\n", __FUNCTION__);
957         TnyDevice *device = modest_runtime_get_device();
958         if (!tny_device_is_online (device)) {
959
960                 /* This is a GDK lock because we are an idle callback and
961                  * tny_maemo_conic_device_connect can contain Gtk+ code */
962
963                 gdk_threads_enter(); /* CHECKED */
964                 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
965                 gdk_threads_leave(); /* CHECKED */
966         }
967         
968         /* Allow the function that requested this idle callback to continue: */
969         UtilIdleData *data = (UtilIdleData*)user_data;
970         if (data->loop)
971                 g_main_loop_quit (data->loop);
972         
973         return FALSE; /* Don't call this again. */
974 }
975
976 static gboolean connect_request_in_progress = FALSE;
977
978 /* This callback is used when connect_and_wait() is already queued as an idle callback.
979  * This can happen because the gtk_dialog_run() for the connection dialog 
980  * (at least in the fake scratchbox version) allows idle handlers to keep running.
981  */
982 static gboolean 
983 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
984 {
985         gboolean result = FALSE;
986         TnyDevice *device = modest_runtime_get_device();
987         if (tny_device_is_online (device))
988                 result = FALSE; /* Stop trying. */
989         else {
990                 /* Keep trying until connect_request_in_progress is FALSE. */
991                 if (connect_request_in_progress)
992                         result = TRUE; /* Keep trying */
993                 else {
994                         printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
995                                                 
996                         result = FALSE; /* Stop trying, now that a result should be available. */
997                 }
998         }
999         
1000         if (result == FALSE) {
1001                 /* Allow the function that requested this idle callback to continue: */
1002                 UtilIdleData *data = (UtilIdleData*)user_data;
1003                 if (data->loop)
1004                         g_main_loop_quit (data->loop);  
1005         }
1006                 
1007         return result;
1008 }
1009
1010 static void 
1011 set_account_to_online (TnyAccount *account)
1012 {
1013         /* TODO: This is necessary to prevent a cancel of the password dialog 
1014          * from making a restart necessary to be asked the password again,
1015          * but it causes a hang:
1016          */
1017         #if 0
1018         if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
1019                 /* Make sure that store accounts are online too, 
1020                  * because tinymail sets accounts to offline if 
1021                  * a password dialog is ever cancelled.
1022                  * We don't do this for transport accounts because 
1023                  * a) They fundamentally need network access, so they can't really be offline.
1024                  * b) That might cause a transport connection to happen too early.
1025                  */
1026
1027                 /* The last argument is user_data, the NULL before that is the callback */
1028                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, NULL, NULL);
1029         }
1030         #endif
1031 }
1032
1033 gboolean 
1034 modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1035 {
1036         if (connect_request_in_progress)
1037                 return FALSE;
1038                 
1039         printf ("DEBUG: %s:\n", __FUNCTION__);
1040         TnyDevice *device = modest_runtime_get_device();
1041         
1042         if (tny_device_is_online (device)) {
1043                 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1044                 set_account_to_online (account);
1045                 return TRUE;
1046         } else
1047         {
1048                 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1049         }
1050                 
1051         /* This blocks on the result: */
1052         UtilIdleData *data = g_slice_new0 (UtilIdleData);
1053         
1054         GMainContext *context = NULL; /* g_main_context_new (); */
1055         data->loop = g_main_loop_new (context, FALSE /* not running */);
1056         
1057         /* Cause the function to be run in an idle-handler, which is always 
1058          * in the main thread:
1059          */
1060         if (!connect_request_in_progress) {
1061                 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1062                 connect_request_in_progress = TRUE;
1063                 g_idle_add (&on_idle_connect_and_wait, data);
1064         }
1065         else {
1066                 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1067                 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1068         }
1069
1070         /* This main loop will run until the idle handler has stopped it: */
1071         printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1072         GDK_THREADS_LEAVE();
1073         g_main_loop_run (data->loop);
1074         GDK_THREADS_ENTER();
1075         printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1076         connect_request_in_progress = FALSE;
1077         printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1078         g_main_loop_unref (data->loop);
1079         /* g_main_context_unref (context); */
1080
1081         g_slice_free (UtilIdleData, data);
1082
1083         const gboolean result = tny_device_is_online (device);
1084
1085         if (result) {
1086                 set_account_to_online (account);
1087         }
1088
1089         return result;
1090 }
1091
1092 gboolean 
1093 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1094 {
1095         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1096                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1097                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1098                         /* This must be a maildir account, which does not require a connection: */
1099                         return TRUE;
1100                 }
1101         }
1102
1103         return modest_platform_connect_and_wait (parent_window, account);
1104 }
1105
1106 gboolean 
1107 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1108 {
1109         if (!folder_store)
1110                 return TRUE; /* Maybe it is something local. */
1111                 
1112         gboolean result = TRUE;
1113         if (TNY_IS_FOLDER (folder_store)) {
1114                 /* Get the folder's parent account: */
1115                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1116                 if (account != NULL) {
1117                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1118                         g_object_unref (account);
1119                 }
1120         } else if (TNY_IS_ACCOUNT (folder_store)) {
1121                 /* Use the folder store as an account: */
1122                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1123         }
1124
1125         return result;
1126 }
1127
1128 gboolean 
1129 modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1130 {
1131         TnyAccount *account = NULL;
1132         gboolean result = TRUE;
1133
1134         g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1135
1136         if (TNY_IS_FOLDER (folder_store)) {
1137                 /* Get the folder's parent account: */
1138                 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1139         } else if (TNY_IS_ACCOUNT (folder_store)) {
1140                 account = TNY_ACCOUNT(folder_store);
1141                 g_object_ref(account);
1142         }
1143
1144         if (account != NULL) {
1145                 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1146                         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1147                             !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1148                                 /* This must be a maildir account, which does
1149                                  * not require a connection: */
1150                                 result = FALSE;
1151                         }
1152                 }
1153                 g_object_unref (account);
1154         } else {
1155                 result = FALSE;
1156         }
1157
1158         return result;
1159 }
1160
1161 void
1162 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1163                                  ModestSortDialogType type)
1164 {
1165         GtkWidget *dialog = NULL;
1166
1167         /* Build dialog */
1168         dialog = hildon_sort_dialog_new (parent_window);
1169         check_modal_and_set_maybe (GTK_DIALOG(dialog));
1170         
1171         /* Fill sort keys */
1172         switch (type) {
1173         case MODEST_SORT_HEADERS:
1174                 launch_sort_headers_dialog (parent_window, 
1175                                             HILDON_SORT_DIALOG(dialog));
1176                 break;
1177         }
1178         
1179         /* Free */
1180         on_destroy_dialog (GTK_DIALOG(dialog));
1181 }
1182
1183
1184 gboolean 
1185 modest_platform_set_update_interval (guint minutes)
1186 {
1187         ModestConf *conf = modest_runtime_get_conf ();
1188         if (!conf)
1189                 return FALSE;
1190                 
1191         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1192
1193         /* Delete any existing alarm,
1194          * because we will replace it: */
1195         if (alarm_cookie) {
1196                 /* TODO: What does the alarm_event_del() return value mean? */
1197                 alarm_event_del(alarm_cookie);
1198                 alarm_cookie = 0;
1199                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1200         }
1201         
1202         /* 0 means no updates: */
1203         if (minutes == 0)
1204                 return TRUE;
1205                 
1206      
1207         /* Register alarm: */
1208         
1209         /* Set the interval in alarm_event_t structure: */
1210         alarm_event_t *event = g_new0(alarm_event_t, 1);
1211         event->alarm_time = minutes * 60; /* seconds */
1212         
1213         /* Set recurrence every few minutes: */
1214         event->recurrence = minutes;
1215         event->recurrence_count = -1; /* Means infinite */
1216
1217         /* Specify what should happen when the alarm happens:
1218          * It should call this D-Bus method: */
1219          
1220         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1221         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1222         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1223         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1224
1225         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1226          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1227          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1228          * This is why we want to use the Alarm API instead of just g_timeout_add().
1229          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1230          */
1231         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1232         
1233         alarm_cookie = alarm_event_add (event);
1234
1235         /* now, free it */
1236         alarm_event_free (event);
1237         
1238         /* Store the alarm ID in GConf, so we can remove it later:
1239          * This is apparently valid between application instances. */
1240         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1241         
1242         if (!alarm_cookie) {
1243             /* Error */
1244             const alarm_error_t alarm_error = alarmd_get_error ();
1245             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1246             
1247             /* Give people some clue: */
1248             /* The alarm API should have a function for this: */
1249             if (alarm_error == ALARMD_ERROR_DBUS) {
1250                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1251             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1252                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1253             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1254                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1255             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1256                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1257             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1258                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1259             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1260                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1261             }
1262             
1263             return FALSE;
1264         }
1265         
1266         return TRUE;
1267 }
1268
1269 GtkWidget * 
1270 modest_platform_get_global_settings_dialog ()
1271 {
1272         return modest_maemo_global_settings_dialog_new ();
1273 }
1274
1275 void 
1276 modest_platform_on_new_header_received (TnyHeader *header)
1277 {
1278 #ifdef MODEST_HAVE_HILDON_NOTIFY
1279         HildonNotification *notification;
1280         gchar *url = NULL;
1281         TnyFolder *folder = NULL;
1282         const gchar *subject;
1283
1284         subject = tny_header_get_subject (header);
1285         if (!subject || strlen(subject) == 0)
1286                 subject = _("mail_va_no_subject");
1287         
1288         notification = hildon_notification_new (tny_header_get_from (header),
1289                                                 subject,
1290                                                 "qgn_list_messagin",
1291                                                 NULL);
1292
1293         folder = tny_header_get_folder (header);
1294         url = g_strdup_printf ("%s/%s", 
1295                                tny_folder_get_url_string (folder), 
1296                                tny_header_get_uid (header));
1297         g_object_unref (folder);
1298
1299         hildon_notification_add_dbus_action(notification,
1300                                             "default",
1301                                             "Cancel",
1302                                             MODEST_DBUS_SERVICE,
1303                                             MODEST_DBUS_OBJECT,
1304                                             MODEST_DBUS_IFACE,
1305                                             MODEST_DBUS_METHOD_OPEN_MESSAGE,
1306                                             G_TYPE_STRING, url,
1307                                             -1);
1308         g_free (url);
1309         
1310         /* Play sound if the user wants */
1311         if (modest_conf_get_bool (modest_runtime_get_conf (), 
1312                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE, 
1313                                   NULL)) {
1314                 hildon_notification_set_sound (HILDON_NOTIFICATION(notification),
1315                                                "/usr/share/sounds/ui-new_email.wav");
1316         }
1317         
1318         /* Set the led pattern */
1319         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION(notification),
1320                                             "dialog-type", 4);
1321         notify_notification_set_hint_string(NOTIFY_NOTIFICATION(notification), 
1322                                             "led-pattern", 
1323                                             "PatternCommunicationEmail");
1324
1325         /* Notify. We need to do this in an idle because this function
1326            could be called from a thread */
1327         if (!notify_notification_show (NOTIFY_NOTIFICATION(notification), NULL))
1328                 g_error ("Failed to send notification");
1329         
1330         g_object_unref (notification);
1331 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1332 }
1333
1334
1335 void
1336 modest_platform_show_help (GtkWindow *parent_window, 
1337                            const gchar *help_id)
1338 {
1339         osso_return_t result;
1340
1341         g_return_if_fail (help_id);
1342         g_return_if_fail (osso_context);
1343
1344         /* Show help */
1345 #ifdef MODEST_HAVE_OSSO_HELP
1346         result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1347 #else
1348         result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1349 #endif
1350
1351         if (result != OSSO_OK) {
1352                 gchar *error_msg;
1353                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1354                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1355                                                 NULL,
1356                                                 error_msg);
1357                 g_free (error_msg);
1358         }
1359 }
1360
1361 void 
1362 modest_platform_show_search_messages (GtkWindow *parent_window)
1363 {
1364         osso_return_t result = OSSO_ERROR;
1365         
1366         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1367
1368         if (result != OSSO_OK) {
1369                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1370         }
1371 }
1372
1373 void 
1374 modest_platform_show_addressbook (GtkWindow *parent_window)
1375 {
1376         osso_return_t result = OSSO_ERROR;
1377         
1378         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1379
1380         if (result != OSSO_OK) {
1381                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1382         }
1383 }
1384
1385 GtkWidget *
1386 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1387 {
1388         GtkWidget *widget = modest_folder_view_new (query);
1389
1390         /* Show one account by default */
1391         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1392                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1393
1394
1395         /* Restore settings */
1396         modest_widget_memory_restore (modest_runtime_get_conf(), 
1397                                       G_OBJECT (widget),
1398                                       MODEST_CONF_FOLDER_VIEW_KEY);
1399
1400         return widget;
1401 }
1402
1403 void 
1404 modest_platform_information_banner (GtkWidget *parent,
1405                                     const gchar *icon_name,
1406                                     const gchar *text)
1407 {
1408         hildon_banner_show_information (parent, icon_name, text);
1409 }
1410
1411 GtkWidget *
1412 modest_platform_animation_banner (GtkWidget *parent,
1413                                   const gchar *animation_name,
1414                                   const gchar *text)
1415 {
1416         GtkWidget *inf_note = NULL;
1417
1418         g_return_val_if_fail (text != NULL, NULL);
1419
1420         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1421
1422         return inf_note;
1423 }
1424
1425 typedef struct
1426 {
1427         GMainLoop* loop;
1428         TnyAccount *account;
1429         gboolean is_online;
1430         gint count_tries;
1431 } CheckAccountIdleData;
1432
1433 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1434
1435 static gboolean 
1436 on_timeout_check_account_is_online(gpointer user_data)
1437 {
1438         printf ("DEBUG: %s:\n", __FUNCTION__);
1439         CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1440         
1441         if (!data) {
1442                 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1443         }
1444         
1445         if (!(data->account)) {
1446                 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1447         }
1448         
1449         if (data && data->account) {
1450                 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));       
1451         }
1452         
1453         gboolean stop_trying = FALSE;
1454         if (data && data->account && 
1455                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1456                  * after which the account is likely to be usable, or never likely to be usable soon: */
1457                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1458         {
1459                 data->is_online = TRUE;
1460                 
1461                 stop_trying = TRUE;
1462         }
1463         else {
1464                 /* Give up if we have tried too many times: */
1465                 if (data->count_tries >= NUMBER_OF_TRIES)
1466                 {
1467                         stop_trying = TRUE;
1468                 }
1469                 else {
1470                         /* Wait for another timeout: */
1471                         ++(data->count_tries);
1472                 }
1473         }
1474         
1475         if (stop_trying) {
1476                 /* Allow the function that requested this idle callback to continue: */
1477                 if (data->loop)
1478                         g_main_loop_quit (data->loop);
1479                         
1480                 if (data->account)
1481                         g_object_unref (data->account);
1482                 
1483                 return FALSE; /* Don't call this again. */
1484         } else {
1485                 return TRUE; /* Call this timeout callback again. */
1486         }
1487 }
1488
1489 /* Return TRUE immediately if the account is already online,
1490  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1491  * soon as the account is online, or FALSE if the account does 
1492  * not become online in the NUMBER_OF_TRIES seconds.
1493  * This is useful when the D-Bus method was run immediately after 
1494  * the application was started (when using D-Bus activation), 
1495  * because the account usually takes a short time to go online.
1496  * The return value is maybe not very useful.
1497  */
1498 gboolean
1499 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1500 {
1501         g_return_val_if_fail (account, FALSE);
1502         
1503         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1504         
1505         if (!tny_device_is_online (modest_runtime_get_device())) {
1506                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1507                 return FALSE;
1508         }
1509         
1510         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1511          * so we avoid wait unnecessarily: */
1512         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1513                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1514                 return TRUE;            
1515         }
1516                 
1517         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1518                 __FUNCTION__, tny_account_get_connection_status (account));
1519         
1520         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1521          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1522          * we want to avoid. */
1523         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1524                 return TRUE;
1525                 
1526         /* This blocks on the result: */
1527         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1528         data->is_online = FALSE;
1529         data->account = account;
1530         g_object_ref (data->account);
1531         data->count_tries = 0;
1532                 
1533         GMainContext *context = NULL; /* g_main_context_new (); */
1534         data->loop = g_main_loop_new (context, FALSE /* not running */);
1535
1536         g_timeout_add (1000, &on_timeout_check_account_is_online, data);
1537
1538         /* This main loop will run until the idle handler has stopped it: */
1539         g_main_loop_run (data->loop);
1540
1541         g_main_loop_unref (data->loop);
1542         /* g_main_context_unref (context); */
1543
1544         g_slice_free (CheckAccountIdleData, data);
1545         
1546         return data->is_online; 
1547 }
1548
1549
1550
1551 static void
1552 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1553 {
1554         /* GTK_RESPONSE_HELP means we need to show the certificate */
1555         if (response_id == GTK_RESPONSE_HELP) {
1556                 GtkWidget *note;
1557                 gchar *msg;
1558                 
1559                 /* Do not close the dialog */
1560                 g_signal_stop_emission_by_name (dialog, "response");
1561
1562                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1563                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1564                 gtk_dialog_run (GTK_DIALOG(note));
1565                 gtk_widget_destroy (note);
1566         }
1567 }
1568
1569
1570 gboolean
1571 modest_platform_run_certificate_conformation_dialog (const gchar* server_name,
1572                                                      const gchar *certificate)
1573 {
1574         GtkWidget *note;
1575         gint response;
1576         GtkWindow *main_win =
1577                 (GtkWindow*)modest_window_mgr_get_main_window (modest_runtime_get_window_mgr());
1578
1579         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1580                                            server_name);
1581         
1582         note = hildon_note_new_confirmation_add_buttons  (
1583                 main_win,
1584                 question,
1585                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1586                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1587                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1588                 NULL, NULL);
1589         
1590         g_signal_connect (G_OBJECT(note), "response", 
1591                           G_CALLBACK(on_cert_dialog_response),
1592                           (gpointer) certificate);
1593         
1594         check_modal_and_set_maybe (GTK_DIALOG(note));
1595         response = gtk_dialog_run(GTK_DIALOG(note));
1596
1597         on_destroy_dialog (GTK_DIALOG(note));
1598         g_free (question);
1599         
1600         return response;
1601 }
1602         
1603
1604
1605 gboolean
1606 modest_platform_run_alert_dialog (const gchar* prompt, 
1607                                   gboolean is_question)
1608 {       
1609         ModestWindow *main_window = 
1610                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
1611         
1612         gboolean retval = TRUE;
1613         if (is_question) {
1614                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1615                  * when it is a question.
1616                  * Obviously, we need tinymail to use more specific error codes instead,
1617                  * so we know what buttons to show. */
1618                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_window), 
1619                                                                               prompt));
1620                 check_modal_and_set_maybe (GTK_DIALOG(dialog));
1621                 
1622                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1623                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1624                 
1625                 on_destroy_dialog (GTK_DIALOG(dialog));         
1626         } else {
1627                 /* Just show the error text and use the default response: */
1628                 modest_platform_run_information_dialog (GTK_WINDOW (main_window), 
1629                                                         prompt);
1630         }
1631         return retval;
1632 }