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