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