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