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