* Changed the update_account_thread it now refreshes the INBOX and pokes the other...
[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 
1017 modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
1018 {
1019         if (connect_request_in_progress)
1020                 return FALSE;
1021                 
1022         printf ("DEBUG: %s:\n", __FUNCTION__);
1023         TnyDevice *device = modest_runtime_get_device();
1024         
1025         if (tny_device_is_online (device)) {
1026                 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1027                 set_account_to_online (account);
1028                 return TRUE;
1029         } else
1030         {
1031                 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1032         }
1033                 
1034         /* This blocks on the result: */
1035         UtilIdleData *data = g_slice_new0 (UtilIdleData);
1036         
1037         GMainContext *context = NULL; /* g_main_context_new (); */
1038         data->loop = g_main_loop_new (context, FALSE /* not running */);
1039         
1040         /* Cause the function to be run in an idle-handler, which is always 
1041          * in the main thread:
1042          */
1043         if (!connect_request_in_progress) {
1044                 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1045                 connect_request_in_progress = TRUE;
1046                 g_idle_add (&on_idle_connect_and_wait, data);
1047         }
1048         else {
1049                 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1050                 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1051         }
1052
1053         /* This main loop will run until the idle handler has stopped it: */
1054         printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1055         GDK_THREADS_LEAVE();
1056         g_main_loop_run (data->loop);
1057         GDK_THREADS_ENTER();
1058         printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1059         connect_request_in_progress = FALSE;
1060         printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1061         g_main_loop_unref (data->loop);
1062         /* g_main_context_unref (context); */
1063
1064         g_slice_free (UtilIdleData, data);
1065
1066         const gboolean result = tny_device_is_online (device);
1067
1068         if (result) {
1069                 set_account_to_online (account);
1070         }
1071
1072         return result;
1073 }
1074
1075 gboolean 
1076 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1077 {
1078         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1079                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1080                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1081                         /* This must be a maildir account, which does not require a connection: */
1082                         return TRUE;
1083                 }
1084         }
1085
1086         return modest_platform_connect_and_wait (parent_window, account);
1087 }
1088
1089 gboolean 
1090 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1091 {
1092         if (!folder_store)
1093                 return TRUE; /* Maybe it is something local. */
1094                 
1095         gboolean result = TRUE;
1096         if (TNY_IS_FOLDER (folder_store)) {
1097                 /* Get the folder's parent account: */
1098                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1099                 if (account != NULL) {
1100                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1101                         g_object_unref (account);
1102                 }
1103         } else if (TNY_IS_ACCOUNT (folder_store)) {
1104                 /* Use the folder store as an account: */
1105                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1106         }
1107
1108         return result;
1109 }
1110
1111 gboolean 
1112 modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1113 {
1114         TnyAccount *account = NULL;
1115         gboolean result = TRUE;
1116
1117         g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1118
1119         if (TNY_IS_FOLDER (folder_store)) {
1120                 /* Get the folder's parent account: */
1121                 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1122         } else if (TNY_IS_ACCOUNT (folder_store)) {
1123                 account = TNY_ACCOUNT(folder_store);
1124                 g_object_ref(account);
1125         }
1126
1127         if (account != NULL) {
1128                 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1129                         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1130                             !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1131                                 /* This must be a maildir account, which does
1132                                  * not require a connection: */
1133                                 result = FALSE;
1134                         }
1135                 }
1136                 g_object_unref (account);
1137         } else {
1138                 result = FALSE;
1139         }
1140
1141         return result;
1142 }
1143
1144 void
1145 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1146                                  ModestSortDialogType type)
1147 {
1148         GtkWidget *dialog = NULL;
1149
1150         /* Build dialog */
1151         dialog = hildon_sort_dialog_new (parent_window);
1152         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1153         
1154         /* Fill sort keys */
1155         switch (type) {
1156         case MODEST_SORT_HEADERS:
1157                 launch_sort_headers_dialog (parent_window, 
1158                                             HILDON_SORT_DIALOG(dialog));
1159                 break;
1160         }
1161         
1162         /* Free */
1163         gtk_widget_destroy (GTK_WIDGET (dialog));
1164 }
1165
1166
1167 gboolean 
1168 modest_platform_set_update_interval (guint minutes)
1169 {
1170         ModestConf *conf = modest_runtime_get_conf ();
1171         if (!conf)
1172                 return FALSE;
1173                 
1174         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1175
1176         /* Delete any existing alarm,
1177          * because we will replace it: */
1178         if (alarm_cookie) {
1179                 /* TODO: What does the alarm_event_del() return value mean? */
1180                 alarm_event_del(alarm_cookie);
1181                 alarm_cookie = 0;
1182                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1183         }
1184         
1185         /* 0 means no updates: */
1186         if (minutes == 0)
1187                 return TRUE;
1188                 
1189      
1190         /* Register alarm: */
1191         
1192         /* Set the interval in alarm_event_t structure: */
1193         alarm_event_t *event = g_new0(alarm_event_t, 1);
1194         event->alarm_time = minutes * 60; /* seconds */
1195         
1196         /* Set recurrence every few minutes: */
1197         event->recurrence = minutes;
1198         event->recurrence_count = -1; /* Means infinite */
1199
1200         /* Specify what should happen when the alarm happens:
1201          * It should call this D-Bus method: */
1202          
1203         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1204         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1205         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1206         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1207
1208         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1209          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1210          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1211          * This is why we want to use the Alarm API instead of just g_timeout_add().
1212          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1213          */
1214         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1215         
1216         alarm_cookie = alarm_event_add (event);
1217
1218         /* now, free it */
1219         alarm_event_free (event);
1220         
1221         /* Store the alarm ID in GConf, so we can remove it later:
1222          * This is apparently valid between application instances. */
1223         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1224         
1225         if (!alarm_cookie) {
1226             /* Error */
1227             const alarm_error_t alarm_error = alarmd_get_error ();
1228             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1229             
1230             /* Give people some clue: */
1231             /* The alarm API should have a function for this: */
1232             if (alarm_error == ALARMD_ERROR_DBUS) {
1233                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1234             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1235                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1236             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1237                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1238             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1239                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1240             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1241                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1242             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1243                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1244             }
1245             
1246             return FALSE;
1247         }
1248         
1249         return TRUE;
1250 }
1251
1252 GtkWidget * 
1253 modest_platform_get_global_settings_dialog ()
1254 {
1255         return modest_maemo_global_settings_dialog_new ();
1256 }
1257
1258 void 
1259 modest_platform_on_new_header_received (TnyHeader *header)
1260 {
1261 #ifdef MODEST_HAVE_HILDON_NOTIFY
1262         HildonNotification *not;
1263         gchar *url = NULL;
1264         TnyFolder *folder = NULL;
1265
1266         /* Create a new notification. TODO: per-mail data needed */
1267         not = hildon_notification_new (tny_header_get_from (header),
1268                                        tny_header_get_subject (header),
1269                                        "qgn_list_messagin_mail_unread",
1270                                        NULL);
1271
1272         folder = tny_header_get_folder (header);
1273         url = g_strdup_printf ("%s/%s", 
1274                                tny_folder_get_url_string (folder), 
1275                                tny_header_get_uid (header));
1276         g_object_unref (folder);
1277
1278         hildon_notification_add_dbus_action(not,
1279                                             "default",
1280                                             "Cancel",
1281                                             MODEST_DBUS_SERVICE,
1282                                             MODEST_DBUS_OBJECT,
1283                                             MODEST_DBUS_IFACE,
1284                                             MODEST_DBUS_METHOD_OPEN_MESSAGE,
1285                                             G_TYPE_STRING, url,
1286                                             -1);
1287         g_free (url);
1288         
1289         /* Play sound SR-SND-18 */
1290         hildon_notification_set_sound
1291                 (not, "/usr/share/sounds/ui-new_email.wav");
1292         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (not), "dialog-type", 4);
1293
1294         /* Set the led pattern */
1295         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (not), 
1296                                             "led-pattern", 
1297                                             "PatternCommunicationEmail");
1298
1299         /* Notify. We need to do this in an idle because this function
1300            could be called from a thread */
1301         if (!notify_notification_show (NOTIFY_NOTIFICATION (not), NULL))
1302                 g_error ("Failed to send notification");
1303                 
1304         g_object_unref (not);
1305 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1306 }
1307
1308
1309 void
1310 modest_platform_show_help (GtkWindow *parent_window, 
1311                            const gchar *help_id)
1312 {
1313         osso_return_t result;
1314
1315         g_return_if_fail (help_id);
1316         g_return_if_fail (osso_context);
1317
1318         /* Show help */
1319 #ifdef MODEST_HAVE_OSSO_HELP
1320         result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1321 #else
1322         result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1323 #endif
1324
1325         if (result != OSSO_OK) {
1326                 gchar *error_msg;
1327                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1328                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1329                                                 NULL,
1330                                                 error_msg);
1331                 g_free (error_msg);
1332         }
1333 }
1334
1335 void 
1336 modest_platform_show_search_messages (GtkWindow *parent_window)
1337 {
1338         osso_return_t result = OSSO_ERROR;
1339         
1340         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1341
1342         if (result != OSSO_OK) {
1343                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1344         }
1345 }
1346
1347 void 
1348 modest_platform_show_addressbook (GtkWindow *parent_window)
1349 {
1350         osso_return_t result = OSSO_ERROR;
1351         
1352         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1353
1354         if (result != OSSO_OK) {
1355                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1356         }
1357 }
1358
1359 GtkWidget *
1360 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1361 {
1362         GtkWidget *widget = modest_folder_view_new (query);
1363
1364         /* Show one account by default */
1365         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1366                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1367
1368
1369         /* Restore settings */
1370         modest_widget_memory_restore (modest_runtime_get_conf(), 
1371                                       G_OBJECT (widget),
1372                                       MODEST_CONF_FOLDER_VIEW_KEY);
1373
1374         return widget;
1375 }
1376
1377 void 
1378 modest_platform_information_banner (GtkWidget *parent,
1379                                     const gchar *icon_name,
1380                                     const gchar *text)
1381 {
1382         hildon_banner_show_information (parent, icon_name, text);
1383 }
1384
1385 GtkWidget *
1386 modest_platform_animation_banner (GtkWidget *parent,
1387                                   const gchar *animation_name,
1388                                   const gchar *text)
1389 {
1390         GtkWidget *inf_note = NULL;
1391
1392         g_return_val_if_fail (text != NULL, NULL);
1393
1394         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1395
1396         return inf_note;
1397 }
1398
1399 typedef struct
1400 {
1401         GMainLoop* loop;
1402         TnyAccount *account;
1403         gboolean is_online;
1404         gint count_tries;
1405 } CheckAccountIdleData;
1406
1407 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1408
1409 static gboolean 
1410 on_timeout_check_account_is_online(gpointer user_data)
1411 {
1412         printf ("DEBUG: %s:\n", __FUNCTION__);
1413         CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1414         
1415         if (!data) {
1416                 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1417         }
1418         
1419         if (!(data->account)) {
1420                 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1421         }
1422         
1423         if (data && data->account) {
1424                 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));       
1425         }
1426         
1427         gboolean stop_trying = FALSE;
1428         if (data && data->account && 
1429                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1430                  * after which the account is likely to be usable, or never likely to be usable soon: */
1431                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1432         {
1433                 data->is_online = TRUE;
1434                 
1435                 stop_trying = TRUE;
1436         }
1437         else {
1438                 /* Give up if we have tried too many times: */
1439                 if (data->count_tries >= NUMBER_OF_TRIES)
1440                 {
1441                         stop_trying = TRUE;
1442                 }
1443                 else {
1444                         /* Wait for another timeout: */
1445                         ++(data->count_tries);
1446                 }
1447         }
1448         
1449         if (stop_trying) {
1450                 /* Allow the function that requested this idle callback to continue: */
1451                 if (data->loop)
1452                         g_main_loop_quit (data->loop);
1453                         
1454                 if (data->account)
1455                         g_object_unref (data->account);
1456                 
1457                 return FALSE; /* Don't call this again. */
1458         } else {
1459                 return TRUE; /* Call this timeout callback again. */
1460         }
1461 }
1462
1463 /* Return TRUE immediately if the account is already online,
1464  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1465  * soon as the account is online, or FALSE if the account does 
1466  * not become online in the NUMBER_OF_TRIES seconds.
1467  * This is useful when the D-Bus method was run immediately after 
1468  * the application was started (when using D-Bus activation), 
1469  * because the account usually takes a short time to go online.
1470  * The return value is maybe not very useful.
1471  */
1472 gboolean
1473 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1474 {
1475         g_return_val_if_fail (account, FALSE);
1476         
1477         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1478         
1479         if (!tny_device_is_online (modest_runtime_get_device())) {
1480                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1481                 return FALSE;
1482         }
1483         
1484         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1485          * so we avoid wait unnecessarily: */
1486         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1487                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1488                 return TRUE;            
1489         }
1490                 
1491         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1492                 __FUNCTION__, tny_account_get_connection_status (account));
1493         
1494         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1495          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1496          * we want to avoid. */
1497         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1498                 return TRUE;
1499                 
1500         /* This blocks on the result: */
1501         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1502         data->is_online = FALSE;
1503         data->account = account;
1504         g_object_ref (data->account);
1505         data->count_tries = 0;
1506                 
1507         GMainContext *context = NULL; /* g_main_context_new (); */
1508         data->loop = g_main_loop_new (context, FALSE /* not running */);
1509
1510         g_timeout_add (1000, &on_timeout_check_account_is_online, data);
1511
1512         /* This main loop will run until the idle handler has stopped it: */
1513         g_main_loop_run (data->loop);
1514
1515         g_main_loop_unref (data->loop);
1516         /* g_main_context_unref (context); */
1517
1518         g_slice_free (CheckAccountIdleData, data);
1519         
1520         return data->is_online; 
1521 }
1522
1523
1524
1525 static void
1526 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1527 {
1528         // handle ok/cancel in the normal way
1529         if (response_id != GTK_RESPONSE_HELP)
1530                 gtk_dialog_response (dialog, response_id);
1531         else {
1532                 // GTK_RESPONSE_HELP means we need to show the certificate
1533                 GtkWidget *note;
1534                 gchar *msg;
1535                 
1536                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1537                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1538                 gtk_dialog_run (GTK_DIALOG(note));
1539                 gtk_widget_destroy (note);
1540         }
1541 }
1542
1543
1544 gboolean
1545 modest_platform_run_certificate_conformation_dialog (const gchar* server_name,
1546                                                      const gchar *certificate)
1547 {
1548         GtkWidget *note;
1549         gint response;
1550         GtkWindow *main_win =
1551                 (GtkWindow*)modest_window_mgr_get_main_window (modest_runtime_get_window_mgr());
1552
1553         gchar *question = g_strdup_printf (_("mcen_mc_unknown_certificate"),
1554                                            server_name);
1555         
1556         note = hildon_note_new_confirmation_add_buttons  (
1557                 main_win,
1558                 question,
1559                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1560                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1561                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1562                 NULL, NULL);
1563         
1564         g_signal_connect (G_OBJECT(note), "response", G_CALLBACK(on_cert_dialog_response),
1565                           (gpointer)certificate);
1566         response = gtk_dialog_run(GTK_DIALOG(note));
1567
1568         gtk_widget_destroy(GTK_WIDGET(note));
1569         g_free (question);
1570         
1571         return response;
1572 }
1573         
1574
1575
1576 gboolean
1577 modest_platform_run_alert_dialog (const gchar* prompt, gboolean is_question)
1578 {       
1579         ModestWindow *main_window = 
1580                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
1581         
1582         gboolean retval = TRUE;
1583         if (is_question) {
1584                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1585                  * when it is a question.
1586                  * Obviously, we need tinymail to use more specific error codes instead,
1587                  * so we know what buttons to show. */
1588                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_window), 
1589                                                                               prompt));
1590                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1591                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1592                 
1593                 gtk_widget_destroy (dialog);
1594                 
1595         } else {
1596                 /* Just show the error text and use the default response: */
1597                 modest_maemo_show_information_note_and_forget(GTK_WINDOW (main_window), 
1598                                                               prompt);
1599         }
1600         return retval;
1601 }