* Fixed modest_text_utils_get_display_address, now the original string is not modifi...
[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                 display_address = modest_text_utils_get_display_address (tny_header_get_from (header));
1246                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1247                 notification = hildon_notification_new (summary,
1248                                                         tny_header_get_subject (header),
1249                                                         "qgn_list_messagin",
1250                                                         "email.arrive");
1251                 
1252                 /* Create the message URL */
1253                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1254                                        tny_header_get_uid (header));
1255
1256                 hildon_notification_add_dbus_action(notification,
1257                                                     "default",
1258                                                     "Cancel",
1259                                                     MODEST_DBUS_SERVICE,
1260                                                     MODEST_DBUS_OBJECT,
1261                                                     MODEST_DBUS_IFACE,
1262                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1263                                                     G_TYPE_STRING, url,
1264                                                     -1);
1265
1266                 /* Play sound if the user wants. Show the LED
1267                    pattern. Show and play just one */
1268                 if (G_UNLIKELY (first_notification)) {
1269                         first_notification = FALSE;
1270                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1271                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1272                                                   NULL))  {
1273                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1274                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1275                         }
1276
1277                         /* Set the led pattern */
1278                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1279                                                             "dialog-type", 4);
1280                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1281                                                             "led-pattern",
1282                                                             "PatternCommunicationEmail");                       
1283                 }
1284
1285                 /* Notify. We need to do this in an idle because this function
1286                    could be called from a thread */
1287                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1288
1289                 /* Save id in the list */
1290                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1291                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1292                 /* We don't listen for the "closed" signal, because we
1293                    don't care about if the notification was removed or
1294                    not to store the list in gconf */
1295         
1296                 /* Free & carry on */
1297                 g_free (display_date);
1298                 g_free (display_address);
1299                 g_free (summary);
1300                 g_free (url);
1301                 g_object_unref (folder);
1302                 g_object_unref (header);
1303                 tny_iterator_next (iter);
1304         }
1305         g_object_unref (iter);
1306
1307         /* Save the ids */
1308         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1309                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1310
1311         g_slist_free (notifications_list);
1312         
1313 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1314 }
1315
1316 void
1317 modest_platform_remove_new_mail_notifications (void) 
1318 {
1319 #ifdef MODEST_HAVE_HILDON_NOTIFY
1320         GSList *notif_list = NULL;
1321
1322         /* Get previous notifications ids */
1323         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1324                                            MODEST_CONF_NOTIFICATION_IDS, 
1325                                            MODEST_CONF_VALUE_INT, NULL);
1326
1327         while (notif_list) {
1328                 gint notif_id;
1329                 NotifyNotification *notif;
1330
1331                 /* Nasty HACK to remove the notifications, set the id
1332                    of the existing ones and then close them */
1333                 notif_id = GPOINTER_TO_INT(notif_list->data);
1334                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1335                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1336
1337                 /* Close the notification, note that some ids could be
1338                    already invalid, but we don't care because it does
1339                    not fail */
1340                 notify_notification_close(notif, NULL);
1341                 g_object_unref(notif);
1342
1343                 /* Delete the link, it's like going to the next */
1344                 notif_list = g_slist_delete_link (notif_list, notif_list);
1345         }
1346
1347         /* Save the ids */
1348         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1349                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1350
1351         g_slist_free (notif_list);
1352
1353 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1354 }
1355
1356
1357
1358 GtkWidget * 
1359 modest_platform_get_global_settings_dialog ()
1360 {
1361         return modest_maemo_global_settings_dialog_new ();
1362 }
1363
1364 void
1365 modest_platform_show_help (GtkWindow *parent_window, 
1366                            const gchar *help_id)
1367 {
1368         osso_return_t result;
1369
1370         g_return_if_fail (help_id);
1371         g_return_if_fail (osso_context);
1372
1373         result = hildon_help_show (osso_context, help_id, HILDON_HELP_SHOW_DIALOG);
1374
1375         if (result != OSSO_OK) {
1376                 gchar *error_msg;
1377                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1378                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1379                                                 NULL,
1380                                                 error_msg);
1381                 g_free (error_msg);
1382         }
1383 }
1384
1385 void
1386 modest_platform_set_dialog_help (GtkDialog *parent_window, 
1387                                  const gchar *help_id)
1388 {
1389         gboolean result;
1390         g_return_if_fail (help_id);
1391         g_return_if_fail (osso_context);
1392         g_return_if_fail (GTK_IS_DIALOG (parent_window));
1393
1394         result = hildon_help_dialog_help_enable (parent_window, help_id, osso_context);
1395
1396         if (!result)
1397                 g_warning ("Help topic %s not found", help_id);
1398
1399 }
1400
1401 void 
1402 modest_platform_show_search_messages (GtkWindow *parent_window)
1403 {
1404         osso_return_t result = OSSO_ERROR;
1405         
1406         result = osso_rpc_run_with_defaults (osso_context, "osso_global_search", "search_email", NULL, DBUS_TYPE_INVALID);
1407
1408         if (result != OSSO_OK) {
1409                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1410         }
1411 }
1412
1413 void 
1414 modest_platform_show_addressbook (GtkWindow *parent_window)
1415 {
1416         osso_return_t result = OSSO_ERROR;
1417         
1418         result = osso_rpc_run_with_defaults (osso_context, "osso_addressbook", "top_application", NULL, DBUS_TYPE_INVALID);
1419
1420         if (result != OSSO_OK) {
1421                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1422         }
1423 }
1424
1425 GtkWidget *
1426 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1427 {
1428         GtkWidget *widget = modest_folder_view_new (query);
1429
1430         /* Show one account by default */
1431         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1432                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1433
1434
1435         /* Restore settings */
1436         modest_widget_memory_restore (modest_runtime_get_conf(), 
1437                                       G_OBJECT (widget),
1438                                       MODEST_CONF_FOLDER_VIEW_KEY);
1439
1440         return widget;
1441 }
1442
1443 void 
1444 modest_platform_information_banner (GtkWidget *parent,
1445                                     const gchar *icon_name,
1446                                     const gchar *text)
1447 {
1448         hildon_banner_show_information (parent, icon_name, text);
1449 }
1450
1451 GtkWidget *
1452 modest_platform_animation_banner (GtkWidget *parent,
1453                                   const gchar *animation_name,
1454                                   const gchar *text)
1455 {
1456         GtkWidget *inf_note = NULL;
1457
1458         g_return_val_if_fail (text != NULL, NULL);
1459
1460         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1461
1462         return inf_note;
1463 }
1464
1465 typedef struct
1466 {
1467         GMainLoop* loop;
1468         TnyAccount *account;
1469         gboolean is_online;
1470         gint count_tries;
1471 } CheckAccountIdleData;
1472
1473 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1474
1475 static gboolean 
1476 on_timeout_check_account_is_online(gpointer user_data)
1477 {
1478         printf ("DEBUG: %s:\n", __FUNCTION__);
1479         CheckAccountIdleData *data = (CheckAccountIdleData*)user_data;
1480         
1481         if (!data) {
1482                 g_warning ("%s: data is NULL.\n", __FUNCTION__);
1483         }
1484         
1485         if (!(data->account)) {
1486                 g_warning ("%s: data->account is NULL.\n", __FUNCTION__);
1487         }
1488         
1489         if (data && data->account) {
1490                 printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, tny_account_get_connection_status (data->account));       
1491         }
1492         
1493         gboolean stop_trying = FALSE;
1494         if (data && data->account && 
1495                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1496                  * after which the account is likely to be usable, or never likely to be usable soon: */
1497                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1498         {
1499                 data->is_online = TRUE;
1500                 
1501                 stop_trying = TRUE;
1502         }
1503         else {
1504                 /* Give up if we have tried too many times: */
1505                 if (data->count_tries >= NUMBER_OF_TRIES)
1506                 {
1507                         stop_trying = TRUE;
1508                 }
1509                 else {
1510                         /* Wait for another timeout: */
1511                         ++(data->count_tries);
1512                 }
1513         }
1514         
1515         if (stop_trying) {
1516                 /* Allow the function that requested this idle callback to continue: */
1517                 if (data->loop)
1518                         g_main_loop_quit (data->loop);
1519                         
1520                 if (data->account)
1521                         g_object_unref (data->account);
1522                 
1523                 return FALSE; /* Don't call this again. */
1524         } else {
1525                 return TRUE; /* Call this timeout callback again. */
1526         }
1527 }
1528
1529 /* Return TRUE immediately if the account is already online,
1530  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1531  * soon as the account is online, or FALSE if the account does 
1532  * not become online in the NUMBER_OF_TRIES seconds.
1533  * This is useful when the D-Bus method was run immediately after 
1534  * the application was started (when using D-Bus activation), 
1535  * because the account usually takes a short time to go online.
1536  * The return value is maybe not very useful.
1537  */
1538 gboolean
1539 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1540 {
1541         g_return_val_if_fail (account, FALSE);
1542         
1543         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1544         
1545         if (!tny_device_is_online (modest_runtime_get_device())) {
1546                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1547                 return FALSE;
1548         }
1549         
1550         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1551          * so we avoid wait unnecessarily: */
1552         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1553                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1554                 return TRUE;            
1555         }
1556                 
1557         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1558                 __FUNCTION__, tny_account_get_connection_status (account));
1559         
1560         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1561          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1562          * we want to avoid. */
1563         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1564                 return TRUE;
1565                 
1566         /* This blocks on the result: */
1567         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1568         data->is_online = FALSE;
1569         data->account = account;
1570         g_object_ref (data->account);
1571         data->count_tries = 0;
1572                 
1573         GMainContext *context = NULL; /* g_main_context_new (); */
1574         data->loop = g_main_loop_new (context, FALSE /* not running */);
1575
1576         g_timeout_add (1000, &on_timeout_check_account_is_online, data);
1577
1578         /* This main loop will run until the idle handler has stopped it: */
1579         g_main_loop_run (data->loop);
1580
1581         g_main_loop_unref (data->loop);
1582         /* g_main_context_unref (context); */
1583
1584         g_slice_free (CheckAccountIdleData, data);
1585         
1586         return data->is_online; 
1587 }
1588
1589
1590
1591 static void
1592 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1593 {
1594         /* GTK_RESPONSE_HELP means we need to show the certificate */
1595         if (response_id == GTK_RESPONSE_HELP) {
1596                 GtkWidget *note;
1597                 gchar *msg;
1598                 
1599                 /* Do not close the dialog */
1600                 g_signal_stop_emission_by_name (dialog, "response");
1601
1602                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1603                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1604                 gtk_dialog_run (GTK_DIALOG(note));
1605                 gtk_widget_destroy (note);
1606         }
1607 }
1608
1609
1610 gboolean
1611 modest_platform_run_certificate_conformation_dialog (const gchar* server_name,
1612                                                      const gchar *certificate)
1613 {
1614         GtkWidget *note;
1615         gint response;
1616         GtkWindow *main_win =
1617                 (GtkWindow*)modest_window_mgr_get_main_window (modest_runtime_get_window_mgr());
1618
1619         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1620                                            server_name);
1621         
1622         note = hildon_note_new_confirmation_add_buttons  (
1623                 main_win,
1624                 question,
1625                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1626                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1627                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1628                 NULL, NULL);
1629         
1630         g_signal_connect (G_OBJECT(note), "response", 
1631                           G_CALLBACK(on_cert_dialog_response),
1632                           (gpointer) certificate);
1633         
1634         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1635                                      GTK_WINDOW (note));
1636         response = gtk_dialog_run(GTK_DIALOG(note));
1637
1638         on_destroy_dialog (GTK_DIALOG(note));
1639         g_free (question);
1640         
1641         return response;
1642 }
1643         
1644
1645
1646 gboolean
1647 modest_platform_run_alert_dialog (const gchar* prompt, 
1648                                   gboolean is_question)
1649 {       
1650         ModestWindow *main_window = 
1651                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ());
1652         
1653         gboolean retval = TRUE;
1654         if (is_question) {
1655                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1656                  * when it is a question.
1657                  * Obviously, we need tinymail to use more specific error codes instead,
1658                  * so we know what buttons to show. */
1659                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_window), 
1660                                                                               prompt));
1661                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
1662                                              GTK_WINDOW (dialog));
1663                 
1664                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1665                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1666                 
1667                 on_destroy_dialog (GTK_DIALOG(dialog));         
1668         } else {
1669                 /* Just show the error text and use the default response: */
1670                 modest_platform_run_information_dialog (GTK_WINDOW (main_window), 
1671                                                         prompt);
1672         }
1673         return retval;
1674 }