2bdd04327a4ca076532d9e794e66f31d30050c9b
[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 static void
940 on_connection_status_changed (TnyAccount *account, 
941                               TnyConnectionStatus status,
942                               gpointer user_data)
943 {
944         UtilIdleData *data = (UtilIdleData *) user_data;
945         TnyConnectionStatus conn_status;
946                         
947         conn_status = tny_account_get_connection_status (account);
948         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
949             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
950                 return;
951
952         /* Remove the handler */
953         g_signal_handler_disconnect (account, data->handler);
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 gboolean 
966 modest_platform_connect_and_wait (GtkWindow *parent_window, 
967                                   TnyAccount *account)
968 {
969         UtilIdleData *data = NULL;
970         gboolean device_online;
971         TnyDevice *device;
972         TnyConnectionStatus conn_status;
973         
974         device = modest_runtime_get_device();
975         device_online = tny_device_is_online (device);
976
977         /* If there is no account check only the device status */
978         if (!account) {
979                 if (device_online)
980                         return TRUE;
981                 else
982                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
983         }
984
985         /* Return if the account is already connected */
986         conn_status = tny_account_get_connection_status (account);
987         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
988                 return TRUE;
989
990         /* Connect the device */
991         if (!device_online) {
992                 data = g_slice_new0 (UtilIdleData);
993                 data->mutex = g_mutex_new ();
994                 data->has_callback = FALSE;
995
996                 /* Track account connection status changes */
997                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
998                                                   G_CALLBACK (on_connection_status_changed),
999                                                   data);
1000                 /* Try to connect the device */
1001                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
1002
1003                 /* If the device connection failed then exit */
1004                 if (!device_online && data->handler)
1005                         goto frees;
1006         }
1007
1008         /* Wait until the callback is executed */
1009         g_mutex_lock (data->mutex);
1010         if (!data->has_callback) {
1011                 data->wait_loop = g_main_loop_new (NULL, FALSE);
1012                 gdk_threads_leave ();
1013                 g_mutex_unlock (data->mutex);
1014                 g_main_loop_run (data->wait_loop);
1015                 g_mutex_lock (data->mutex);
1016                 gdk_threads_enter ();
1017         }
1018         g_mutex_unlock (data->mutex);
1019
1020  frees:
1021         if (data) {
1022                 if (g_signal_handler_is_connected (account, data->handler))
1023                         g_signal_handler_disconnect (account, data->handler);
1024                 g_mutex_free (data->mutex);
1025                 g_main_loop_unref (data->wait_loop);
1026                 g_slice_free (UtilIdleData, data);
1027         }
1028
1029         conn_status = tny_account_get_connection_status (account);
1030         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1031 }
1032
1033 gboolean 
1034 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1035 {
1036         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1037                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1038                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1039                         /* This must be a maildir account, which does not require a connection: */
1040                         return TRUE;
1041                 }
1042         }
1043
1044         return modest_platform_connect_and_wait (parent_window, account);
1045 }
1046
1047 gboolean 
1048 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1049 {
1050         if (!folder_store)
1051                 return TRUE; /* Maybe it is something local. */
1052                 
1053         gboolean result = TRUE;
1054         if (TNY_IS_FOLDER (folder_store)) {
1055                 /* Get the folder's parent account: */
1056                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1057                 if (account != NULL) {
1058                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1059                         g_object_unref (account);
1060                 }
1061         } else if (TNY_IS_ACCOUNT (folder_store)) {
1062                 /* Use the folder store as an account: */
1063                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1064         }
1065
1066         return result;
1067 }
1068
1069 gboolean 
1070 modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1071 {
1072         TnyAccount *account = NULL;
1073         gboolean result = TRUE;
1074
1075         g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1076
1077         if (TNY_IS_FOLDER (folder_store)) {
1078                 /* Get the folder's parent account: */
1079                 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1080         } else if (TNY_IS_ACCOUNT (folder_store)) {
1081                 account = TNY_ACCOUNT(folder_store);
1082                 g_object_ref(account);
1083         }
1084
1085         if (account != NULL) {
1086                 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1087                         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1088                             !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1089                                 /* This must be a maildir account, which does
1090                                  * not require a connection: */
1091                                 result = FALSE;
1092                         }
1093                 }
1094                 g_object_unref (account);
1095         } else {
1096                 result = FALSE;
1097         }
1098
1099         return result;
1100 }
1101
1102 void
1103 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1104                                  ModestSortDialogType type)
1105 {
1106         GtkWidget *dialog = NULL;
1107
1108         /* Build dialog */
1109         dialog = hildon_sort_dialog_new (parent_window);
1110         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1111                                      GTK_WINDOW (dialog));
1112         
1113         /* Fill sort keys */
1114         switch (type) {
1115         case MODEST_SORT_HEADERS:
1116                 launch_sort_headers_dialog (parent_window, 
1117                                             HILDON_SORT_DIALOG(dialog));
1118                 break;
1119         }
1120         
1121         /* Free */
1122         on_destroy_dialog (GTK_DIALOG(dialog));
1123 }
1124
1125
1126 gboolean 
1127 modest_platform_set_update_interval (guint minutes)
1128 {
1129         ModestConf *conf = modest_runtime_get_conf ();
1130         if (!conf)
1131                 return FALSE;
1132                 
1133         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1134
1135         /* Delete any existing alarm,
1136          * because we will replace it: */
1137         if (alarm_cookie) {
1138                 /* TODO: What does the alarm_event_del() return value mean? */
1139                 alarm_event_del(alarm_cookie);
1140                 alarm_cookie = 0;
1141                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1142         }
1143         
1144         /* 0 means no updates: */
1145         if (minutes == 0)
1146                 return TRUE;
1147                 
1148      
1149         /* Register alarm: */
1150         
1151         /* Set the interval in alarm_event_t structure: */
1152         alarm_event_t *event = g_new0(alarm_event_t, 1);
1153         event->alarm_time = minutes * 60; /* seconds */
1154         
1155         /* Set recurrence every few minutes: */
1156         event->recurrence = minutes;
1157         event->recurrence_count = -1; /* Means infinite */
1158
1159         /* Specify what should happen when the alarm happens:
1160          * It should call this D-Bus method: */
1161          
1162         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1163         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1164         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1165         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1166
1167         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1168          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1169          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1170          * This is why we want to use the Alarm API instead of just g_timeout_add().
1171          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1172          */
1173         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1174         
1175         alarm_cookie = alarm_event_add (event);
1176
1177         /* now, free it */
1178         alarm_event_free (event);
1179         
1180         /* Store the alarm ID in GConf, so we can remove it later:
1181          * This is apparently valid between application instances. */
1182         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1183         
1184         if (!alarm_cookie) {
1185             /* Error */
1186             const alarm_error_t alarm_error = alarmd_get_error ();
1187             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1188             
1189             /* Give people some clue: */
1190             /* The alarm API should have a function for this: */
1191             if (alarm_error == ALARMD_ERROR_DBUS) {
1192                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1193             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1194                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1195             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1196                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1197             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1198                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1199             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1200                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1201             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1202                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1203             }
1204             
1205             return FALSE;
1206         }
1207         
1208         return TRUE;
1209 }
1210
1211 void 
1212 modest_platform_on_new_headers_received (TnyList *header_list) 
1213 {
1214 #ifdef MODEST_HAVE_HILDON_NOTIFY
1215         HildonNotification *notification;
1216         TnyIterator *iter;
1217         GSList *notifications_list = NULL;
1218
1219         /* Get previous notifications ids */
1220         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1221                                                    MODEST_CONF_NOTIFICATION_IDS, 
1222                                                    MODEST_CONF_VALUE_INT, NULL);
1223
1224         iter = tny_list_create_iterator (header_list);
1225         while (!tny_iterator_is_done (iter)) {
1226                 gchar *url = NULL, *display_address = NULL, *display_date = NULL, *summary = NULL;
1227                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1228                 TnyFolder *folder = tny_header_get_folder (header);
1229                 gboolean first_notification = TRUE;
1230                 gint notif_id;
1231         
1232                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1233
1234                 display_address = g_strdup(tny_header_get_from (header));
1235                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1236                 
1237                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1238                 notification = hildon_notification_new (summary,
1239                                                         tny_header_get_subject (header),
1240                                                         "qgn_list_messagin",
1241                                                         "email.arrive");
1242                 
1243                 /* Create the message URL */
1244                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1245                                        tny_header_get_uid (header));
1246
1247                 hildon_notification_add_dbus_action(notification,
1248                                                     "default",
1249                                                     "Cancel",
1250                                                     MODEST_DBUS_SERVICE,
1251                                                     MODEST_DBUS_OBJECT,
1252                                                     MODEST_DBUS_IFACE,
1253                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1254                                                     G_TYPE_STRING, url,
1255                                                     -1);
1256
1257                 /* Play sound if the user wants. Show the LED
1258                    pattern. Show and play just one */
1259                 if (G_UNLIKELY (first_notification)) {
1260                         first_notification = FALSE;
1261                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1262                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1263                                                   NULL))  {
1264                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1265                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1266                         }
1267
1268                         /* Set the led pattern */
1269                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1270                                                             "dialog-type", 4);
1271                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1272                                                             "led-pattern",
1273                                                             "PatternCommunicationEmail");                       
1274                 }
1275
1276                 /* Notify. We need to do this in an idle because this function
1277                    could be called from a thread */
1278                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1279
1280                 /* Save id in the list */
1281                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1282                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1283                 /* We don't listen for the "closed" signal, because we
1284                    don't care about if the notification was removed or
1285                    not to store the list in gconf */
1286         
1287                 /* Free & carry on */
1288                 g_free (display_date);
1289                 g_free (display_address);
1290                 g_free (summary);
1291                 g_free (url);
1292                 g_object_unref (folder);
1293                 g_object_unref (header);
1294                 tny_iterator_next (iter);
1295         }
1296         g_object_unref (iter);
1297
1298         /* Save the ids */
1299         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1300                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1301
1302         g_slist_free (notifications_list);
1303         
1304 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1305 }
1306
1307 void
1308 modest_platform_remove_new_mail_notifications (void) 
1309 {
1310 #ifdef MODEST_HAVE_HILDON_NOTIFY
1311         GSList *notif_list = NULL;
1312
1313         /* Get previous notifications ids */
1314         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1315                                            MODEST_CONF_NOTIFICATION_IDS, 
1316                                            MODEST_CONF_VALUE_INT, NULL);
1317
1318         while (notif_list) {
1319                 gint notif_id;
1320                 NotifyNotification *notif;
1321
1322                 /* Nasty HACK to remove the notifications, set the id
1323                    of the existing ones and then close them */
1324                 notif_id = GPOINTER_TO_INT(notif_list->data);
1325                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1326                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1327
1328                 /* Close the notification, note that some ids could be
1329                    already invalid, but we don't care because it does
1330                    not fail */
1331                 notify_notification_close(notif, NULL);
1332                 g_object_unref(notif);
1333
1334                 /* Delete the link, it's like going to the next */
1335                 notif_list = g_slist_delete_link (notif_list, notif_list);
1336         }
1337
1338         /* Save the ids */
1339         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1340                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1341
1342         g_slist_free (notif_list);
1343
1344 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1345 }
1346
1347
1348
1349 GtkWidget * 
1350 modest_platform_get_global_settings_dialog ()
1351 {
1352         return modest_maemo_global_settings_dialog_new ();
1353 }
1354
1355 void
1356 modest_platform_show_help (GtkWindow *parent_window, 
1357                            const gchar *help_id)
1358 {
1359         osso_return_t result;
1360
1361         g_return_if_fail (help_id);
1362         g_return_if_fail (osso_context);
1363
1364         result = hildon_help_show (osso_context, help_id, HILDON_HELP_SHOW_DIALOG);
1365
1366         if (result != OSSO_OK) {
1367                 gchar *error_msg;
1368                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1369                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1370                                                 NULL,
1371                                                 error_msg);
1372                 g_free (error_msg);
1373         }
1374 }
1375
1376 void
1377 modest_platform_set_dialog_help (GtkDialog *parent_window, 
1378                                  const gchar *help_id)
1379 {
1380         gboolean result;
1381         g_return_if_fail (help_id);
1382         g_return_if_fail (osso_context);
1383         g_return_if_fail (GTK_IS_DIALOG (parent_window));
1384
1385         result = hildon_help_dialog_help_enable (parent_window, help_id, osso_context);
1386
1387         if (!result)
1388                 g_warning ("Help topic %s not found", help_id);
1389
1390 }
1391
1392 void 
1393 modest_platform_show_search_messages (GtkWindow *parent_window)
1394 {
1395         osso_return_t result = OSSO_ERROR;
1396         
1397         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1398
1399         if (result != OSSO_OK) {
1400                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1401         }
1402 }
1403
1404 void 
1405 modest_platform_show_addressbook (GtkWindow *parent_window)
1406 {
1407         osso_return_t result = OSSO_ERROR;
1408         
1409         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1410
1411         if (result != OSSO_OK) {
1412                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1413         }
1414 }
1415
1416 GtkWidget *
1417 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1418 {
1419         GtkWidget *widget = modest_folder_view_new (query);
1420
1421         /* Show one account by default */
1422         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1423                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1424
1425
1426         /* Restore settings */
1427         modest_widget_memory_restore (modest_runtime_get_conf(), 
1428                                       G_OBJECT (widget),
1429                                       MODEST_CONF_FOLDER_VIEW_KEY);
1430
1431         return widget;
1432 }
1433
1434 void 
1435 modest_platform_information_banner (GtkWidget *parent,
1436                                     const gchar *icon_name,
1437                                     const gchar *text)
1438 {
1439         hildon_banner_show_information (parent, icon_name, text);
1440 }
1441
1442 GtkWidget *
1443 modest_platform_animation_banner (GtkWidget *parent,
1444                                   const gchar *animation_name,
1445                                   const gchar *text)
1446 {
1447         GtkWidget *inf_note = NULL;
1448
1449         g_return_val_if_fail (text != NULL, NULL);
1450
1451         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1452
1453         return inf_note;
1454 }
1455
1456 typedef struct
1457 {
1458         GMainLoop* loop;
1459         TnyAccount *account;
1460         gboolean is_online;
1461         gint count_tries;
1462 } CheckAccountIdleData;
1463
1464 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1465
1466 static gboolean 
1467 on_timeout_check_account_is_online(gpointer user_data)
1468 {
1469         printf ("DEBUG: %s:\n", __FUNCTION__);
1470         CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1471         
1472         if (!data) {
1473                 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1474         }
1475         
1476         if (!(data->account)) {
1477                 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1478         }
1479         
1480         if (data && data->account) {
1481                 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));       
1482         }
1483         
1484         gboolean stop_trying = FALSE;
1485         if (data && data->account && 
1486                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1487                  * after which the account is likely to be usable, or never likely to be usable soon: */
1488                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1489         {
1490                 data->is_online = TRUE;
1491                 
1492                 stop_trying = TRUE;
1493         }
1494         else {
1495                 /* Give up if we have tried too many times: */
1496                 if (data->count_tries >= NUMBER_OF_TRIES)
1497                 {
1498                         stop_trying = TRUE;
1499                 }
1500                 else {
1501                         /* Wait for another timeout: */
1502                         ++(data->count_tries);
1503                 }
1504         }
1505         
1506         if (stop_trying) {
1507                 /* Allow the function that requested this idle callback to continue: */
1508                 if (data->loop)
1509                         g_main_loop_quit (data->loop);
1510                         
1511                 if (data->account)
1512                         g_object_unref (data->account);
1513                 
1514                 return FALSE; /* Don't call this again. */
1515         } else {
1516                 return TRUE; /* Call this timeout callback again. */
1517         }
1518 }
1519
1520 /* Return TRUE immediately if the account is already online,
1521  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1522  * soon as the account is online, or FALSE if the account does 
1523  * not become online in the NUMBER_OF_TRIES seconds.
1524  * This is useful when the D-Bus method was run immediately after 
1525  * the application was started (when using D-Bus activation), 
1526  * because the account usually takes a short time to go online.
1527  * The return value is maybe not very useful.
1528  */
1529 gboolean
1530 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1531 {
1532         g_return_val_if_fail (account, FALSE);
1533         
1534         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1535         
1536         if (!tny_device_is_online (modest_runtime_get_device())) {
1537                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1538                 return FALSE;
1539         }
1540         
1541         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1542          * so we avoid wait unnecessarily: */
1543         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1544                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1545                 return TRUE;            
1546         }
1547                 
1548         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1549                 __FUNCTION__, tny_account_get_connection_status (account));
1550         
1551         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1552          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1553          * we want to avoid. */
1554         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1555                 return TRUE;
1556                 
1557         /* This blocks on the result: */
1558         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1559         data->is_online = FALSE;
1560         data->account = account;
1561         g_object_ref (data->account);
1562         data->count_tries = 0;
1563                 
1564         GMainContext *context = NULL; /* g_main_context_new (); */
1565         data->loop = g_main_loop_new (context, FALSE /* not running */);
1566
1567         g_timeout_add (1000, on_timeout_check_account_is_online, data);
1568
1569         /* This main loop will run until the idle handler has stopped it: */
1570         g_main_loop_run (data->loop);
1571
1572         g_main_loop_unref (data->loop);
1573         /* g_main_context_unref (context); */
1574
1575         g_slice_free (CheckAccountIdleData, data);
1576         
1577         return data->is_online; 
1578 }
1579
1580
1581
1582 static void
1583 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1584 {
1585         /* GTK_RESPONSE_HELP means we need to show the certificate */
1586         if (response_id == GTK_RESPONSE_HELP) {
1587                 GtkWidget *note;
1588                 gchar *msg;
1589                 
1590                 /* Do not close the dialog */
1591                 g_signal_stop_emission_by_name (dialog, "response");
1592
1593                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1594                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1595                 gtk_dialog_run (GTK_DIALOG(note));
1596                 gtk_widget_destroy (note);
1597         }
1598 }
1599
1600
1601 gboolean
1602 modest_platform_run_certificate_conformation_dialog (const gchar* server_name,
1603                                                      const gchar *certificate)
1604 {
1605         GtkWidget *note;
1606         gint response;
1607         GtkWindow *main_win =
1608                 (GtkWindow*)modest_window_mgr_get_main_window (modest_runtime_get_window_mgr());
1609
1610         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1611                                            server_name);
1612         
1613         note = hildon_note_new_confirmation_add_buttons  (
1614                 main_win,
1615                 question,
1616                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1617                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1618                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1619                 NULL, NULL);
1620         
1621         g_signal_connect (G_OBJECT(note), "response", 
1622                           G_CALLBACK(on_cert_dialog_response),
1623                           (gpointer) certificate);
1624         
1625         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1626                                      GTK_WINDOW (note));
1627         response = gtk_dialog_run(GTK_DIALOG(note));
1628
1629         on_destroy_dialog (GTK_DIALOG(note));
1630         g_free (question);
1631         
1632         return response;
1633 }
1634         
1635
1636
1637 gboolean
1638 modest_platform_run_alert_dialog (const gchar* prompt, 
1639                                   gboolean is_question)
1640 {       
1641         ModestWindow *main_window = 
1642                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
1643         
1644         gboolean retval = TRUE;
1645         if (is_question) {
1646                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1647                  * when it is a question.
1648                  * Obviously, we need tinymail to use more specific error codes instead,
1649                  * so we know what buttons to show. */
1650                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_window), 
1651                                                                               prompt));
1652                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1653                                              GTK_WINDOW (dialog));
1654                 
1655                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1656                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1657                 
1658                 on_destroy_dialog (GTK_DIALOG(dialog));         
1659         } else {
1660                 /* Just show the error text and use the default response: */
1661                 modest_platform_run_information_dialog (GTK_WINDOW (main_window), 
1662                                                         prompt);
1663         }
1664         return retval;
1665 }