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