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