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