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