bb472f1d86a8d6e5387b7135ded8edfbe6fb4a65
[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-defs.h>
33 #include <modest-platform.h>
34 #include <modest-runtime.h>
35 #include <modest-main-window.h>
36 #include <modest-header-view.h>
37 #include "maemo/modest-maemo-global-settings-dialog.h"
38 #include "modest-widget-memory.h"
39 #include <modest-hildon-includes.h>
40 #include <modest-utils.h>
41 #include <modest-maemo-utils.h>
42 #include <dbus_api/modest-dbus-callbacks.h>
43 #include <maemo/modest-osso-autosave-callbacks.h>
44 #include <libosso.h>
45 #include <tny-maemo-conic-device.h>
46 #include <tny-simple-list.h>
47 #include <tny-folder.h>
48 #include <tny-error.h>
49 #include <tny-merge-folder.h>
50 #include <tny-camel-imap-store-account.h>
51 #include <tny-camel-pop-store-account.h>
52 #include <gtk/gtkicontheme.h>
53 #include <gtk/gtkmenuitem.h>
54 #include <gtk/gtkmain.h>
55 #include <modest-text-utils.h>
56 #include "modest-tny-folder.h"
57 #include "modest-tny-account.h"
58 #include <string.h>
59 #include <libgnomevfs/gnome-vfs-mime-utils.h>
60 #include <modest-account-settings-dialog.h>
61 #include <easysetup/modest-easysetup-wizard-dialog.h>
62 #include "modest-hildon-sort-dialog.h"
63 #include <hildon/hildon-sound.h>
64 #include <osso-mem.h>
65 #include "widgets/modest-details-dialog.h"
66
67 #ifdef MODEST_HAVE_MCE
68 #include <mce/dbus-names.h>
69 #endif /*MODEST_HAVE_MCE*/
70
71 #ifdef MODEST_HAVE_ABOOK
72 #include <libosso-abook/osso-abook.h>
73 #endif /*MODEST_HAVE_ABOOK*/
74
75 #ifdef MODEST_HAVE_LIBALARM
76 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
77 #endif /*MODEST_HAVE_LIBALARM*/
78
79
80 #define HILDON_OSSO_URI_ACTION "uri-action"
81 #define URI_ACTION_COPY "copy:"
82 #define MODEST_NEW_MAIL_SOUND_FILE "/usr/share/sounds/ui-new_email.wav"
83 #define MODEST_NEW_MAIL_LIGHTING_PATTERN "PatternCommunicationEmail"
84
85 static void     
86 on_modest_conf_update_interval_changed (ModestConf* self, 
87                                         const gchar *key, 
88                                         ModestConfEvent event,
89                                         ModestConfNotificationId id, 
90                                         gpointer user_data)
91 {
92         g_return_if_fail (key);
93         
94         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
95                 const guint update_interval_minutes = 
96                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
97                 modest_platform_set_update_interval (update_interval_minutes);
98         }
99 }
100
101
102
103 static gboolean
104 check_required_files (void)
105 {
106         FILE *mcc_file = modest_utils_open_mcc_mapping_file (FALSE,NULL);
107         if (!mcc_file) {
108                 g_printerr ("modest: check for mcc file failed\n");
109                 return FALSE;
110         } else
111                 fclose (mcc_file);
112
113
114         mcc_file = modest_utils_open_mcc_mapping_file (TRUE, NULL);
115         if (!mcc_file) {
116                 g_printerr ("modest: check for mcc file (for LC_MESSAGES) failed\n");
117                 return FALSE;
118         } else
119                 fclose (mcc_file);
120
121         if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
122             access(MODEST_FALLBACK_PROVIDER_DATA_FILE, R_OK) != 0) {
123                 g_printerr ("modest: cannot find providers data\n");
124                 return FALSE;
125         }
126
127         return TRUE;
128 }
129
130
131 /* the gpointer here is the osso_context. */
132 gboolean
133 modest_platform_init (int argc, char *argv[])
134 {
135         osso_context_t *osso_context;
136         
137         osso_hw_state_t hw_state = { 0 };
138         DBusConnection *con;    
139         GSList *acc_names;
140         
141         if (!check_required_files ()) {
142                 g_printerr ("modest: missing required files\n");
143                 return FALSE;
144         }
145         
146         osso_context =  osso_initialize(PACKAGE,PACKAGE_VERSION,
147                                         FALSE, NULL);   
148         if (!osso_context) {
149                 g_printerr ("modest: failed to acquire osso context\n");
150                 return FALSE;
151         }
152         modest_maemo_utils_set_osso_context (osso_context);
153
154         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
155                 g_printerr ("modest: could not get dbus connection\n");
156                 return FALSE;
157         }
158
159         /* Add a D-Bus handler to be used when the main osso-rpc 
160          * D-Bus handler has not handled something.
161          * We use this for D-Bus methods that need to use more complex types 
162          * than osso-rpc supports. 
163          */
164         if (!dbus_connection_add_filter (con,
165                                          modest_dbus_req_filter,
166                                          NULL,
167                                          NULL)) {
168
169                 g_printerr ("modest: Could not add D-Bus filter\n");
170                 return FALSE;
171         }
172
173         /* Register our simple D-Bus callbacks, via the osso API: */
174         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
175                                MODEST_DBUS_SERVICE, 
176                                MODEST_DBUS_OBJECT, 
177                                MODEST_DBUS_IFACE,
178                                modest_dbus_req_handler, NULL /* user_data */);
179         if (result != OSSO_OK) {
180                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
181                 return FALSE;
182         }
183
184         /* Register hardware event dbus callback: */
185         hw_state.shutdown_ind = TRUE;
186         osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
187
188         /* Register osso auto-save callbacks: */
189         result = osso_application_set_autosave_cb (osso_context, 
190                 modest_on_osso_application_autosave, NULL /* user_data */);
191         if (result != OSSO_OK) {
192                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
193                 return FALSE;
194         }
195         
196
197         /* Make sure that the update interval is changed whenever its gconf key 
198          * is changed */
199         /* CAUTION: we're not using here the
200            modest_conf_listen_to_namespace because we know that there
201            are other parts of Modest listening for this namespace, so
202            we'll receive the notifications anyway. We basically do not
203            use it because there is no easy way to do the
204            modest_conf_forget_namespace */
205         ModestConf *conf = modest_runtime_get_conf ();
206         g_signal_connect (G_OBJECT(conf),
207                           "key_changed",
208                           G_CALLBACK (on_modest_conf_update_interval_changed), 
209                           NULL);
210
211         /* only force the setting of the default interval, if there are actually
212          * any accounts */
213         acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
214         if (acc_names) {
215                 /* Get the initial update interval from gconf: */
216                 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
217                                                        MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
218                 modest_account_mgr_free_account_names (acc_names);
219         }
220
221         
222 #ifdef MODEST_HAVE_ABOOK
223         /* initialize the addressbook */
224         if (!osso_abook_init (&argc, &argv, osso_context)) {
225                 g_printerr ("modest: failed to initialized addressbook\n");
226                 return FALSE;
227         }
228 #endif /*MODEST_HAVE_ABOOK*/
229
230         return TRUE;
231 }
232
233 gboolean
234 modest_platform_uninit (void)
235 {
236         osso_context_t *osso_context =
237                 modest_maemo_utils_get_osso_context ();
238         if (osso_context)
239                 osso_deinitialize (osso_context);
240
241         return TRUE;
242 }
243
244
245
246
247 TnyDevice*
248 modest_platform_get_new_device (void)
249 {
250         return TNY_DEVICE (tny_maemo_conic_device_new ());
251 }
252
253 gchar*
254 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
255                                     gchar **effective_mime_type)
256 {
257         GString *mime_str = NULL;
258         gchar *icon_name  = NULL;
259         gchar **icons, **cursor;
260         
261         if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) 
262                 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
263         else {
264                 mime_str = g_string_new (mime_type);
265                 g_string_ascii_down (mime_str);
266         }
267         
268         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
269         
270         for (cursor = icons; cursor; ++cursor) {
271                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
272                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
273                         icon_name = g_strdup ("qgn_list_messagin");
274                         break;
275                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
276                         icon_name = g_strdup (*cursor);
277                         break;
278                 }
279         }
280         g_strfreev (icons);
281
282         if (effective_mime_type)
283                 *effective_mime_type = g_string_free (mime_str, FALSE);
284         else
285                 g_string_free (mime_str, TRUE);
286         
287         return icon_name;
288 }
289
290
291 static gboolean
292 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
293 {
294         GError *err = NULL;
295         gboolean result;
296
297         g_return_val_if_fail (uri, FALSE);
298         
299         result = hildon_uri_open (uri, action, &err);
300         if (!result) {
301                 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
302                             uri, action,  err && err->message ? err->message : "unknown error");
303                 if (err)
304                         g_error_free (err);
305         }
306         return result;
307 }
308
309
310
311 gboolean 
312 modest_platform_activate_uri (const gchar *uri)
313 {
314         HildonURIAction *action;
315         gboolean result = FALSE;
316         GSList *actions, *iter = NULL;
317         
318         g_return_val_if_fail (uri, FALSE);
319         if (!uri)
320                 return FALSE;
321
322         /* don't try to activate file: uri's -- they might confuse the user,
323          * and/or might have security implications */
324         if (!g_str_has_prefix (uri, "file:")) {
325                 
326                 actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
327                 
328                 for (iter = actions; iter; iter = g_slist_next (iter)) {
329                         action = (HildonURIAction*) iter->data;
330                         if (action && strcmp (hildon_uri_action_get_service (action),
331                                               "com.nokia.modest") == 0) {
332                                 result = checked_hildon_uri_open (uri, action);
333                                 break;
334                         }
335                 }
336                 
337                 /* if we could not open it with email, try something else */
338                 if (!result)
339                         result = checked_hildon_uri_open (uri, NULL);   
340         } 
341         
342         if (!result) {
343                 ModestWindow *parent =
344                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
345                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
346                                                 _("mcen_ib_unsupported_link"));
347                 g_debug ("%s: cannot open uri '%s'", __FUNCTION__,uri);
348         } 
349         
350         return result;
351 }
352
353 gboolean 
354 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
355 {
356         gint result = 0;
357         DBusConnection *con;
358         gchar *uri_path = NULL;
359         
360         uri_path = gnome_vfs_get_uri_from_local_path (path);    
361         con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
362         
363         if (mime_type)
364                 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
365         if (result != 1)
366                 result = hildon_mime_open_file (con, uri_path);
367         if (result != 1)
368                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"), FALSE);
369         
370         return result != 1;
371 }
372
373 typedef struct  {
374         GSList *actions;
375         gchar  *uri;
376 } ModestPlatformPopupInfo;
377
378 static gboolean
379 delete_uri_popup (GtkWidget *menu,
380                   GdkEvent *event,
381                   gpointer userdata)
382 {
383         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
384
385         g_free (popup_info->uri);
386         hildon_uri_free_actions (popup_info->actions);
387
388         return FALSE;
389 }
390
391 static void
392 activate_uri_popup_item (GtkMenuItem *menu_item,
393                          gpointer userdata)
394 {
395         GSList *node;
396         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
397         const gchar* action_name;
398
399         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
400         if (!action_name) {
401                 g_printerr ("modest: no action name defined\n");
402                 return;
403         }
404
405         /* special handling for the copy menu item -- copy the uri to the clipboard */
406         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
407         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
408                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
409                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
410
411                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
412                         action_name += strlen ("mailto:");
413                 
414                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
415                 modest_platform_information_banner (NULL, NULL, _CS("ecoc_ib_edwin_copied"));
416                 return; /* we're done */
417         }
418         
419         /* now, the real uri-actions... */
420         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
421                 HildonURIAction *action = (HildonURIAction *) node->data;
422                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
423                         if (!checked_hildon_uri_open (popup_info->uri, action)) {
424                                 ModestWindow *parent =
425                                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
426                                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
427                                                                 _("mcen_ib_unsupported_link"));
428                         }
429                         break;
430                 }
431         }
432 }
433
434 gboolean 
435 modest_platform_show_uri_popup (const gchar *uri)
436 {
437         GSList *actions_list;
438
439         if (uri == NULL)
440                 return FALSE;
441         
442         actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
443         if (actions_list) {
444
445                 GtkWidget *menu = gtk_menu_new ();
446                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
447
448                 /* don't add actions for file: uri's -- they might confuse the user,
449                  * and/or might have security implications
450                  * we still allow to copy the url though
451                  */
452                 if (!g_str_has_prefix (uri, "file:")) {                 
453                 
454                         GSList *node;                   
455                         popup_info->actions = actions_list;
456                         popup_info->uri = g_strdup (uri);
457                         
458                         for (node = actions_list; node != NULL; node = g_slist_next (node)) {
459                                 GtkWidget *menu_item;
460                                 const gchar *action_name;
461                                 const gchar *translation_domain;
462                                 HildonURIAction *action = (HildonURIAction *) node->data;
463                                 action_name = hildon_uri_action_get_name (action);
464                                 translation_domain = hildon_uri_action_get_translation_domain (action);
465                                 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
466                                 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
467                                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
468                                                   popup_info);
469                                 
470                                 if (hildon_uri_is_default_action (action, NULL)) {
471                                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
472                                 } else {
473                                         gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
474                                 }
475                                 gtk_widget_show (menu_item);
476                         }
477                 }
478
479                 /* always add the copy item */
480                 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri",
481                                                                               "uri_link_copy_link_location"));
482                 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
483                                         g_strconcat (URI_ACTION_COPY, uri, NULL),
484                                         g_free);
485                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
486                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
487                 gtk_widget_show (menu_item);
488
489                 
490                 /* and what to do when the link is deleted */
491                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
492                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
493                                                   
494         } else {
495                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
496         }
497         
498         return TRUE;
499 }
500
501
502 GdkPixbuf*
503 modest_platform_get_icon (const gchar *name, guint icon_size)
504 {
505         GError *err = NULL;
506         GdkPixbuf* pixbuf = NULL;
507         GtkIconTheme *current_theme = NULL;
508
509         g_return_val_if_fail (name, NULL);
510
511         /* strlen == 0 is not really an error; it just
512          * means the icon is not available
513          */
514         if (!name || strlen(name) == 0)
515                 return NULL;
516         
517         current_theme = gtk_icon_theme_get_default ();
518         pixbuf = gtk_icon_theme_load_icon (current_theme, name, icon_size,
519                                            GTK_ICON_LOOKUP_NO_SVG,
520                                            &err);
521         if (!pixbuf) {
522                 g_printerr ("modest: error loading theme icon '%s': %s\n",
523                             name, err->message);
524                 g_error_free (err);
525         } 
526         return pixbuf;
527 }
528
529 const gchar*
530 modest_platform_get_app_name (void)
531 {
532         return _("mcen_ap_name");
533 }
534
535 static void 
536 entry_insert_text (GtkEditable *editable,
537                    const gchar *text,
538                    gint         length,
539                    gint        *position,
540                    gpointer     data)
541 {
542         gchar *chars;
543         gint chars_length;
544
545         chars = gtk_editable_get_chars (editable, 0, -1);
546         chars_length = g_utf8_strlen (chars, -1);
547         g_free (chars);
548
549         /* Show WID-INF036 */
550         if (chars_length >= 20) {
551                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
552                                                  _CS("ckdg_ib_maximum_characters_reached"));
553         } else {
554                 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
555                         /* Show an error */
556                         gchar *tmp, *msg;
557                         
558                         tmp = g_strndup (folder_name_forbidden_chars, 
559                                          FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
560                         msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
561                         hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), 
562                                                          NULL, msg);
563                         g_free (msg);
564                         g_free (tmp);
565                 } else {        
566                         /* Write the text in the entry if it's valid */
567                         g_signal_handlers_block_by_func (editable,
568                                                          (gpointer) entry_insert_text, data);
569                         gtk_editable_insert_text (editable, text, length, position);
570                         g_signal_handlers_unblock_by_func (editable,
571                                                            (gpointer) entry_insert_text, data);
572                 }
573         }
574         /* Do not allow further processing */
575         g_signal_stop_emission_by_name (editable, "insert_text");
576 }
577
578 static void
579 entry_changed (GtkEditable *editable,
580                gpointer     user_data)
581 {
582         gchar *chars;
583         GtkWidget *ok_button;
584         GList *buttons;
585
586         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
587         ok_button = GTK_WIDGET (buttons->next->data);
588         
589         chars = gtk_editable_get_chars (editable, 0, -1);
590         g_return_if_fail (chars != NULL);
591
592         
593         if (g_utf8_strlen (chars,-1) >= 21)
594                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
595                                                  _CS("ckdg_ib_maximum_characters_reached"));
596         else
597                 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
598                 
599         /* Free */
600         g_list_free (buttons);
601         g_free (chars);
602 }
603
604
605
606 static void
607 on_response (GtkDialog *dialog,
608              gint response,
609              gpointer user_data)
610 {
611         GList *child_vbox, *child_hbox;
612         GtkWidget *hbox, *entry;
613         TnyFolderStore *parent;
614         const gchar *new_name;
615         gboolean exists;
616
617         if (response != GTK_RESPONSE_ACCEPT)
618                 return;
619         
620         /* Get entry */
621         child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
622         hbox = child_vbox->data;
623         child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
624         entry = child_hbox->next->data;
625         
626         parent = TNY_FOLDER_STORE (user_data);
627         new_name = gtk_entry_get_text (GTK_ENTRY (entry));
628         exists = FALSE;
629         
630         /* Look for another folder with the same name */
631         if (modest_tny_folder_has_subfolder_with_name (parent, 
632                                                        new_name,
633                                                        TRUE)) {
634                 exists = TRUE;
635         }
636         
637         if (!exists) {
638                 if (TNY_IS_ACCOUNT (parent) &&
639                     modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
640                     modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
641                                                                          new_name)) {
642                         exists = TRUE;
643                 }
644         }
645         
646         if (exists) {
647                 
648                 /* Show an error */
649                 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), 
650                                                 NULL, _CS("ckdg_ib_folder_already_exists"));
651                 /* Select the text */
652                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
653                 gtk_widget_grab_focus (entry);
654                 /* Do not close the dialog */
655                 g_signal_stop_emission_by_name (dialog, "response");
656         }
657 }
658
659
660
661 static gint
662 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
663                                         TnyFolderStore *parent,
664                                         const gchar *dialog_title,
665                                         const gchar *label_text,
666                                         const gchar *suggested_name,
667                                         gchar **folder_name)
668 {
669         GtkWidget *accept_btn = NULL; 
670         GtkWidget *dialog, *entry, *label, *hbox;
671         GList *buttons = NULL;
672         gint result;
673
674         /* Ask the user for the folder name */
675         dialog = gtk_dialog_new_with_buttons (dialog_title,
676                                               parent_window,
677                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
678                                               _("mcen_bd_dialog_ok"),
679                                               GTK_RESPONSE_ACCEPT,
680                                               _("mcen_bd_dialog_cancel"),
681                                               GTK_RESPONSE_REJECT,
682                                               NULL);
683
684         /* Add accept button (with unsensitive handler) */
685         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
686         accept_btn = GTK_WIDGET (buttons->next->data);
687         /* Create label and entry */
688         label = gtk_label_new (label_text);
689         /* TODO: check that the suggested name does not exist */
690         /* We set 21 as maximum because we want to show WID-INF036
691            when the user inputs more that 20 */
692         entry = gtk_entry_new_with_max_length (21);
693         if (suggested_name)
694                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
695         else
696                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
697         gtk_entry_set_width_chars (GTK_ENTRY (entry),
698                                    MAX (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1),
699                                         g_utf8_strlen (_("mcen_ia_default_folder_name"), -1)));
700         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
701
702         /* Connect to the response method to avoid closing the dialog
703            when an invalid name is selected*/
704         g_signal_connect (dialog,
705                           "response",
706                           G_CALLBACK (on_response),
707                           parent);
708
709         /* Track entry changes */
710         g_signal_connect (entry,
711                           "insert-text",
712                           G_CALLBACK (entry_insert_text),
713                           dialog);
714         g_signal_connect (entry,
715                           "changed",
716                           G_CALLBACK (entry_changed),
717                           dialog);
718
719
720         /* Some locales like pt_BR need this to get the full window
721            title shown */
722         gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1);
723
724         /* Create the hbox */
725         hbox = gtk_hbox_new (FALSE, 12);
726         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
727         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
728
729         /* Add hbox to dialog */
730         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
731                             hbox, FALSE, FALSE, 0);
732         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
733                                      GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
734         gtk_widget_show_all (GTK_WIDGET(dialog));
735                 
736         result = gtk_dialog_run (GTK_DIALOG(dialog));
737         if (result == GTK_RESPONSE_ACCEPT)
738                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
739
740         gtk_widget_destroy (dialog);
741
742         while (gtk_events_pending ())
743                 gtk_main_iteration ();
744
745         return result;
746 }
747
748 gint
749 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
750                                        TnyFolderStore *suggested_parent,
751                                        gchar *suggested_name,
752                                        gchar **folder_name,
753                                        TnyFolderStore **parent_folder)
754 {
755         gchar *real_suggested_name = NULL, *tmp = NULL;
756         gint result;
757
758         if(suggested_name == NULL)
759         {
760                 const gchar *default_name = _("mcen_ia_default_folder_name");
761                 unsigned int i;
762                 gchar num_str[3];
763
764                 for(i = 0; i < 100; ++ i) {
765                         gboolean exists = FALSE;
766
767                         sprintf(num_str, "%.2u", i);
768
769                         if (i == 0)
770                                 real_suggested_name = g_strdup (default_name);
771                         else
772                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
773                                                                        num_str);
774                         exists = modest_tny_folder_has_subfolder_with_name (suggested_parent,
775                                                                             real_suggested_name,
776                                                                             TRUE);
777
778                         if (!exists)
779                                 break;
780
781                         g_free (real_suggested_name);
782                 }
783
784                 /* Didn't find a free number */
785                 if (i == 100)
786                         real_suggested_name = g_strdup (default_name);
787         } else {
788                 real_suggested_name = suggested_name;
789         }
790
791         tmp = g_strconcat (_("mcen_fi_new_folder_name"), ":", NULL);
792         result = modest_platform_run_folder_name_dialog (parent_window, 
793                                                          suggested_parent,
794                                                          _("mcen_ti_new_folder"),
795                                                          tmp,
796                                                          real_suggested_name,
797                                                          folder_name);
798         g_free (tmp);
799
800         if (suggested_name == NULL)
801                 g_free(real_suggested_name);
802
803         if (parent_folder != NULL) {
804                 *parent_folder = suggested_parent?g_object_ref (suggested_parent): NULL;
805         }
806
807         return result;
808 }
809
810 gint
811 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
812                                           TnyFolderStore *parent_folder,
813                                           const gchar *suggested_name,
814                                           gchar **folder_name)
815 {
816         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
817
818         return modest_platform_run_folder_name_dialog (parent_window, 
819                                                        parent_folder,
820                                                        _HL("ckdg_ti_rename_folder"),
821                                                        _HL("ckdg_fi_rename_name"),
822                                                        suggested_name,
823                                                        folder_name);
824 }
825
826
827
828 static void
829 on_destroy_dialog (GtkWidget *dialog)
830 {
831         /* This could happen when the dialogs get programatically
832            hidden or destroyed (for example when closing the
833            application while a dialog is being shown) */
834         if (!GTK_IS_WIDGET (dialog))
835                 return;
836
837         gtk_widget_destroy (dialog);
838
839         if (gtk_events_pending ())
840                 gtk_main_iteration ();
841 }
842
843 gint
844 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
845                                          const gchar *message)
846 {
847         GtkWidget *dialog;
848         gint response;
849         
850         dialog = hildon_note_new_confirmation (parent_window, message);
851         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
852                                      GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
853
854         response = gtk_dialog_run (GTK_DIALOG (dialog));
855
856         on_destroy_dialog (dialog);
857
858         return response;
859 }
860
861 gint
862 modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window,
863                                                       const gchar *message,
864                                                       const gchar *button_accept,
865                                                       const gchar *button_cancel)
866 {
867         GtkWidget *dialog;
868         gint response;
869         
870         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
871                                                            button_accept, GTK_RESPONSE_ACCEPT,
872                                                            button_cancel, GTK_RESPONSE_CANCEL,
873                                                            NULL);
874         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
875                                      GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
876
877         response = gtk_dialog_run (GTK_DIALOG (dialog));
878
879         on_destroy_dialog (dialog);
880
881         return response;
882 }
883         
884 void
885 modest_platform_run_information_dialog (GtkWindow *parent_window,
886                                         const gchar *message,
887                                         gboolean block)
888 {
889         GtkWidget *note;
890         
891         note = hildon_note_new_information (parent_window, message);
892         if (block)
893                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
894                                              GTK_WINDOW (note), GTK_WINDOW (parent_window));
895         
896         if (block) {
897                 gtk_dialog_run (GTK_DIALOG (note));
898         
899                 on_destroy_dialog (note);
900         } else {
901                 g_signal_connect_swapped (note,
902                                           "response", 
903                                           G_CALLBACK (on_destroy_dialog),
904                                           note);
905
906                 gtk_widget_show_all (note);
907         }
908 }
909
910 typedef struct _ConnectAndWaitData {
911         GMutex *mutex;
912         GMainLoop *wait_loop;
913         gboolean has_callback;
914         gulong handler;
915 } ConnectAndWaitData;
916
917
918 static void
919 quit_wait_loop (TnyAccount *account,
920                 ConnectAndWaitData *data) 
921 {
922         /* Set the has_callback to TRUE (means that the callback was
923            executed and wake up every code waiting for cond to be
924            TRUE */
925         g_mutex_lock (data->mutex);
926         data->has_callback = TRUE;
927         if (data->wait_loop)
928                 g_main_loop_quit (data->wait_loop);
929         g_mutex_unlock (data->mutex);
930 }
931
932 static void
933 on_connection_status_changed (TnyAccount *account, 
934                               TnyConnectionStatus status,
935                               gpointer user_data)
936 {
937         TnyConnectionStatus conn_status;
938         ConnectAndWaitData *data;
939                         
940         /* Ignore if reconnecting or disconnected */
941         conn_status = tny_account_get_connection_status (account);
942         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
943             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
944                 return;
945
946         /* Remove the handler */
947         data = (ConnectAndWaitData *) user_data;
948         g_signal_handler_disconnect (account, data->handler);
949
950         /* Quit from wait loop */
951         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
952 }
953
954 static void
955 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
956                                     gboolean canceled, 
957                                     GError *err, 
958                                     gpointer user_data)
959 {
960         /* Quit from wait loop */
961         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
962 }
963
964 gboolean 
965 modest_platform_connect_and_wait (GtkWindow *parent_window, 
966                                   TnyAccount *account)
967 {
968         ConnectAndWaitData *data = NULL;
969         gboolean device_online;
970         TnyDevice *device;
971         TnyConnectionStatus conn_status;
972         gboolean user_requested;
973         
974         device = modest_runtime_get_device();
975         device_online = tny_device_is_online (device);
976
977         /* Whether the connection is user requested or automatically
978            requested, for example via D-Bus */
979         user_requested = (parent_window) ? TRUE : FALSE;
980
981         /* If there is no account check only the device status */
982         if (!account) {
983                 if (device_online)
984                         return TRUE;
985                 else
986                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
987                                                                NULL, user_requested);
988         }
989
990         /* Return if the account is already connected */
991         conn_status = tny_account_get_connection_status (account);
992         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
993                 return TRUE;
994
995         /* Create the helper */
996         data = g_slice_new0 (ConnectAndWaitData);
997         data->mutex = g_mutex_new ();
998         data->has_callback = FALSE;
999
1000         /* Connect the device */
1001         if (!device_online) {
1002                 /* Track account connection status changes */
1003                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1004                                                   G_CALLBACK (on_connection_status_changed),
1005                                                   data);
1006                 /* Try to connect the device */
1007                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1008                                                                 NULL, user_requested);
1009
1010                 /* If the device connection failed then exit */
1011                 if (!device_online && data->handler)
1012                         goto frees;
1013         } else {
1014                 /* Force a reconnection of the account */
1015                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1016                                               on_tny_camel_account_set_online_cb, data);
1017         }
1018
1019         /* Wait until the callback is executed */
1020         g_mutex_lock (data->mutex);
1021         if (!data->has_callback) {
1022                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1023                 gdk_threads_leave ();
1024                 g_mutex_unlock (data->mutex);
1025                 g_main_loop_run (data->wait_loop);
1026                 g_mutex_lock (data->mutex);
1027                 gdk_threads_enter ();
1028         }
1029         g_mutex_unlock (data->mutex);
1030
1031  frees:
1032         if (data) {
1033                 if (g_signal_handler_is_connected (account, data->handler))
1034                         g_signal_handler_disconnect (account, data->handler);
1035                 g_mutex_free (data->mutex);
1036                 g_main_loop_unref (data->wait_loop);
1037                 g_slice_free (ConnectAndWaitData, data);
1038         }
1039
1040         conn_status = tny_account_get_connection_status (account);
1041         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1042 }
1043
1044 gboolean 
1045 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1046 {
1047         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1048                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1049                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1050                         /* This must be a maildir account, which does not require a connection: */
1051                         return TRUE;
1052                 }
1053         }
1054
1055         return modest_platform_connect_and_wait (parent_window, account);
1056 }
1057
1058 gboolean 
1059 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1060 {
1061         if (!folder_store)
1062                 return TRUE; /* Maybe it is something local. */
1063                 
1064         gboolean result = TRUE;
1065         if (TNY_IS_FOLDER (folder_store)) {
1066                 /* Get the folder's parent account: */
1067                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1068                 if (account != NULL) {
1069                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1070                         g_object_unref (account);
1071                 }
1072         } else if (TNY_IS_ACCOUNT (folder_store)) {
1073                 /* Use the folder store as an account: */
1074                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1075         }
1076
1077         return result;
1078 }
1079
1080 GtkWidget *
1081 modest_platform_create_sort_dialog       (GtkWindow *parent_window)
1082 {
1083         GtkWidget *dialog;
1084
1085         dialog = modest_hildon_sort_dialog_new (parent_window);
1086
1087         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1088                                         "applications_email_sort",
1089                                         modest_maemo_utils_get_osso_context());
1090
1091         return dialog;
1092 }
1093
1094
1095 gboolean 
1096 modest_platform_set_update_interval (guint minutes)
1097 {
1098 #ifdef MODEST_HAVE_LIBALARM
1099         
1100         ModestConf *conf = modest_runtime_get_conf ();
1101         if (!conf)
1102                 return FALSE;
1103                 
1104         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1105
1106         /* Delete any existing alarm,
1107          * because we will replace it: */
1108         if (alarm_cookie) {
1109                 if (alarm_event_del(alarm_cookie) != 1)
1110                         g_debug ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1111                 alarm_cookie = 0;
1112                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1113         }
1114         
1115         /* 0 means no updates: */
1116         if (minutes == 0)
1117                 return TRUE;
1118         
1119      
1120         /* Register alarm: */
1121         
1122         /* Set the interval in alarm_event_t structure: */
1123         alarm_event_t *event = g_new0(alarm_event_t, 1);
1124         event->alarm_time = minutes * 60; /* seconds */
1125         
1126         /* Set recurrence every few minutes: */
1127         event->recurrence = minutes;
1128         event->recurrence_count = -1; /* Means infinite */
1129
1130         /* Specify what should happen when the alarm happens:
1131          * It should call this D-Bus method: */
1132          
1133         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1134         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1135         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1136         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1137
1138         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1139          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1140          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1141          * This is why we want to use the Alarm API instead of just g_timeout_add().
1142          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1143          * ALARM_EVENT_CONNECTED will prevent the alarm from being called in case that the device is offline
1144          */
1145         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION | ALARM_EVENT_CONNECTED;
1146         
1147         alarm_cookie = alarm_event_add (event);
1148
1149         /* now, free it */
1150         alarm_event_free (event);
1151         
1152         /* Store the alarm ID in GConf, so we can remove it later:
1153          * This is apparently valid between application instances. */
1154         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1155         
1156         if (!alarm_cookie) {
1157             /* Error */
1158             const alarm_error_t alarm_error = alarmd_get_error ();
1159             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1160             
1161             /* Give people some clue: */
1162             /* The alarm API should have a function for this: */
1163             if (alarm_error == ALARMD_ERROR_DBUS) {
1164                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1165             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1166                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1167             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1168                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1169             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1170                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1171             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1172                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1173             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1174                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1175             }
1176             
1177             return FALSE;
1178         }
1179 #endif /* MODEST_HAVE_LIBALARM */       
1180         return TRUE;
1181 }
1182
1183 void
1184 modest_platform_push_email_notification(void)
1185 {
1186         gboolean play_sound;
1187         ModestWindow *main_window;
1188         gboolean screen_on = TRUE, app_in_foreground;
1189
1190         /* Check whether or not we should play a sound */
1191         play_sound = modest_conf_get_bool (modest_runtime_get_conf (),
1192                                            MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1193                                            NULL);
1194
1195         /* Get the screen status */
1196         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1197         if (main_window)
1198                 screen_on = modest_main_window_screen_is_on (MODEST_MAIN_WINDOW (main_window));
1199
1200         /* Get the window status */
1201         app_in_foreground = hildon_program_get_is_topmost (hildon_program_get_instance ());
1202
1203         /* If the screen is on and the app is in the
1204            foreground we don't show anything */
1205         if (!(screen_on && app_in_foreground)) {
1206                 /* Play a sound */
1207                 if (play_sound)
1208                         hildon_play_system_sound (MODEST_NEW_MAIL_SOUND_FILE);
1209
1210                 /* Activate LED. This must be deactivated by
1211                    modest_platform_remove_new_mail_notifications */
1212 #ifdef MODEST_HAVE_MCE
1213                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1214                                      MCE_SERVICE,
1215                                      MCE_REQUEST_PATH,
1216                                      MCE_REQUEST_IF,
1217                                      MCE_ACTIVATE_LED_PATTERN,
1218                                      NULL,
1219                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1220                                      DBUS_TYPE_INVALID);
1221 #endif
1222         }
1223 }
1224
1225 void 
1226 modest_platform_on_new_headers_received (GList *URI_list,
1227                                          gboolean show_visual)
1228 {
1229         if (g_list_length (URI_list) == 0)
1230                 return;
1231
1232         if (!show_visual) {
1233                 modest_platform_push_email_notification ();
1234                 /* We do a return here to avoid indentation with an else */
1235                 return;
1236         }
1237
1238 #ifdef MODEST_HAVE_HILDON_NOTIFY
1239         gboolean play_sound;
1240
1241         /* Check whether or not we should play a sound */
1242         play_sound = modest_conf_get_bool (modest_runtime_get_conf (),
1243                                            MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1244                                            NULL);
1245
1246         HildonNotification *notification;
1247         GList *iter;
1248         GSList *notifications_list = NULL;
1249
1250         /* Get previous notifications ids */
1251         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1252                                                    MODEST_CONF_NOTIFICATION_IDS, 
1253                                                    MODEST_CONF_VALUE_INT, NULL);
1254
1255         iter = URI_list;
1256         while (iter) {
1257                 gchar *display_address = NULL;
1258                 gboolean first_notification = TRUE;
1259                 gint notif_id;
1260                 ModestMsgNotificationData *data;
1261
1262                 data = (ModestMsgNotificationData *) iter->data;
1263
1264                 display_address = g_strdup (data->from);
1265                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1266
1267                 notification = hildon_notification_new (display_address,
1268                                                         data->subject,
1269                                                         "qgn_list_messagin",
1270                                                         "email.arrive");
1271                 g_free (display_address);
1272
1273                 /* Add DBus action */
1274                 hildon_notification_add_dbus_action(notification,
1275                                                     "default",
1276                                                     "Cancel",
1277                                                     MODEST_DBUS_SERVICE,
1278                                                     MODEST_DBUS_OBJECT,
1279                                                     MODEST_DBUS_IFACE,
1280                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1281                                                     G_TYPE_STRING, data->uri,
1282                                                     -1);
1283
1284                 /* Play sound if the user wants. Show the LED
1285                    pattern. Show and play just one */
1286                 if (G_UNLIKELY (first_notification)) {
1287                         first_notification = FALSE;
1288                         if (play_sound)  {
1289                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1290                                                                     "sound-file", MODEST_NEW_MAIL_SOUND_FILE);
1291                         }
1292
1293                         /* Set the led pattern */
1294                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1295                                                             "dialog-type", 4);
1296                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1297                                                             "led-pattern",
1298                                                             MODEST_NEW_MAIL_LIGHTING_PATTERN);
1299                 }
1300
1301                 /* Notify. We need to do this in an idle because this function
1302                    could be called from a thread */
1303                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1304
1305                 /* Save id in the list */
1306                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1307                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1308                 /* We don't listen for the "closed" signal, because we
1309                    don't care about if the notification was removed or
1310                    not to store the list in gconf */
1311
1312                 iter = g_list_next (iter);
1313         }
1314
1315         /* Save the ids */
1316         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1317                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1318
1319         g_slist_free (notifications_list);
1320
1321 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1322 }
1323
1324 void
1325 modest_platform_remove_new_mail_notifications (gboolean only_visuals) 
1326 {
1327         if (only_visuals) {
1328 #ifdef MODEST_HAVE_MCE
1329                 osso_rpc_run_system (modest_maemo_utils_get_osso_context (),
1330                                      MCE_SERVICE,
1331                                      MCE_REQUEST_PATH,
1332                                      MCE_REQUEST_IF,
1333                                      MCE_DEACTIVATE_LED_PATTERN,
1334                                      NULL,
1335                                      DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN,
1336                                      DBUS_TYPE_INVALID);
1337 #endif
1338                 return;
1339         }
1340
1341 #ifdef MODEST_HAVE_HILDON_NOTIFY
1342         GSList *notif_list = NULL;
1343
1344         /* Get previous notifications ids */
1345         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1346                                            MODEST_CONF_NOTIFICATION_IDS, 
1347                                            MODEST_CONF_VALUE_INT, NULL);
1348
1349         while (notif_list) {
1350                 gint notif_id;
1351                 NotifyNotification *notif;
1352
1353                 /* Nasty HACK to remove the notifications, set the id
1354                    of the existing ones and then close them */
1355                 notif_id = GPOINTER_TO_INT(notif_list->data);
1356                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1357                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1358
1359                 /* Close the notification, note that some ids could be
1360                    already invalid, but we don't care because it does
1361                    not fail */
1362                 notify_notification_close(notif, NULL);
1363                 g_object_unref(notif);
1364
1365                 /* Delete the link, it's like going to the next */
1366                 notif_list = g_slist_delete_link (notif_list, notif_list);
1367         }
1368
1369         /* Save the ids */
1370         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1371                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1372
1373         g_slist_free (notif_list);
1374
1375 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1376 }
1377
1378
1379
1380 GtkWidget * 
1381 modest_platform_get_global_settings_dialog ()
1382 {
1383         return modest_maemo_global_settings_dialog_new ();
1384 }
1385
1386 void
1387 modest_platform_show_help (GtkWindow *parent_window, 
1388                            const gchar *help_id)
1389 {
1390         osso_return_t result;
1391         g_return_if_fail (help_id);
1392
1393         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1394                                    help_id, HILDON_HELP_SHOW_DIALOG);
1395         
1396         if (result != OSSO_OK) {
1397                 gchar *error_msg;
1398                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1399                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1400                                                 NULL,
1401                                                 error_msg);
1402                 g_free (error_msg);
1403         }
1404 }
1405
1406 void 
1407 modest_platform_show_search_messages (GtkWindow *parent_window)
1408 {
1409         osso_return_t result = OSSO_ERROR;
1410         
1411         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1412                                              "osso_global_search",
1413                                              "search_email", NULL, DBUS_TYPE_INVALID);
1414
1415         if (result != OSSO_OK) {
1416                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1417         }
1418 }
1419
1420 void 
1421 modest_platform_show_addressbook (GtkWindow *parent_window)
1422 {
1423         osso_return_t result = OSSO_ERROR;
1424         
1425         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1426                                              "osso_addressbook",
1427                                              "top_application", NULL, DBUS_TYPE_INVALID);
1428
1429         if (result != OSSO_OK) {
1430                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1431         }
1432 }
1433
1434 GtkWidget *
1435 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1436 {
1437         GtkWidget *widget = modest_folder_view_new (query);
1438
1439         /* Show one account by default */
1440         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1441                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1442
1443         /* Restore settings */
1444         modest_widget_memory_restore (modest_runtime_get_conf(), 
1445                                       G_OBJECT (widget),
1446                                       MODEST_CONF_FOLDER_VIEW_KEY);
1447
1448         return widget;
1449 }
1450
1451 void
1452 banner_finish (gpointer data, GObject *object)
1453 {
1454         ModestWindowMgr *mgr = (ModestWindowMgr *) data;
1455         modest_window_mgr_unregister_banner (mgr);
1456         g_object_unref (mgr);
1457 }
1458
1459 void
1460 modest_platform_information_banner (GtkWidget *parent,
1461                                     const gchar *icon_name,
1462                                     const gchar *text)
1463 {
1464         GtkWidget *banner_parent = NULL;
1465         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1466
1467         if (modest_window_mgr_get_num_windows (mgr) == 0)
1468                 return;
1469
1470         if (parent && GTK_IS_WINDOW (parent)) {
1471                 /* If the window is the active one then show the
1472                    banner on top of this window */
1473                 if (gtk_window_is_active (GTK_WINDOW (parent)))
1474                         banner_parent = parent;
1475                 /* If the window is not the topmost but it's visible
1476                    (it's minimized for example) then show the banner
1477                    with no parent */
1478                 else if (GTK_WIDGET_VISIBLE (parent))
1479                         banner_parent = NULL;
1480                 /* If the window is hidden (like the main window when
1481                    running in the background) then do not show
1482                    anything */
1483                 else 
1484                         return;
1485         }
1486
1487         modest_platform_system_banner (banner_parent, icon_name, text);
1488
1489 }
1490
1491 void
1492 modest_platform_system_banner (GtkWidget *parent,
1493                                     const gchar *icon_name,
1494                                     const gchar *text)
1495 {
1496         GtkWidget *banner = NULL;
1497         ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1498
1499
1500         if (parent && GTK_IS_WINDOW (parent)) {
1501                 if (!gtk_window_is_active (GTK_WINDOW (parent)))
1502                         parent = NULL;
1503         }
1504
1505         banner = hildon_banner_show_information (parent, icon_name, text);
1506
1507         modest_window_mgr_register_banner (mgr);
1508         g_object_ref (mgr);
1509         g_object_weak_ref ((GObject *) banner, banner_finish, mgr);
1510 }
1511
1512 void
1513 modest_platform_information_banner_with_timeout (GtkWidget *parent,
1514                                                  const gchar *icon_name,
1515                                                  const gchar *text,
1516                                                  gint timeout)
1517 {
1518         GtkWidget *banner;
1519
1520         if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1521                 return;
1522
1523         banner = hildon_banner_show_information (parent, icon_name, text);
1524         hildon_banner_set_timeout(HILDON_BANNER(banner), timeout);
1525 }
1526
1527 GtkWidget *
1528 modest_platform_animation_banner (GtkWidget *parent,
1529                                   const gchar *animation_name,
1530                                   const gchar *text)
1531 {
1532         GtkWidget *inf_note = NULL;
1533
1534         g_return_val_if_fail (text != NULL, NULL);
1535
1536         if (modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()) == 0)
1537                 return NULL;
1538
1539         /* If the parent is not visible then do not show */
1540         if (parent && !GTK_WIDGET_VISIBLE (parent))
1541                 return NULL;
1542
1543         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1544
1545         return inf_note;
1546 }
1547
1548 typedef struct
1549 {
1550         GMainLoop* loop;
1551         TnyAccount *account;
1552         gboolean is_online;
1553         gint count_tries;
1554 } CheckAccountIdleData;
1555
1556 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1557
1558 static gboolean 
1559 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1560 {
1561         gboolean stop_trying = FALSE;
1562         g_return_val_if_fail (data && data->account, FALSE);
1563         
1564         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1565                 tny_account_get_connection_status (data->account));     
1566         
1567         if (data && data->account && 
1568                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1569                  * after which the account is likely to be usable, or never likely to be usable soon: */
1570                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1571         {
1572                 data->is_online = TRUE;
1573                 
1574                 stop_trying = TRUE;
1575         } else {
1576                 /* Give up if we have tried too many times: */
1577                 if (data->count_tries >= NUMBER_OF_TRIES) {
1578                         stop_trying = TRUE;
1579                 } else {
1580                         /* Wait for another timeout: */
1581                         ++(data->count_tries);
1582                 }
1583         }
1584         
1585         if (stop_trying) {
1586                 /* Allow the function that requested this idle callback to continue: */
1587                 if (data->loop)
1588                         g_main_loop_quit (data->loop);
1589                         
1590                 if (data->account)
1591                         g_object_unref (data->account);
1592                 
1593                 return FALSE; /* Don't call this again. */
1594         } else {
1595                 return TRUE; /* Call this timeout callback again. */
1596         }
1597 }
1598
1599 /* Return TRUE immediately if the account is already online,
1600  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1601  * soon as the account is online, or FALSE if the account does 
1602  * not become online in the NUMBER_OF_TRIES seconds.
1603  * This is useful when the D-Bus method was run immediately after 
1604  * the application was started (when using D-Bus activation), 
1605  * because the account usually takes a short time to go online.
1606  * The return value is maybe not very useful.
1607  */
1608 gboolean
1609 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1610 {
1611         gboolean is_online;
1612
1613         g_return_val_if_fail (account, FALSE);
1614         
1615         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1616         
1617         if (!tny_device_is_online (modest_runtime_get_device())) {
1618                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1619                 return FALSE;
1620         }
1621         
1622         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1623          * so we avoid wait unnecessarily: */
1624         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1625                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1626                 return TRUE;            
1627         }
1628                 
1629         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1630                 __FUNCTION__, tny_account_get_connection_status (account));
1631         
1632         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1633          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1634          * we want to avoid. */
1635         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1636                 return TRUE;
1637                 
1638         /* This blocks on the result: */
1639         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1640         data->is_online = FALSE;
1641         data->account = account;
1642         g_object_ref (data->account);
1643         data->count_tries = 0;
1644                 
1645         GMainContext *context = NULL; /* g_main_context_new (); */
1646         data->loop = g_main_loop_new (context, FALSE /* not running */);
1647
1648         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1649
1650         /* This main loop will run until the idle handler has stopped it: */
1651         g_main_loop_run (data->loop);
1652
1653         g_main_loop_unref (data->loop);
1654         /* g_main_context_unref (context); */
1655
1656         is_online = data->is_online;
1657         g_slice_free (CheckAccountIdleData, data);
1658         
1659         return is_online;       
1660 }
1661
1662
1663
1664 static void
1665 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1666 {
1667         /* GTK_RESPONSE_HELP means we need to show the certificate */
1668         if (response_id == GTK_RESPONSE_APPLY) {
1669                 GtkWidget *note;
1670                 gchar *msg;
1671                 
1672                 /* Do not close the dialog */
1673                 g_signal_stop_emission_by_name (dialog, "response");
1674
1675                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1676                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1677                 gtk_dialog_run (GTK_DIALOG(note));
1678                 gtk_widget_destroy (note);
1679         }
1680 }
1681
1682
1683 gboolean
1684 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1685                                                      const gchar *certificate)
1686 {
1687         GtkWidget *note;
1688         gint response;
1689         ModestWindow *main_win;
1690         
1691         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1692                 g_debug ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1693                          __FUNCTION__);
1694                 return FALSE;
1695         }
1696
1697         /* don't create it */
1698         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1699         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1700         
1701         
1702         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1703                                            server_name);
1704         
1705         /* We use GTK_RESPONSE_APPLY because we want the button in the
1706            middle of OK and CANCEL the same as the browser does for
1707            example. With GTK_RESPONSE_HELP the view button is aligned
1708            to the left while the other two to the right */
1709         note = hildon_note_new_confirmation_add_buttons  (
1710                 GTK_WINDOW(main_win),
1711                 question,
1712                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1713                 _("mcen_bd_view"),          GTK_RESPONSE_APPLY,   /* abusing this... */
1714                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1715                 NULL, NULL);
1716         
1717         g_signal_connect (G_OBJECT(note), "response", 
1718                           G_CALLBACK(on_cert_dialog_response),
1719                           (gpointer) certificate);
1720         
1721         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1722                                      GTK_WINDOW (note), (GtkWindow *) main_win);
1723         response = gtk_dialog_run(GTK_DIALOG(note));
1724
1725         on_destroy_dialog (note);
1726         g_free (question);
1727         
1728         return response == GTK_RESPONSE_OK;
1729 }
1730
1731 gboolean
1732 modest_platform_run_alert_dialog (const gchar* prompt, 
1733                                   gboolean is_question)
1734 {       
1735         ModestWindow *main_win; 
1736
1737         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1738                 g_debug ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1739                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1740                 return is_question ? FALSE : TRUE;
1741         }
1742
1743         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1744         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1745         
1746         gboolean retval = TRUE;
1747         if (is_question) {
1748                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1749                  * when it is a question.
1750                  * Obviously, we need tinymail to use more specific error codes instead,
1751                  * so we know what buttons to show. */
1752                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1753                                                                               prompt));
1754                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1755                                              GTK_WINDOW (dialog), (GtkWindow *) main_win);
1756                 
1757                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1758                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1759                 
1760                 on_destroy_dialog (dialog);             
1761         } else {
1762                 /* Just show the error text and use the default response: */
1763                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1764                                                         prompt, FALSE);
1765         }
1766         return retval;
1767 }
1768
1769 /***************/
1770 typedef struct {
1771         GtkWindow *parent_window;
1772         ModestConnectedPerformer callback;
1773         TnyAccount *account;
1774         gpointer user_data;
1775         gchar *iap;
1776         TnyDevice *device;
1777 } OnWentOnlineInfo;
1778  
1779 static void 
1780 on_went_online_info_free (OnWentOnlineInfo *info)
1781 {
1782         /* And if we cleanup, we DO cleanup  :-)  */
1783         
1784         if (info->device)
1785                 g_object_unref (info->device);
1786         if (info->iap)
1787                 g_free (info->iap);
1788         if (info->parent_window)
1789                 g_object_unref (info->parent_window);
1790         if (info->account)
1791                 g_object_unref (info->account);
1792         
1793         g_slice_free (OnWentOnlineInfo, info);
1794         
1795         /* We're done ... */
1796         
1797         return;
1798 }
1799  
1800 static void
1801 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1802 {
1803         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1804  
1805         /* Now it's really time to callback to the caller. If going online didn't succeed,
1806          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1807          * canceled will be set. Etcetera etcetera. */
1808         
1809         if (info->callback) {
1810                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1811         }
1812         
1813         /* This is our last call, we must cleanup here if we didn't yet do that */
1814         on_went_online_info_free (info);
1815         
1816         return;
1817 }
1818  
1819  
1820 static void
1821 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1822 {
1823         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1824         info->iap = g_strdup (iap_id);
1825         
1826         if (canceled || err || !info->account) {
1827         
1828                 /* If there's a problem or if there's no account (then that's it for us, we callback
1829                  * the caller's callback now. He'll have to handle err or canceled, of course.
1830                  * We are not really online, as the account is not really online here ... */    
1831                 
1832                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1833                  * this info. We don't cleanup err, Tinymail does that! */
1834                 
1835                 if (info->callback) {
1836                         
1837                         /* info->account can be NULL here, this means that the user did not
1838                          * provide a nice account instance. We'll assume that the user knows
1839                          * what he's doing and is happy with just the device going online. 
1840                          * 
1841                          * We can't do magic, we don't know what account the user wants to
1842                          * see going online. So just the device goes online, end of story */
1843                         
1844                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1845                 }
1846                 
1847         } else if (info->account) {
1848                 
1849                 /* If there's no problem and if we have an account, we'll put the account
1850                  * online too. When done, the callback of bringing the account online
1851                  * will callback the caller's callback. This is the most normal case. */
1852  
1853                 info->device = TNY_DEVICE (g_object_ref (device));
1854                 
1855                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1856                                               on_account_went_online, info);
1857                 
1858                 /* The on_account_went_online cb frees up the info, go look if you
1859                  * don't believe me! (so we return here) */
1860                 
1861                 return;
1862         }
1863         
1864         /* We cleanup if we are not bringing the account online too */
1865         on_went_online_info_free (info);
1866  
1867         return; 
1868 }
1869         
1870 void 
1871 modest_platform_connect_and_perform (GtkWindow *parent_window, 
1872                                      gboolean force,
1873                                      TnyAccount *account, 
1874                                      ModestConnectedPerformer callback, 
1875                                      gpointer user_data)
1876 {
1877         gboolean device_online;
1878         TnyDevice *device;
1879         TnyConnectionStatus conn_status;
1880         OnWentOnlineInfo *info;
1881         
1882         device = modest_runtime_get_device();
1883         device_online = tny_device_is_online (device);
1884
1885         /* If there is no account check only the device status */
1886         if (!account) {
1887                 
1888                 if (device_online) {
1889  
1890                         /* We promise to instantly perform the callback, so ... */
1891                         if (callback) {
1892                                 callback (FALSE, NULL, parent_window, account, user_data);
1893                         }
1894                         
1895                 } else {
1896                         
1897                         info = g_slice_new0 (OnWentOnlineInfo);
1898                         
1899                         info->iap = NULL;
1900                         info->device = NULL;
1901                         info->account = NULL;
1902                 
1903                         if (parent_window)
1904                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1905                         else
1906                                 info->parent_window = NULL;
1907                         info->user_data = user_data;
1908                         info->callback = callback;
1909                 
1910                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1911                                                               force, on_conic_device_went_online, 
1912                                                               info);
1913  
1914                         /* We'll cleanup in on_conic_device_went_online */
1915                 }
1916  
1917                 /* The other code has no more reason to run. This is all that we can do for the
1918                  * caller (he should have given us a nice and clean account instance!). We
1919                  * can't do magic, we don't know what account he intends to bring online. So
1920                  * we'll just bring the device online (and await his false bug report). */
1921                 
1922                 return;
1923         }
1924  
1925         
1926         /* Return if the account is already connected */
1927         
1928         conn_status = tny_account_get_connection_status (account);
1929         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1930  
1931                 /* We promise to instantly perform the callback, so ... */
1932                 if (callback) {
1933                         callback (FALSE, NULL, parent_window, account, user_data);
1934                 }
1935                 
1936                 return;
1937         }
1938         
1939         /* Else, we are in a state that requires that we go online before we
1940          * call the caller's callback. */
1941         
1942         info = g_slice_new0 (OnWentOnlineInfo);
1943         
1944         info->device = NULL;
1945         info->iap = NULL;
1946         info->account = TNY_ACCOUNT (g_object_ref (account));
1947         
1948         if (parent_window)
1949                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1950         else
1951                 info->parent_window = NULL;
1952         
1953         /* So we'll put the callback away for later ... */
1954         
1955         info->user_data = user_data;
1956         info->callback = callback;
1957         
1958         if (!device_online) {
1959  
1960                 /* If also the device is offline, then we connect both the device 
1961                  * and the account */
1962                 
1963                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1964                                                       force, on_conic_device_went_online, 
1965                                                       info);
1966                 
1967         } else {
1968                 
1969                 /* If the device is online, we'll just connect the account */
1970                 
1971                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1972                                               on_account_went_online, info);
1973         }
1974  
1975         /* The info gets freed by on_account_went_online or on_conic_device_went_online
1976          * in both situations, go look if you don't believe me! */
1977         
1978         return;
1979 }
1980
1981 void
1982 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
1983                                                gboolean force,
1984                                                TnyFolderStore *folder_store, 
1985                                                ModestConnectedPerformer callback, 
1986                                                gpointer user_data)
1987 {
1988         TnyAccount *account = NULL;
1989
1990         if (!folder_store ||
1991             (TNY_IS_MERGE_FOLDER (folder_store) &&
1992              (tny_folder_get_folder_type (TNY_FOLDER(folder_store)) == TNY_FOLDER_TYPE_OUTBOX))) {
1993
1994                 /* We promise to instantly perform the callback, so ... */
1995                 if (callback) {
1996                         GError *error = NULL;
1997                         g_set_error (&error, TNY_ERROR_DOMAIN, TNY_SERVICE_ERROR_UNKNOWN,
1998                                      "Unable to move or not found folder");
1999                         callback (FALSE, error, parent_window, NULL, user_data);
2000                         g_error_free (error);
2001                 }
2002                 return;
2003
2004         } else if (TNY_IS_FOLDER (folder_store)) {
2005                 /* Get the folder's parent account: */
2006                 account = tny_folder_get_account (TNY_FOLDER (folder_store));
2007         } else if (TNY_IS_ACCOUNT (folder_store)) {
2008                 /* Use the folder store as an account: */
2009                 account = TNY_ACCOUNT (g_object_ref (folder_store));
2010         }
2011  
2012         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2013                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2014                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2015  
2016                         /* No need to connect a local account */
2017                         if (callback)
2018                                 callback (FALSE, NULL, parent_window, account, user_data);
2019
2020                         goto clean;
2021                 }
2022         }
2023         modest_platform_connect_and_perform (parent_window, force, account, callback, user_data);
2024  
2025  clean:
2026         if (account)
2027                 g_object_unref (account);
2028 }
2029
2030 static void
2031 src_account_connect_performer (gboolean canceled, 
2032                                GError *err,
2033                                GtkWindow *parent_window, 
2034                                TnyAccount *src_account, 
2035                                gpointer user_data)
2036 {
2037         DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data;
2038
2039         if (canceled || err) {
2040                 /* If there was any error call the user callback */
2041                 info->callback (canceled, err, parent_window, src_account, info->data);
2042         } else {
2043                 /* Connect the destination account */
2044                 modest_platform_connect_if_remote_and_perform (parent_window, TRUE, 
2045                                                                TNY_FOLDER_STORE (info->dst_account),
2046                                                                info->callback, info->data);
2047         }
2048
2049         /* Free the info object */
2050         g_object_unref (info->dst_account);
2051         g_slice_free (DoubleConnectionInfo, info);
2052 }
2053
2054
2055 void 
2056 modest_platform_double_connect_and_perform (GtkWindow *parent_window, 
2057                                             gboolean force,
2058                                             TnyFolderStore *folder_store,
2059                                             DoubleConnectionInfo *connect_info)
2060 {
2061         modest_platform_connect_if_remote_and_perform(parent_window, 
2062                                                       force,
2063                                                       folder_store, 
2064                                                       src_account_connect_performer, 
2065                                                       connect_info);
2066 }
2067
2068 GtkWidget *
2069 modest_platform_get_account_settings_wizard (void)
2070 {
2071         ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new ();
2072
2073         return GTK_WIDGET (dialog);
2074 }
2075
2076 ModestConnectedVia
2077 modest_platform_get_current_connection (void)
2078 {
2079         TnyDevice *device = NULL;
2080         ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY;
2081         
2082         device = modest_runtime_get_device ();
2083
2084         if (!tny_device_is_online (device))
2085                 return MODEST_CONNECTED_VIA_ANY;
2086
2087 #ifdef MODEST_HAVE_CONIC
2088         /* Get iap id */
2089         const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
2090         if (iap_id) {
2091                 ConIcIap *iap = tny_maemo_conic_device_get_iap (
2092                         TNY_MAEMO_CONIC_DEVICE (device), iap_id);
2093                 const gchar *bearer_type = con_ic_iap_get_bearer_type (iap);
2094                 if (bearer_type) {
2095                         if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) ||
2096                             !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) ||
2097                             !strcmp (bearer_type, "WIMAX")) {
2098                                 retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX;
2099                         } else {
2100                                 retval = MODEST_CONNECTED_VIA_ANY;
2101                         }
2102                 }       
2103                 g_object_unref (iap);
2104         }
2105 #else
2106         retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */  
2107 #endif /* MODEST_HAVE_CONIC */
2108         return retval;
2109 }
2110
2111 gboolean
2112 modest_platform_check_memory_low (ModestWindow *win,
2113                                   gboolean visuals)
2114 {
2115         gboolean lowmem;
2116         
2117         /* are we in low memory state? */
2118         lowmem = osso_mem_in_lowmem_state () ? TRUE : FALSE;
2119         
2120         if (win && lowmem && visuals)
2121                 modest_platform_run_information_dialog (
2122                         GTK_WINDOW(win),
2123                         _KR("memr_ib_operation_disabled"),
2124                         TRUE);
2125
2126         if (lowmem)
2127                 g_debug ("%s: low memory reached. disallowing some operations",
2128                          __FUNCTION__);
2129
2130         return lowmem;
2131 }
2132
2133 void 
2134 modest_platform_run_folder_details_dialog (GtkWindow *parent_window,
2135                                            TnyFolder *folder)
2136 {
2137         GtkWidget *dialog;
2138         
2139         /* Create dialog */
2140         dialog = modest_details_dialog_new_with_folder (parent_window, folder);
2141
2142         /* Run dialog */
2143         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2144                                      GTK_WINDOW (dialog), 
2145                                      parent_window);
2146         gtk_widget_show_all (dialog);
2147
2148         g_signal_connect_swapped (dialog, "response", 
2149                                   G_CALLBACK (gtk_widget_destroy),
2150                                   dialog);
2151 }
2152
2153 void
2154 modest_platform_run_header_details_dialog (GtkWindow *parent_window,
2155                                            TnyHeader *header,
2156                                            gboolean async_get_size,
2157                                            TnyMsg *msg)
2158 {
2159         GtkWidget *dialog;
2160         
2161         /* Create dialog */
2162         dialog = modest_details_dialog_new_with_header (parent_window, header, TRUE);
2163
2164         /* Run dialog */
2165         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
2166                                      GTK_WINDOW (dialog),
2167                                      parent_window);
2168         gtk_widget_show_all (dialog);
2169
2170         g_signal_connect_swapped (dialog, "response", 
2171                                   G_CALLBACK (gtk_widget_destroy),
2172                                   dialog);
2173 }
2174
2175 osso_context_t *
2176 modest_platform_get_osso_context (void)
2177 {
2178         return modest_maemo_utils_get_osso_context ();
2179 }
2180
2181 GtkWidget* 
2182 modest_platform_create_move_to_dialog (GtkWindow *parent_window,
2183                                        GtkWidget **folder_view)
2184 {
2185         GtkWidget *dialog, *folder_view_container;
2186
2187         dialog = gtk_dialog_new_with_buttons (_("mcen_ti_moveto_folders_title"),
2188                                               GTK_WINDOW (parent_window),
2189                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR |
2190                                               GTK_DIALOG_DESTROY_WITH_PARENT,
2191                                               _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK,
2192                                               _("mcen_bd_new"), MODEST_GTK_RESPONSE_NEW_FOLDER,
2193                                               _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
2194                                               NULL);
2195
2196         /* Create folder view */
2197         *folder_view = modest_platform_create_folder_view (NULL);
2198
2199         /* Create pannable and add it to the dialog */
2200         folder_view_container = gtk_scrolled_window_new (NULL, NULL);
2201         gtk_scrolled_window_set_policy  (GTK_SCROLLED_WINDOW (folder_view_container),
2202                                          GTK_POLICY_AUTOMATIC,
2203                                          GTK_POLICY_AUTOMATIC);
2204         gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), folder_view_container);
2205         gtk_container_add (GTK_CONTAINER (folder_view_container), *folder_view);
2206
2207         gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 300);
2208
2209         gtk_widget_show (GTK_DIALOG (dialog)->vbox);
2210         gtk_widget_show (folder_view_container);
2211         gtk_widget_show (*folder_view);
2212
2213         return dialog;
2214 }
2215
2216
2217 TnyList *
2218 modest_platform_get_list_to_move (ModestWindow *window)
2219 {
2220         TnyList *list = NULL;
2221
2222         /* If it's a main window then it could be that we're moving a
2223            folder or a set of messages */
2224         if (MODEST_IS_MAIN_WINDOW (window)) {
2225                 ModestHeaderView *header_view = NULL;
2226                 ModestFolderView *folder_view = NULL;
2227
2228                 folder_view = (ModestFolderView *)
2229                         modest_main_window_get_child_widget (MODEST_MAIN_WINDOW (window),
2230                                                              MODEST_MAIN_WINDOW_WIDGET_TYPE_FOLDER_VIEW);
2231                 header_view = (ModestHeaderView *)
2232                         modest_main_window_get_child_widget (MODEST_MAIN_WINDOW (window),
2233                                                              MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW);
2234
2235                 /* Get folder or messages to transfer */
2236                 if (gtk_widget_is_focus (GTK_WIDGET (folder_view))) {
2237                         TnyFolderStore *src_folder;
2238
2239                         src_folder = modest_folder_view_get_selected (folder_view);
2240                         if (src_folder) {
2241                                 list = tny_simple_list_new ();
2242                                 tny_list_prepend (list, G_OBJECT (src_folder));
2243                                 g_object_unref (src_folder);
2244                         }
2245                 } else if (gtk_widget_is_focus (GTK_WIDGET(header_view))) {
2246                         list = modest_header_view_get_selected_headers(header_view);
2247                 }
2248         } else if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
2249                 TnyHeader *header = NULL;
2250
2251                 /* We simply return the currently viewed message */
2252                 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
2253                 if (header) {
2254                         list = tny_simple_list_new ();
2255                         tny_list_prepend (list, G_OBJECT (header));
2256                         g_object_unref (header);
2257                 }
2258         } else {
2259                 g_return_val_if_reached (NULL);
2260         }
2261
2262         return list;
2263 }