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