Modified webpage: now tinymail repository is in gitorious.
[modest] / src / widgets / modest-msg-view-window.c
index 38456ee..91dfcc4 100644 (file)
 
 #ifdef MODEST_TOOLKIT_HILDON2
 #include <hildon/hildon.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#include <X11/XKBlib.h>
+#include <X11/Xdmcp.h>
 #endif
 
+#include <tny-camel-bs-mime-part.h>
+#include <tny-camel-bs-msg.h>
+
 #define MYDOCS_ENV "MYDOCSDIR"
 #define DOCS_FOLDER ".documents"
 
@@ -124,12 +131,14 @@ struct _ModestMsgViewWindowPrivate {
        gulong row_deleted_handler;
        gulong row_inserted_handler;
        gulong rows_reordered_handler;
+       gulong fetch_image_redraw_handler;
 
        guint purge_timeout;
        GtkWidget *remove_attachment_banner;
 
        gchar *msg_uid;
        TnyMimePart *other_body;
+       TnyMsg * top_msg;
 
        GSList *sighandlers;
 };
@@ -241,6 +250,11 @@ static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
                                                   gpointer userdata);
 static void update_branding (ModestMsgViewWindow *self);
 static void sync_flags      (ModestMsgViewWindow *self);
+static gboolean on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part, 
+                                   GtkContainer *container, ModestMsgViewWindow *self);
+
+static gboolean on_realize (GtkWidget *widget,
+                           gpointer userdata);
 
 /* list my signals */
 enum {
@@ -269,7 +283,7 @@ static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
                                                     ModestMsgViewWindowPrivate))
 /* globals */
-static GtkWindowClass *parent_class = NULL;
+static ModestWindowParentClass *parent_class = NULL;
 
 /* uncomment the following if you have defined any signals */
 static guint signals[LAST_SIGNAL] = {0};
@@ -490,6 +504,7 @@ modest_msg_view_window_init (ModestMsgViewWindow *obj)
        priv->row_deleted_handler = 0;
        priv->row_inserted_handler = 0;
        priv->rows_reordered_handler = 0;
+       priv->fetch_image_redraw_handler = 0;
        priv->progress_hint = FALSE;
        priv->fetching_images = 0;
 
@@ -498,12 +513,19 @@ modest_msg_view_window_init (ModestMsgViewWindow *obj)
        priv->remove_attachment_banner = NULL;
        priv->msg_uid = NULL;
        priv->other_body = NULL;
-       
+
        priv->sighandlers = NULL;
-       
+
        /* Init window */
        init_window (MODEST_MSG_VIEW_WINDOW(obj));
 
+#ifdef MODEST_TOOLKIT_HILDON2
+       /* Grab the zoom keys, it will be used for Zoom and not for
+          changing volume */
+       g_signal_connect (G_OBJECT (obj), "realize",
+                         G_CALLBACK (on_realize),
+                         NULL);
+#endif
 }
 
 static void
@@ -637,11 +659,21 @@ modest_msg_view_window_finalize (GObject *obj)
           call this function before */
        modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
 
+       if (priv->fetch_image_redraw_handler > 0) {
+               g_source_remove (priv->fetch_image_redraw_handler);
+               priv->fetch_image_redraw_handler = 0;
+       }
+
        if (priv->other_body != NULL) {
                g_object_unref (priv->other_body);
                priv->other_body = NULL;
        }
 
+       if (priv->top_msg != NULL) {
+               g_object_unref (priv->top_msg);
+               priv->top_msg = NULL;
+       }
+
        if (priv->header_model != NULL) {
                g_object_unref (priv->header_model);
                priv->header_model = NULL;
@@ -811,6 +843,8 @@ modest_msg_view_window_construct (ModestMsgViewWindow *self,
                          G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
        g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
                          G_CALLBACK (modest_ui_actions_on_limit_error), obj);
+       g_signal_connect (G_OBJECT(priv->msg_view), "handle_calendar",
+                         G_CALLBACK (on_handle_calendar), obj);
        g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
                          G_CALLBACK (on_fetch_image), obj);
 
