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