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