f41dd9541112770652b26b7a3dbaea0d4fe4865b
[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 static gint
700 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
701                                         TnyFolderStore *parent,
702                                         const gchar *dialog_title,
703                                         const gchar *label_text,
704                                         const gchar *suggested_name,
705                                         gchar **folder_name)
706 {
707         GtkWidget *accept_btn = NULL; 
708         GtkWidget *dialog, *entry, *label, *hbox;
709         GList *buttons = NULL;
710         gint result;
711
712         /* Ask the user for the folder name */
713         dialog = gtk_dialog_new_with_buttons (dialog_title,
714                                               parent_window,
715                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
716                                               _("mcen_bd_dialog_ok"),
717                                               GTK_RESPONSE_ACCEPT,
718                                               _("mcen_bd_dialog_cancel"),
719                                               GTK_RESPONSE_REJECT,
720                                               NULL);
721
722         /* Add accept button (with unsensitive handler) */
723         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
724         accept_btn = GTK_WIDGET (buttons->next->data);
725         /* Create label and entry */
726         label = gtk_label_new (label_text);
727         /* TODO: check that the suggested name does not exist */
728         /* We set 21 as maximum because we want to show WID-INF036
729            when the user inputs more that 20 */
730         entry = gtk_entry_new_with_max_length (21);
731         if (suggested_name)
732                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
733         else
734                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
735         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
736
737         /* Connect to the response method to avoid closing the dialog
738            when an invalid name is selected*/
739         g_signal_connect (dialog,
740                           "response",
741                           G_CALLBACK (on_response),
742                           parent);
743
744         /* Track entry changes */
745         g_signal_connect (entry,
746                           "insert-text",
747                           G_CALLBACK (entry_insert_text),
748                           dialog);
749         g_signal_connect (entry,
750                           "changed",
751                           G_CALLBACK (entry_changed),
752                           dialog);
753
754         /* Create the hbox */
755         hbox = gtk_hbox_new (FALSE, 12);
756         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
757         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
758
759         /* Add hbox to dialog */
760         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
761                             hbox, FALSE, FALSE, 0);
762         
763         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
764         
765         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
766
767
768
769         result = gtk_dialog_run (GTK_DIALOG(dialog));
770         if (result == GTK_RESPONSE_ACCEPT)
771                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
772
773         gtk_widget_destroy (dialog);
774
775         while (gtk_events_pending ())
776                 gtk_main_iteration ();
777
778         return result;
779 }
780
781 gint
782 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
783                                        TnyFolderStore *parent_folder,
784                                        gchar *suggested_name,
785                                        gchar **folder_name)
786 {
787         gchar *real_suggested_name = NULL;
788         gint result;
789
790         if(suggested_name == NULL)
791         {
792                 const gchar *default_name = _("mcen_ia_default_folder_name");
793                 unsigned int i;
794                 gchar num_str[3];
795
796                 for(i = 0; i < 100; ++ i) {
797                         gboolean exists = FALSE;
798
799                         sprintf(num_str, "%.2u", i);
800
801                         if (i == 0)
802                                 real_suggested_name = g_strdup (default_name);
803                         else
804                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
805                                                                        num_str);
806
807                         exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
808                                                                             real_suggested_name);
809
810                         if (!exists)
811                                 break;
812
813                         g_free (real_suggested_name);
814                 }
815
816                 /* Didn't find a free number */
817                 if (i == 100)
818                         real_suggested_name = g_strdup (default_name);
819         } else {
820                 real_suggested_name = suggested_name;
821         }
822
823         result = modest_platform_run_folder_name_dialog (parent_window, 
824                                                          parent_folder,
825                                                          _("mcen_ti_new_folder"),
826                                                          _("mcen_fi_new_folder_name"),
827                                                          real_suggested_name,
828                                                          folder_name);
829         if (suggested_name == NULL)
830                 g_free(real_suggested_name);
831
832         return result;
833 }
834
835 gint
836 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
837                                           TnyFolderStore *parent_folder,
838                                           const gchar *suggested_name,
839                                           gchar **folder_name)
840 {
841         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
842
843         return modest_platform_run_folder_name_dialog (parent_window, 
844                                                        parent_folder,
845                                                        _HL("ckdg_ti_rename_folder"),
846                                                        _HL("ckdg_fi_rename_name"),
847                                                        suggested_name,
848                                                        folder_name);
849 }
850
851 gint
852 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
853                                          const gchar *message)
854 {
855         GtkWidget *dialog;
856         gint response;
857
858         dialog = hildon_note_new_confirmation (parent_window, message);
859         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
860
861         response = gtk_dialog_run (GTK_DIALOG (dialog));
862
863         gtk_widget_destroy (GTK_WIDGET (dialog));
864
865         while (gtk_events_pending ())
866                 gtk_main_iteration ();
867
868         return response;
869 }
870
871 gint
872 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
873                                    const gchar *message)
874 {
875         GtkWidget *dialog;
876         gint response;
877
878         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
879                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
880                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
881                                                            NULL);
882         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
883
884         response = gtk_dialog_run (GTK_DIALOG (dialog));
885
886         gtk_widget_destroy (GTK_WIDGET (dialog));
887
888         while (gtk_events_pending ())
889                 gtk_main_iteration ();
890
891         return response;
892 }
893
894 void
895 modest_platform_run_information_dialog (GtkWindow *parent_window,
896                                         const gchar *message)
897 {
898         GtkWidget *dialog;
899
900         dialog = hildon_note_new_information (parent_window, message);
901
902         g_signal_connect_swapped (dialog,
903                                   "response", 
904                                   G_CALLBACK (gtk_widget_destroy),
905                                   dialog);
906
907         gtk_widget_show_all (dialog);
908 }
909
910
911
912 typedef struct
913 {
914         GMainLoop* loop;
915 } UtilIdleData;
916
917 static gboolean 
918 on_idle_connect_and_wait(gpointer user_data)
919 {
920         printf ("DEBUG: %s:\n", __FUNCTION__);
921         TnyDevice *device = modest_runtime_get_device();
922         if (!tny_device_is_online (device)) {
923
924                 /* This is a GDK lock because we are an idle callback and
925                  * tny_maemo_conic_device_connect can contain Gtk+ code */
926
927                 gdk_threads_enter(); /* CHECKED */
928                 tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), NULL);
929                 gdk_threads_leave(); /* CHECKED */
930         }
931         
932         /* Allow the function that requested this idle callback to continue: */
933         UtilIdleData *data = (UtilIdleData*)user_data;
934         if (data->loop)
935                 g_main_loop_quit (data->loop);
936         
937         return FALSE; /* Don't call this again. */
938 }
939
940 static gboolean connect_request_in_progress = FALSE;
941
942 /* This callback is used when connect_and_wait() is already queued as an idle callback.
943  * This can happen because the gtk_dialog_run() for the connection dialog 
944  * (at least in the fake scratchbox version) allows idle handlers to keep running.
945  */
946 static gboolean 
947 on_idle_wait_for_previous_connect_to_finish(gpointer user_data)
948 {
949         gboolean result = FALSE;
950         TnyDevice *device = modest_runtime_get_device();
951         if (tny_device_is_online (device))
952                 result = FALSE; /* Stop trying. */
953         else {
954                 /* Keep trying until connect_request_in_progress is FALSE. */
955                 if (connect_request_in_progress)
956                         result = TRUE; /* Keep trying */
957                 else {
958                         printf ("DEBUG: %s: other idle has finished.\n", __FUNCTION__);
959                                                 
960                         result = FALSE; /* Stop trying, now that a result should be available. */
961                 }
962         }
963         
964         if (result == FALSE) {
965                 /* Allow the function that requested this idle callback to continue: */
966                 UtilIdleData *data = (UtilIdleData*)user_data;
967                 if (data->loop)
968                         g_main_loop_quit (data->loop);  
969         }
970                 
971         return result;
972 }
973
974 static void 
975 set_account_to_online (TnyAccount *account)
976 {
977         /* TODO: This is necessary to prevent a cancel of the password dialog 
978          * from making a restart necessary to be asked the password again,
979          * but it causes a hang:
980          */
981         #if 0
982         if (account && TNY_IS_CAMEL_STORE_ACCOUNT (account)) {
983                 /* Make sure that store accounts are online too, 
984                  * because tinymail sets accounts to offline if 
985                  * a password dialog is ever cancelled.
986                  * We don't do this for transport accounts because 
987                  * a) They fundamentally need network access, so they can't really be offline.
988                  * b) That might cause a transport connection to happen too early.
989                  */
990
991                 /* The last argument is user_data, the NULL before that is the callback */
992                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, NULL, NULL);
993         }
994         #endif
995 }
996
997 gboolean 
998 modest_platform_connect_and_wait (GtkWindow *parent_window, TnyAccount *account)
999 {
1000         if (connect_request_in_progress)
1001                 return FALSE;
1002                 
1003         printf ("DEBUG: %s:\n", __FUNCTION__);
1004         TnyDevice *device = modest_runtime_get_device();
1005         
1006         if (tny_device_is_online (device)) {
1007                 printf ("DEBUG: %s: Already online.\n", __FUNCTION__);
1008                 set_account_to_online (account);
1009                 return TRUE;
1010         } else
1011         {
1012                 printf ("DEBUG: %s: tny_device_is_online() returned FALSE\n", __FUNCTION__);
1013         }
1014                 
1015         /* This blocks on the result: */
1016         UtilIdleData *data = g_slice_new0 (UtilIdleData);
1017         
1018         GMainContext *context = NULL; /* g_main_context_new (); */
1019         data->loop = g_main_loop_new (context, FALSE /* not running */);
1020         
1021         /* Cause the function to be run in an idle-handler, which is always 
1022          * in the main thread:
1023          */
1024         if (!connect_request_in_progress) {
1025                 printf ("DEBUG: %s: First request\n", __FUNCTION__);
1026                 connect_request_in_progress = TRUE;
1027                 g_idle_add (&on_idle_connect_and_wait, data);
1028         }
1029         else {
1030                 printf ("DEBUG: %s: nth request\n", __FUNCTION__);
1031                 g_idle_add_full (G_PRIORITY_LOW, &on_idle_wait_for_previous_connect_to_finish, data, NULL);
1032         }
1033
1034         /* This main loop will run until the idle handler has stopped it: */
1035         printf ("DEBUG: %s: before g_main_loop_run()\n", __FUNCTION__);
1036         GDK_THREADS_LEAVE();
1037         g_main_loop_run (data->loop);
1038         GDK_THREADS_ENTER();
1039         printf ("DEBUG: %s: after g_main_loop_run()\n", __FUNCTION__);
1040         connect_request_in_progress = FALSE;
1041         printf ("DEBUG: %s: Finished\n", __FUNCTION__);
1042         g_main_loop_unref (data->loop);
1043         /* g_main_context_unref (context); */
1044
1045         g_slice_free (UtilIdleData, data);
1046
1047         const gboolean result = tny_device_is_online (device);
1048
1049         if (result) {
1050                 set_account_to_online (account);
1051         }
1052
1053         return result;
1054 }
1055
1056 gboolean 
1057 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1058 {
1059         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1060                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1061                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1062                         /* This must be a maildir account, which does not require a connection: */
1063                         return TRUE;
1064                 }
1065         }
1066
1067         return modest_platform_connect_and_wait (parent_window, account);
1068 }
1069
1070 gboolean 
1071 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1072 {
1073         if (!folder_store)
1074                 return TRUE; /* Maybe it is something local. */
1075                 
1076         gboolean result = TRUE;
1077         if (TNY_IS_FOLDER (folder_store)) {
1078                 /* Get the folder's parent account: */
1079                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1080                 if (account != NULL) {
1081                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1082                         g_object_unref (account);
1083                 }
1084         } else if (TNY_IS_ACCOUNT (folder_store)) {
1085                 /* Use the folder store as an account: */
1086                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1087         }
1088
1089         return result;
1090 }
1091
1092 gboolean 
1093 modest_platform_is_network_folderstore (TnyFolderStore *folder_store)
1094 {
1095         TnyAccount *account = NULL;
1096         gboolean result = TRUE;
1097
1098         g_return_val_if_fail(TNY_IS_FOLDER_STORE(folder_store), FALSE);
1099
1100         if (TNY_IS_FOLDER (folder_store)) {
1101                 /* Get the folder's parent account: */
1102                 account = tny_folder_get_account(TNY_FOLDER(folder_store));
1103         } else if (TNY_IS_ACCOUNT (folder_store)) {
1104                 account = TNY_ACCOUNT(folder_store);
1105                 g_object_ref(account);
1106         }
1107
1108         if (account != NULL) {
1109                 if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1110                         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1111                             !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1112                                 /* This must be a maildir account, which does
1113                                  * not require a connection: */
1114                                 result = FALSE;
1115                         }
1116                 }
1117                 g_object_unref (account);
1118         } else {
1119                 result = FALSE;
1120         }
1121
1122         return result;
1123 }
1124
1125 void
1126 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1127                                  ModestSortDialogType type)
1128 {
1129         GtkWidget *dialog = NULL;
1130
1131         /* Build dialog */
1132         dialog = hildon_sort_dialog_new (parent_window);
1133         gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1134         
1135         /* Fill sort keys */
1136         switch (type) {
1137         case MODEST_SORT_HEADERS:
1138                 launch_sort_headers_dialog (parent_window, 
1139                                             HILDON_SORT_DIALOG(dialog));
1140                 break;
1141         }
1142         
1143         /* Free */
1144         gtk_widget_destroy (GTK_WIDGET (dialog));
1145 }
1146
1147
1148 gboolean 
1149 modest_platform_set_update_interval (guint minutes)
1150 {
1151         ModestConf *conf = modest_runtime_get_conf ();
1152         if (!conf)
1153                 return FALSE;
1154                 
1155         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1156
1157         /* Delete any existing alarm,
1158          * because we will replace it: */
1159         if (alarm_cookie) {
1160                 /* TODO: What does the alarm_event_del() return value mean? */
1161                 alarm_event_del(alarm_cookie);
1162                 alarm_cookie = 0;
1163                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1164         }
1165         
1166         /* 0 means no updates: */
1167         if (minutes == 0)
1168                 return TRUE;
1169                 
1170      
1171         /* Register alarm: */
1172         
1173         /* Set the interval in alarm_event_t structure: */
1174         alarm_event_t *event = g_new0(alarm_event_t, 1);
1175         event->alarm_time = minutes * 60; /* seconds */
1176         
1177         /* Set recurrence every few minutes: */
1178         event->recurrence = minutes;
1179         event->recurrence_count = -1; /* Means infinite */
1180
1181         /* Specify what should happen when the alarm happens:
1182          * It should call this D-Bus method: */
1183          
1184         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1185         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1186         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1187         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1188
1189         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1190          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1191          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1192          * This is why we want to use the Alarm API instead of just g_timeout_add().
1193          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1194          */
1195         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1196         
1197         alarm_cookie = alarm_event_add (event);
1198
1199         /* now, free it */
1200         alarm_event_free (event);
1201         
1202         /* Store the alarm ID in GConf, so we can remove it later:
1203          * This is apparently valid between application instances. */
1204         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1205         
1206         if (!alarm_cookie) {
1207             /* Error */
1208             const alarm_error_t alarm_error = alarmd_get_error ();
1209             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1210             
1211             /* Give people some clue: */
1212             /* The alarm API should have a function for this: */
1213             if (alarm_error == ALARMD_ERROR_DBUS) {
1214                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1215             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1216                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1217             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1218                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1219             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1220                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1221             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1222                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1223             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1224                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1225             }
1226             
1227             return FALSE;
1228         }
1229         
1230         return TRUE;
1231 }
1232
1233 GtkWidget * 
1234 modest_platform_get_global_settings_dialog ()
1235 {
1236         return modest_maemo_global_settings_dialog_new ();
1237 }
1238
1239 void 
1240 modest_platform_on_new_header_received (TnyHeader *header)
1241 {
1242 #ifdef MODEST_HAVE_HILDON_NOTIFY
1243         HildonNotification *notification;
1244         gchar *url = NULL;
1245         TnyFolder *folder = NULL;
1246         const gchar *subject;
1247
1248         subject = tny_header_get_subject (header);
1249         if (!subject || strlen(subject) == 0)
1250                 subject = _("mail_va_no_subject");
1251         
1252         notification = hildon_notification_new (tny_header_get_from (header),
1253                                                 subject,
1254                                                 "qgn_list_messagin",
1255                                                 NULL);
1256
1257         folder = tny_header_get_folder (header);
1258         url = g_strdup_printf ("%s/%s", 
1259                                tny_folder_get_url_string (folder), 
1260                                tny_header_get_uid (header));
1261         g_object_unref (folder);
1262
1263         hildon_notification_add_dbus_action(notification,
1264                                             "default",
1265                                             "Cancel",
1266                                             MODEST_DBUS_SERVICE,
1267                                             MODEST_DBUS_OBJECT,
1268                                             MODEST_DBUS_IFACE,
1269                                             MODEST_DBUS_METHOD_OPEN_MESSAGE,
1270                                             G_TYPE_STRING, url,
1271                                             -1);
1272         g_free (url);
1273         
1274         /* Play sound if the user wants */
1275         if (modest_conf_get_bool (modest_runtime_get_conf (), 
1276                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE, 
1277                                   NULL)) {
1278                 hildon_notification_set_sound (HILDON_NOTIFICATION(notification),
1279                                                "/usr/share/sounds/ui-new_email.wav");
1280         }
1281         
1282         /* Set the led pattern */
1283         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION(notification),
1284                                             "dialog-type", 4);
1285         notify_notification_set_hint_string(NOTIFY_NOTIFICATION(notification), 
1286                                             "led-pattern", 
1287                                             "PatternCommunicationEmail");
1288
1289         /* Notify. We need to do this in an idle because this function
1290            could be called from a thread */
1291         if (!notify_notification_show (NOTIFY_NOTIFICATION(notification), NULL))
1292                 g_error ("Failed to send notification");
1293         
1294         g_object_unref (notification);
1295 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1296 }
1297
1298
1299 void
1300 modest_platform_show_help (GtkWindow *parent_window, 
1301                            const gchar *help_id)
1302 {
1303         osso_return_t result;
1304
1305         g_return_if_fail (help_id);
1306         g_return_if_fail (osso_context);
1307
1308         /* Show help */
1309 #ifdef MODEST_HAVE_OSSO_HELP
1310         result = ossohelp_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1311 #else
1312         result = hildon_help_show (osso_context, help_id, OSSO_HELP_SHOW_DIALOG);
1313 #endif
1314
1315         if (result != OSSO_OK) {
1316                 gchar *error_msg;
1317                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1318                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1319                                                 NULL,
1320                                                 error_msg);
1321                 g_free (error_msg);
1322         }
1323 }
1324
1325 void 
1326 modest_platform_show_search_messages (GtkWindow *parent_window)
1327 {
1328         osso_return_t result = OSSO_ERROR;
1329         
1330         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1331
1332         if (result != OSSO_OK) {
1333                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1334         }
1335 }
1336
1337 void 
1338 modest_platform_show_addressbook (GtkWindow *parent_window)
1339 {
1340         osso_return_t result = OSSO_ERROR;
1341         
1342         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1343
1344         if (result != OSSO_OK) {
1345                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1346         }
1347 }
1348
1349 GtkWidget *
1350 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1351 {
1352         GtkWidget *widget = modest_folder_view_new (query);
1353
1354         /* Show one account by default */
1355         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1356                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1357
1358
1359         /* Restore settings */
1360         modest_widget_memory_restore (modest_runtime_get_conf(), 
1361                                       G_OBJECT (widget),
1362                                       MODEST_CONF_FOLDER_VIEW_KEY);
1363
1364         return widget;
1365 }
1366
1367 void 
1368 modest_platform_information_banner (GtkWidget *parent,
1369                                     const gchar *icon_name,
1370                                     const gchar *text)
1371 {
1372         hildon_banner_show_information (parent, icon_name, text);
1373 }
1374
1375 GtkWidget *
1376 modest_platform_animation_banner (GtkWidget *parent,
1377                                   const gchar *animation_name,
1378                                   const gchar *text)
1379 {
1380         GtkWidget *inf_note = NULL;
1381
1382         g_return_val_if_fail (text != NULL, NULL);
1383
1384         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1385
1386         return inf_note;
1387 }
1388
1389 typedef struct
1390 {
1391         GMainLoop* loop;
1392         TnyAccount *account;
1393         gboolean is_online;
1394         gint count_tries;
1395 } CheckAccountIdleData;
1396
1397 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1398
1399 static gboolean 
1400 on_timeout_check_account_is_online(gpointer user_data)
1401 {
1402         printf ("DEBUG: %s:\n", __FUNCTION__);
1403         CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1404         
1405         if (!data) {
1406                 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1407         }
1408         
1409         if (!(data->account)) {
1410                 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1411         }
1412         
1413         if (data && data->account) {
1414                 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));       
1415         }
1416         
1417         gboolean stop_trying = FALSE;
1418         if (data && data->account && 
1419                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1420                  * after which the account is likely to be usable, or never likely to be usable soon: */
1421                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1422         {
1423                 data->is_online = TRUE;
1424                 
1425                 stop_trying = TRUE;
1426         }
1427         else {
1428                 /* Give up if we have tried too many times: */
1429                 if (data->count_tries >= NUMBER_OF_TRIES)
1430                 {
1431                         stop_trying = TRUE;
1432                 }
1433                 else {
1434                         /* Wait for another timeout: */
1435                         ++(data->count_tries);
1436                 }
1437         }
1438         
1439         if (stop_trying) {
1440                 /* Allow the function that requested this idle callback to continue: */
1441                 if (data->loop)
1442                         g_main_loop_quit (data->loop);
1443                         
1444                 if (data->account)
1445                         g_object_unref (data->account);
1446                 
1447                 return FALSE; /* Don't call this again. */
1448         } else {
1449                 return TRUE; /* Call this timeout callback again. */
1450         }
1451 }
1452
1453 /* Return TRUE immediately if the account is already online,
1454  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1455  * soon as the account is online, or FALSE if the account does 
1456  * not become online in the NUMBER_OF_TRIES seconds.
1457  * This is useful when the D-Bus method was run immediately after 
1458  * the application was started (when using D-Bus activation), 
1459  * because the account usually takes a short time to go online.
1460  * The return value is maybe not very useful.
1461  */
1462 gboolean
1463 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1464 {
1465         g_return_val_if_fail (account, FALSE);
1466         
1467         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1468         
1469         if (!tny_device_is_online (modest_runtime_get_device())) {
1470                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1471                 return FALSE;
1472         }
1473         
1474         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1475          * so we avoid wait unnecessarily: */
1476         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1477                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1478                 return TRUE;            
1479         }
1480                 
1481         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1482                 __FUNCTION__, tny_account_get_connection_status (account));
1483         
1484         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1485          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1486          * we want to avoid. */
1487         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1488                 return TRUE;
1489                 
1490         /* This blocks on the result: */
1491         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1492         data->is_online = FALSE;
1493         data->account = account;
1494         g_object_ref (data->account);
1495         data->count_tries = 0;
1496                 
1497         GMainContext *context = NULL; /* g_main_context_new (); */
1498         data->loop = g_main_loop_new (context, FALSE /* not running */);
1499
1500         g_timeout_add (1000, &on_timeout_check_account_is_online, data);
1501
1502         /* This main loop will run until the idle handler has stopped it: */
1503         g_main_loop_run (data->loop);
1504
1505         g_main_loop_unref (data->loop);
1506         /* g_main_context_unref (context); */
1507
1508         g_slice_free (CheckAccountIdleData, data);
1509         
1510         return data->is_online; 
1511 }
1512
1513
1514
1515 static void
1516 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1517 {
1518         /* GTK_RESPONSE_HELP means we need to show the certificate */
1519         if (response_id == GTK_RESPONSE_HELP) {
1520                 GtkWidget *note;
1521                 gchar *msg;
1522                 
1523                 /* Do not close the dialog */
1524                 g_signal_stop_emission_by_name (dialog, "response");
1525
1526                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1527                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1528                 gtk_dialog_run (GTK_DIALOG(note));
1529                 gtk_widget_destroy (note);
1530         }
1531 }
1532
1533
1534 gboolean
1535 modest_platform_run_certificate_conformation_dialog (const gchar* server_name,
1536                                                      const gchar *certificate)
1537 {
1538         GtkWidget *note;
1539         gint response;
1540         GtkWindow *main_win =
1541                 (GtkWindow*)modest_window_mgr_get_main_window (modest_runtime_get_window_mgr());
1542
1543         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1544                                            server_name);
1545         
1546         note = hildon_note_new_confirmation_add_buttons  (
1547                 main_win,
1548                 question,
1549                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1550                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1551                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1552                 NULL, NULL);
1553         
1554         g_signal_connect (G_OBJECT(note), "response", 
1555                           G_CALLBACK(on_cert_dialog_response),
1556                           (gpointer) certificate);
1557         response = gtk_dialog_run(GTK_DIALOG(note));
1558
1559         gtk_widget_destroy(GTK_WIDGET(note));
1560         g_free (question);
1561         
1562         return response;
1563 }
1564         
1565
1566
1567 gboolean
1568 modest_platform_run_alert_dialog (const gchar* prompt, 
1569                                   gboolean is_question)
1570 {       
1571         ModestWindow *main_window = 
1572                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
1573         
1574         gboolean retval = TRUE;
1575         if (is_question) {
1576                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1577                  * when it is a question.
1578                  * Obviously, we need tinymail to use more specific error codes instead,
1579                  * so we know what buttons to show. */
1580                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_window), 
1581                                                                               prompt));
1582                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1583                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1584                 
1585                 gtk_widget_destroy (dialog);
1586                 
1587         } else {
1588                 /* Just show the error text and use the default response: */
1589                 modest_platform_run_information_dialog (GTK_WINDOW (main_window), 
1590                                                         prompt);
1591         }
1592         return retval;
1593 }