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