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