@@ -891,6 +925,7 @@ modest_msg_view_window_new_with_header_model (TnyMsg *msg,
        modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
 
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
+       priv->top_msg = NULL;
 
        /* Remember the message list's TreeModel so we can detect changes
         * and change the list selection when necessary: */
@@ -969,6 +1004,7 @@ modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
        modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
 
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
+       priv->top_msg = NULL;
 
        is_merge = g_str_has_prefix (msg_uid, "merge:");
 
@@ -1049,6 +1085,7 @@ modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
        modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
 
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
+       priv->top_msg = NULL;
 
        /* Remember the message list's TreeModel so we can detect changes 
         * and change the list selection when necessary: */
@@ -1142,6 +1179,7 @@ modest_msg_view_window_new_for_search_result (TnyMsg *msg,
        modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
 
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
+       priv->top_msg = NULL;
 
        /* Remember that this is a search result, 
         * so we can disable some UI appropriately: */
@@ -1176,6 +1214,7 @@ modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
 ModestWindow *
 modest_msg_view_window_new_with_other_body (TnyMsg *msg, 
                                            TnyMimePart *other_body,
+                                           TnyMsg *top_msg,
                                            const gchar *modest_account_name,
                                            const gchar *mailbox,
                                            const gchar *msg_uid)
@@ -1197,6 +1236,11 @@ modest_msg_view_window_new_with_other_body (TnyMsg *msg,
        } else {
                tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
        }
+       if (top_msg) {
+               priv->top_msg = g_object_ref (top_msg);
+       } else {
+               priv->top_msg = NULL;
+       }
        update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
        update_branding (MODEST_MSG_VIEW_WINDOW (obj));
 
@@ -1211,12 +1255,13 @@ modest_msg_view_window_new_with_other_body (TnyMsg *msg,
 }
 
 ModestWindow *
-modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
+modest_msg_view_window_new_for_attachment (TnyMsg *msg,
+                                          TnyMsg *top_msg,
                                           const gchar *modest_account_name,
                                           const gchar *mailbox,
                                           const gchar *msg_uid)
 {
-       return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
+       return modest_msg_view_window_new_with_other_body (msg, NULL, top_msg, modest_account_name, mailbox, msg_uid);
 }
 
 static void
@@ -1523,6 +1568,21 @@ modest_msg_view_window_get_message (ModestMsgViewWindow *self)
        return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
 }
 
+TnyMsg*
+modest_msg_view_window_get_top_message (ModestMsgViewWindow *self)
+{
+       ModestMsgViewWindowPrivate *priv;
+       
+       g_return_val_if_fail (self, NULL);
+       
+       priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
+
+       if (priv->top_msg)
+               return g_object_ref (priv->top_msg);
+       else
+               return NULL;
+}
+
 const gchar*
 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
 {
@@ -1737,7 +1797,7 @@ modest_msg_view_window_key_event (GtkWidget *window,
 {
        GtkWidget *focus;
 
-       focus = gtk_window_get_focus (GTK_WINDOW (window));
+       focus = gtk_container_get_focus_child ((GtkContainer *) window);
 
        /* for the isearch toolbar case */
        if (focus && GTK_IS_ENTRY (focus)) {
@@ -1886,7 +1946,7 @@ typedef struct {
 static void
 message_reader_performer (gboolean canceled, 
                          GError *err,
-                         GtkWindow *parent_window, 
+                         ModestWindow *parent_window,
                          TnyAccount *account, 
                          gpointer user_data)
 {
@@ -1973,9 +2033,10 @@ message_reader (ModestMsgViewWindow *window,
                   we're not online */
                if (!tny_device_is_online (modest_runtime_get_device())) {
                        GtkResponseType response;
+                       GtkWindow *toplevel;
 
-                       response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
-                                                                           _("mcen_nc_get_msg"));
+                       toplevel = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) window);
+                       response = modest_platform_run_confirmation_dialog (toplevel, _("mcen_nc_get_msg"));
                        if (response == GTK_RESPONSE_CANCEL) {
                                update_window_title (window);
                                return FALSE;
@@ -2001,7 +2062,7 @@ message_reader (ModestMsgViewWindow *window,
                        }
 
                        /* Offer the connection dialog if necessary */
-                       modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
+                       modest_platform_connect_if_remote_and_perform ((ModestWindow *) window,
                                                                       TRUE,
                                                                       TNY_FOLDER_STORE (folder),
                                                                       message_reader_performer, 
@@ -2033,7 +2094,7 @@ message_reader (ModestMsgViewWindow *window,
        else
                info->row_reference = NULL;
 
-       message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
+       message_reader_performer (FALSE, NULL, (ModestWindow *) window, account, info);
         if (account)
                g_object_unref (account);
        if (folder)
@@ -2167,10 +2228,10 @@ view_msg_cb (ModestMailOperation *mail_op,
        modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
 
        row_reference = (GtkTreeRowReference *) user_data;
-       if (canceled) {
+       self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
+       if (canceled || !self || MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self)->msg_view == NULL ) {
                if (row_reference)
                        gtk_tree_row_reference_free (row_reference);
-               self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
                if (self) {
                        /* Restore window title */
                        update_window_title (self);
@@ -2184,7 +2245,6 @@ view_msg_cb (ModestMailOperation *mail_op,
        if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
                if (row_reference)
                        gtk_tree_row_reference_free (row_reference);
-               self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
                if (self) {
                        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
                        /* First we check if the parent is a folder window */
@@ -2248,7 +2308,6 @@ view_msg_cb (ModestMailOperation *mail_op,
        }
 
        /* Get the window */ 
-       self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
        g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
 
@@ -2268,8 +2327,14 @@ view_msg_cb (ModestMailOperation *mail_op,
        }
 
        /* Mark header as read */
-       if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
+       if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN)) {
+               gchar *uid;
+
                tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
+               uid = modest_tny_folder_get_header_unique_id (header);
+               modest_platform_emit_msg_read_changed_signal (uid, TRUE);
+               g_free (uid);
+       }
 
        /* Set new message */
        if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
@@ -2658,6 +2723,9 @@ on_decode_to_stream_async_handler (TnyMimePart *mime_part,
 {
        DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
        const gchar *content_type;
+       ModestMsgViewWindowPrivate *priv;
+
+       priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
 
        if (cancelled || err) {
                if (err) {
@@ -2684,7 +2752,7 @@ on_decode_to_stream_async_handler (TnyMimePart *mime_part,
        set_progress_hint (helper->self, FALSE);
 
        content_type = tny_mime_part_get_content_type (mime_part);
-       if (g_str_has_prefix (content_type, "message/rfc822")) {
+       if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
                ModestWindowMgr *mgr;
                ModestWindow *msg_win = NULL;
                TnyMsg * msg;
@@ -2695,6 +2763,7 @@ on_decode_to_stream_async_handler (TnyMimePart *mime_part,
 
                fd = g_open (helper->file_path, O_RDONLY, 0644);
                if (fd != -1) {
+                       TnyMsg *top_msg;
                        file_stream = tny_fs_stream_new (fd);
 
                        mgr = modest_runtime_get_window_mgr ();
@@ -2706,8 +2775,16 @@ on_decode_to_stream_async_handler (TnyMimePart *mime_part,
                                account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
 
                        msg = tny_camel_msg_new ();
-                       tny_camel_msg_parse (msg, file_stream);
-                       msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
+                       tny_camel_msg_parse ((TnyCamelMsg *) msg, file_stream);
+
+                       if (priv->top_msg)
+                               top_msg = g_object_ref (priv->top_msg);
+                       else
+                               top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
+
+                       msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg, 
+                                                                            account, mailbox, helper->attachment_uid);
+                       if (top_msg) g_object_unref (top_msg);
                        modest_window_set_zoom (MODEST_WINDOW (msg_win),
                                                modest_window_get_zoom (MODEST_WINDOW (helper->self)));
                        if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
@@ -2726,7 +2803,7 @@ on_decode_to_stream_async_handler (TnyMimePart *mime_part,
                g_chmod(helper->file_path, 0444);
 
                /* Activate the file */
-               modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
+               modest_platform_activate_file (helper->file_path, content_type);
        }
 
  free:
@@ -2737,6 +2814,24 @@ on_decode_to_stream_async_handler (TnyMimePart *mime_part,
        g_slice_free (DecodeAsyncHelper, helper);
 }
 
+static void
+view_attachment_connect_handler (gboolean canceled,
+                                GError *err,
+                                GtkWindow *parent_window,
+                                TnyAccount *account,
+                                TnyMimePart *part)
+{
+
+       if (canceled || err) {
+               g_object_unref (part);
+               return;
+       }
+
+       modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
+                                               part);
+       g_object_unref (part);
+}
+
 void
 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
                                        TnyMimePart *mime_part)
@@ -2786,6 +2881,28 @@ modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
        if (tny_mime_part_is_purged (mime_part))
                goto frees;
 
+       if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
+           !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
+               gboolean is_merge;
+               TnyAccount *account;
+
+               is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
+               account = NULL;
+               /* Get the account */
+               if (!is_merge)
+                       account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
+                                                                 priv->msg_uid);
+
+               if (!tny_device_is_online (modest_runtime_get_device())) {
+                       modest_platform_connect_and_perform ((ModestWindow *) window,
+                                                            TRUE,
+                                                            TNY_ACCOUNT (account),
+                                                            (ModestConnectedPerformer) view_attachment_connect_handler,
+                                                            g_object_ref (mime_part));
+                       goto frees;
+               }
+       }
+
        if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
                gchar *filepath = NULL;
                const gchar *att_filename = tny_mime_part_get_filename (mime_part);
@@ -2870,6 +2987,7 @@ modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
                if (found) {
                        g_debug ("window for this body is already being created");
                } else {
+                       TnyMsg *top_msg;
 
                        /* it's not found, so create a new window for it */
                        modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
@@ -2877,9 +2995,16 @@ modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
                        const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
                        if (!account)
                                account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
+
+                       if (priv->top_msg)
+                               top_msg = g_object_ref (priv->top_msg);
+                       else
+                               top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
                        
-                       msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
+                       msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
                                                                              account, mailbox, attachment_uid);
+
+                       if (top_msg) g_object_unref (top_msg);
                        
                        modest_window_set_zoom (MODEST_WINDOW (msg_win),
                                                modest_window_get_zoom (MODEST_WINDOW (window)));
@@ -2905,14 +3030,20 @@ modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
                         * thus, we don't do anything */
                        g_debug ("window for is already being created");
                } else {
+                       TnyMsg *top_msg;
                        /* it's not found, so create a new window for it */
                        modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
                        gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
                        const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
                        if (!account)
                                account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
-                       msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, 
-                                                                            mailbox, attachment_uid);
+                       if (priv->top_msg)
+                               top_msg = g_object_ref (priv->top_msg);
+                       else
+                               top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
+                       msg_win = modest_msg_view_window_new_for_attachment (
+                               TNY_MSG (mime_part), top_msg, account, 
+                               mailbox, attachment_uid);
                        modest_window_set_zoom (MODEST_WINDOW (msg_win),
                                                modest_window_get_zoom (MODEST_WINDOW (window)));
                        if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
@@ -2976,7 +3107,9 @@ idle_save_mime_part_show_result (SaveMimePartInfo *info)
         * modest_platform_system_banner is or does Gtk+ code */
 
        gdk_threads_enter (); /* CHECKED */
-       if (info->result == GNOME_VFS_OK) {
+       if (info->result == GNOME_VFS_ERROR_CANCELLED) {
+               /* nothing */
+       } else if (info->result == GNOME_VFS_OK) {
                modest_platform_system_banner (NULL, NULL, _CS_SAVED);
        } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
                gchar *msg = NULL;
@@ -2991,12 +3124,59 @@ idle_save_mime_part_show_result (SaveMimePartInfo *info)
        } else {
                modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
        }
+       set_progress_hint (info->window, FALSE);
        save_mime_part_info_free (info, FALSE);
        gdk_threads_leave (); /* CHECKED */
 
        return FALSE;
 }
 
+static void
+save_mime_part_to_file_connect_handler (gboolean canceled,
+                                       GError *err,
+                                       GtkWindow *parent_window,
+                                       TnyAccount *account,
+                                       SaveMimePartInfo *info)
+{
+       if (canceled || err) {
+               if (canceled && !err) {
+                       info->result = GNOME_VFS_ERROR_CANCELLED;
+               }
+               g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
+       } else {
+               g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
+       }
+}
+
+static gboolean
+save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
+{
+       gboolean is_merge;
+       TnyAccount *account;
+       ModestMsgViewWindowPrivate *priv;
+
+       priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
+
+       is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
+       account = NULL;
+
+       /* Get the account */
+       if (!is_merge)
+               account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
+                                                         priv->msg_uid);
+
+       modest_platform_connect_and_perform ((ModestWindow *) info->window,
+                                            TRUE,
+                                            TNY_ACCOUNT (account),
+                                            (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
+                                            info);
+
+       if (account)
+               g_object_unref (account);
+
+       return FALSE;
+}
+
 static gpointer
 save_mime_part_to_file (SaveMimePartInfo *info)
 {
@@ -3004,6 +3184,38 @@ save_mime_part_to_file (SaveMimePartInfo *info)
        TnyStream *stream;
        SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
 
+       if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
+           !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
+               gboolean check_online = TRUE;
+               ModestMsgViewWindowPrivate *priv = NULL;
+
+               /* Check if we really need to connect to save the mime part */
+               priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
+               if (g_str_has_prefix (priv->msg_uid, "merge:")) {
+                       check_online = FALSE;
+               } else {
+                       TnyAccountStore *acc_store;
+                       TnyAccount *account = NULL;
+
+                       acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
+                       account = tny_account_store_find_account (acc_store, priv->msg_uid);
+
+                       if (account) {
+                               if (tny_account_get_connection_status (account) ==
+                                   TNY_CONNECTION_STATUS_CONNECTED)
+                                       check_online = FALSE;
+                               g_object_unref (account);
+                       } else {
+                               check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
+                       }
+               }
+
+               if (check_online) {
+                       g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
+                       return NULL;
+               }
+       }
+
        info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
        if (info->result == GNOME_VFS_OK) {
                GError *error = NULL;
@@ -3036,7 +3248,7 @@ save_mime_part_to_file (SaveMimePartInfo *info)
                if (written < 0) {
                        g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
 
-                       if ((error->domain == TNY_ERROR_DOMAIN) && 
+                       if (error && (error->domain == TNY_ERROR_DOMAIN) &&
                            (error->code == TNY_IO_ERROR_WRITE) &&
                            (errno == ENOSPC)) {
                                info->result = GNOME_VFS_ERROR_NO_SPACE;
@@ -3091,14 +3303,14 @@ save_mime_parts_to_file_with_checks (GtkWindow *parent,
 
                        escaped_basename = g_uri_unescape_string (basename, NULL);
                        message = g_strdup_printf ("%s\n%s",
-                                                  _FM("docm_nc_replace_file"),
+                                                  _FM_REPLACE_FILE,
                                                   (escaped_basename) ? escaped_basename : "");
                        response = modest_platform_run_confirmation_dialog (parent, message);
                        g_free (message);
                        g_free (escaped_basename);
                } else {
                        response = modest_platform_run_confirmation_dialog (parent,
-                                                                           _FM("docm_nc_replace_multiple"));
+                                                                           _FM_REPLACE_MULTIPLE);
                }
                if (response != GTK_RESPONSE_OK)
                        is_ok = FALSE;
@@ -3107,6 +3319,8 @@ save_mime_parts_to_file_with_checks (GtkWindow *parent,
        if (!is_ok) {
                save_mime_part_info_free (info, TRUE);
        } else {
+               /* Start progress and launch thread */
+               set_progress_hint (info->window, TRUE);
                g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
        }
 
@@ -3153,10 +3367,10 @@ save_attachments_response (GtkDialog *dialog,
                if (modest_maemo_utils_in_usb_mode ()) {
                        err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
                } else {
-                       err_msg = _FM("sfil_ib_readonly_location");
+                       err_msg = _FM_READ_ONLY_LOCATION;
                }
 #else
-               err_msg = _FM("sfil_ib_readonly_location");
+               err_msg = _FM_READ_ONLY_LOCATION;
 #endif
                modest_platform_system_banner (NULL, NULL, err_msg);
        } else {
@@ -3207,6 +3421,28 @@ save_attachments_response (GtkDialog *dialog,
        gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
+static gboolean
+msg_is_attachment (TnyList *mime_parts)
+{
+       TnyIterator *iter;
+       gboolean retval = FALSE;
+
+       if (tny_list_get_length (mime_parts) > 1)
+               return FALSE;
+
+       iter = tny_list_create_iterator (mime_parts);
+       if (iter) {
+               TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
+               if (part) {
+                       if (TNY_IS_MSG (part))
+                               retval = TRUE;
+                       g_object_unref (part);
+               }
+               g_object_unref (iter);
+       }
+       return retval;
+}
+
 void
 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, 
                                         TnyList *mime_parts)
@@ -3222,13 +3458,34 @@ modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
 
        if (mime_parts == NULL) {
+               gboolean allow_msgs = FALSE;
+
                /* In Hildon 2.2 save and delete operate over all the attachments as there's no
                 * selection available */
                mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
-               if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
+
+               /* Check if the message is composed by an unique MIME
+                  part whose content disposition is attachment. There
+                  could be messages like this:
+
+                  Date: Tue, 12 Jan 2010 20:40:59 +0000
+                  From: <sender@example.org>
+                  To: <recipient@example.org>
+                  Subject: no body
+                  Content-Type: image/jpeg
+                  Content-Disposition: attachment; filename="bug7718.jpeg"
+
+                  whose unique MIME part is the message itself whose
+                  content disposition is attachment
+               */
+               if (mime_parts && msg_is_attachment (mime_parts))
+                       allow_msgs = TRUE;
+
+               if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))), mime_parts, allow_msgs)) {
                        g_object_unref (mime_parts);
                        return;
                }
+
                if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
                        if (mime_parts) {
                                g_object_unref (mime_parts);
@@ -3307,7 +3564,7 @@ modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
        /* if multiple, set multiple string */
        if (save_multiple_str) {
                g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
-               gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
+               gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
                g_free (save_multiple_str);
        }
 
@@ -3360,6 +3617,7 @@ modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean
        g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
        priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
 
+#ifdef MODEST_TOOLKIT_HILDON2
        /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
         * because we don't have selection
         */
@@ -3385,6 +3643,31 @@ modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean
                g_object_unref (mime_parts);
                return;
        }
+#else
+       /* In gtk we get only selected attachments for the operation.
+        */
+       mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
+
+       /* Remove already purged messages from mime parts list. We use
+          a copy of the list to remove items in the original one */
+       tmp = tny_list_copy (mime_parts);
+       iter = tny_list_create_iterator (tmp);
+       while (!tny_iterator_is_done (iter)) {
+               TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
+               if (tny_mime_part_is_purged (part))
+                       tny_list_remove (mime_parts, (GObject *) part);
+
+               g_object_unref (part);
+               tny_iterator_next (iter);
+       }
+       g_object_unref (tmp);
+       g_object_unref (iter);
+
+       if (tny_list_get_length (mime_parts) == 0) {
+               g_object_unref (mime_parts);
+               return;
+       }
+#endif
 
        n_attachments = tny_list_get_length (mime_parts);
        if (n_attachments == 1) {
@@ -3412,7 +3695,7 @@ modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean
                                                                 "mcen_nc_purge_files_text", 
                                                                 n_attachments), n_attachments);
        }
-       response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
+       response = modest_platform_run_confirmation_dialog (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))),
                                                            confirmation_message);
        g_free (confirmation_message);
 
@@ -3544,6 +3827,23 @@ typedef struct {
 } FetchImageData;
 
 gboolean
+on_fetch_image_timeout_refresh_view (gpointer userdata)
+{
+       ModestMsgViewWindowPrivate *priv;
+
+       priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
+       update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
+       /* Note that priv->msg_view is set to NULL when this window is
+          distroyed */
+       if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
+               gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
+       }
+       priv->fetch_image_redraw_handler = 0;
+       g_object_unref (userdata);
+       return FALSE;
+}
+
+gboolean
 on_fetch_image_idle_refresh_view (gpointer userdata)
 {
 
@@ -3555,8 +3855,10 @@ on_fetch_image_idle_refresh_view (gpointer userdata)
 
                priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
                priv->fetching_images--;
-               gtk_widget_queue_draw (fidata->msg_view);
-               update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
+               if (priv->fetch_image_redraw_handler == 0) {
+                       priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
+               }
+
        }
        gdk_threads_leave ();
 
@@ -3661,9 +3963,9 @@ setup_menu (ModestMsgViewWindow *self)
        g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
 
        /* Settings menu buttons */
-       modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
-                                  MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
-                                  MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
+       modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
+                                  MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
+                                  MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
 
        modest_window_add_to_menu (MODEST_WINDOW (self),
                                   dngettext(GETTEXT_PACKAGE,
@@ -3677,7 +3979,6 @@ setup_menu (ModestMsgViewWindow *self)
        modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
                                   MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
                                   MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
-
        modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
                                   MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
                                   MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
@@ -3689,9 +3990,6 @@ setup_menu (ModestMsgViewWindow *self)
                                   MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
                                   MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
 
-       modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
-                                  MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
-                                  MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
        modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
                                   MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
                                   MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
@@ -3699,6 +3997,10 @@ setup_menu (ModestMsgViewWindow *self)
        modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
                                   MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
                                   MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
+
+       modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
+                                  MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
+                                  MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
 }
 
 void
@@ -3862,3 +4164,47 @@ sync_flags (ModestMsgViewWindow *self)
                g_object_unref (header);
        }
 }
+
+#ifdef MODEST_TOOLKIT_HILDON2
+static gboolean
+on_realize (GtkWidget *widget,
+           gpointer userdata)
+{
+       GdkDisplay *display;
+       Atom atom;
+       unsigned long val = 1;
+
+       display = gdk_drawable_get_display (widget->window);
+       atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
+       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+                        GDK_WINDOW_XID (widget->window), atom,
+                        XA_INTEGER, 32, PropModeReplace,
+                        (unsigned char *) &val, 1);
+
+       return FALSE;
+}
+#endif
+
+static gboolean
+on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part, GtkContainer *container, ModestMsgViewWindow *self)
+{
+       const gchar *account_name;
+       ModestProtocolType proto_type;
+       ModestProtocol *protocol;
+       gboolean retval = FALSE;
+
+       account_name = modest_window_get_active_account (MODEST_WINDOW (self));
+       
+       /* Get proto */
+       proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (), 
+                                                           account_name);
+       protocol = 
+               modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (), 
+                                                              proto_type);
+
+       if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
+               retval = modest_account_protocol_handle_calendar (MODEST_ACCOUNT_PROTOCOL (protocol), MODEST_WINDOW (self),
+                                                                 calendar_part, container);
+       }
+       return retval;
+}