If, on retrieving bs message, body is not available, mark the message as
[modest] / src / widgets / modest-msg-view-window.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 #include <glib/gi18n.h>
30 #include <string.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
33 #include <tny-msg.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-toolkit-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include "modest-defs.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
62 #include <math.h>
63 #include <errno.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
66 #include <modest-header-window.h>
67 #include <modest-account-protocol.h>
68 #include <modest-icon-names.h>
69 #include <modest-ui-actions.h>
70 #include <modest-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <modest-icon-names.h>
73
74 #ifdef MODEST_PLATFORM_MAEMO
75 #include <modest-maemo-utils.h>
76 #endif
77
78 #ifdef MODEST_TOOLKIT_HILDON2
79 #include <hildon/hildon.h>
80 #endif
81 #include <tny-camel-bs-mime-part.h>
82 #include <tny-camel-bs-msg.h>
83
84 #define MYDOCS_ENV "MYDOCSDIR"
85 #define DOCS_FOLDER ".documents"
86
87 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
88 struct _ModestMsgViewWindowPrivate {
89
90         GtkWidget   *msg_view;
91         GtkWidget   *main_scroll;
92         GtkWidget   *isearch_toolbar;
93         gchar       *last_search;
94
95         /* Progress observers */
96         GSList           *progress_widgets;
97
98         /* Tollbar items */
99         GtkWidget   *prev_toolitem;
100         GtkWidget   *next_toolitem;
101         gboolean    progress_hint;
102         gint        fetching_images;
103
104         /* Optimized view enabled */
105         gboolean optimized_view;
106
107         /* Whether this was created via the *_new_for_search_result() function. */
108         gboolean is_search_result;
109
110         /* Whether the message is in outbox */
111         gboolean is_outbox;
112
113         /* A reference to the @model of the header view 
114          * to allow selecting previous/next messages,
115          * if the message is currently selected in the header view.
116          */
117         const gchar *header_folder_id;
118         GtkTreeModel *header_model;
119         GtkTreeRowReference *row_reference;
120         GtkTreeRowReference *next_row_reference;
121
122         gulong clipboard_change_handler;
123         gulong queue_change_handler;
124         gulong account_removed_handler;
125         gulong row_changed_handler;
126         gulong row_deleted_handler;
127         gulong row_inserted_handler;
128         gulong rows_reordered_handler;
129
130         guint purge_timeout;
131         GtkWidget *remove_attachment_banner;
132
133         gchar *msg_uid;
134         TnyMimePart *other_body;
135
136         GSList *sighandlers;
137 };
138
139 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
140 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
141 static void  modest_header_view_observer_init    (ModestHeaderViewObserverIface *iface_class);
142 static void  modest_msg_view_window_finalize     (GObject *obj);
143 static void  modest_msg_view_window_show_isearch_toolbar   (GtkWidget *obj, gpointer data);
144 static void  modest_msg_view_window_isearch_toolbar_close  (GtkWidget *widget,
145                                                             ModestMsgViewWindow *obj);
146 static void  modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
147                                                             ModestMsgViewWindow *obj);
148 static void  modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
149                                                             gpointer data);
150 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
151
152 static gdouble modest_msg_view_window_get_zoom    (ModestWindow *window);
153 static void modest_msg_view_window_set_zoom       (ModestWindow *window,
154                                                    gdouble zoom);
155 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
156 static gboolean modest_msg_view_window_zoom_plus  (ModestWindow *window);
157 static gboolean modest_msg_view_window_key_event  (GtkWidget *window,
158                                                    GdkEventKey *event,
159                                                    gpointer userdata);
160 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
161
162 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
163                                                    gboolean show_toolbar);
164
165 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
166                                                            GdkEvent *event,
167                                                            ModestMsgViewWindow *window);
168
169 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
170                                                    GtkTreePath *arg1,
171                                                    GtkTreeIter *arg2,
172                                                    ModestMsgViewWindow *window);
173
174 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
175                                                    GtkTreePath *arg1,
176                                                    ModestMsgViewWindow *window);
177
178 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
179                                                     GtkTreePath *tree_path,
180                                                     GtkTreeIter *tree_iter,
181                                                     ModestMsgViewWindow *window);
182
183 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
184                                                      GtkTreePath *arg1,
185                                                      GtkTreeIter *arg2,
186                                                      gpointer arg3,
187                                                      ModestMsgViewWindow *window);
188
189 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
190                                                           GtkTreeModel *model,
191                                                           const gchar *tny_folder_id);
192
193 static void on_queue_changed    (ModestMailOperationQueue *queue,
194                                  ModestMailOperation *mail_op,
195                                  ModestMailOperationQueueNotification type,
196                                  ModestMsgViewWindow *self);
197
198 static void on_account_removed  (TnyAccountStore *account_store, 
199                                  TnyAccount *account,
200                                  gpointer user_data);
201
202 static void on_move_focus (GtkWidget *widget,
203                            GtkDirectionType direction,
204                            gpointer userdata);
205
206 static void view_msg_cb         (ModestMailOperation *mail_op, 
207                                  TnyHeader *header, 
208                                  gboolean canceled,
209                                  TnyMsg *msg, 
210                                  GError *error,
211                                  gpointer user_data);
212
213 static void set_progress_hint    (ModestMsgViewWindow *self, 
214                                   gboolean enabled);
215
216 static void update_window_title (ModestMsgViewWindow *window);
217
218 static void init_window (ModestMsgViewWindow *obj);
219
220 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
221
222 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
223
224 static gboolean on_fetch_image (ModestMsgView *msgview,
225                                 const gchar *uri,
226                                 TnyStream *stream,
227                                 ModestMsgViewWindow *window);
228
229 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
230                                                      GtkScrollType scroll_type,
231                                                      gboolean horizontal,
232                                                      gpointer userdata);
233 static gboolean message_reader (ModestMsgViewWindow *window,
234                                 ModestMsgViewWindowPrivate *priv,
235                                 TnyHeader *header,
236                                 const gchar *msg_uid,
237                                 TnyFolder *folder,
238                                 GtkTreeRowReference *row_reference);
239
240 static void setup_menu (ModestMsgViewWindow *self);
241 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
242                                                    GdkEvent *event,
243                                                    gpointer userdata);
244 static void update_branding (ModestMsgViewWindow *self);
245 static void sync_flags      (ModestMsgViewWindow *self);
246
247 /* list my signals */
248 enum {
249         MSG_CHANGED_SIGNAL,
250         SCROLL_CHILD_SIGNAL,
251         LAST_SIGNAL
252 };
253
254 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
255
256         /* Toolbar items */
257         { "ToolbarMessageReply",      MODEST_STOCK_REPLY,     N_("mcen_me_inbox_reply"),      "<CTRL>R", NULL,  G_CALLBACK (modest_ui_actions_on_reply) },
258         { "ToolbarMessageReplyAll",   MODEST_STOCK_REPLY_ALL,     N_("mcen_me_inbox_replytoall"),         NULL, NULL,  G_CALLBACK (modest_ui_actions_on_reply_all) },
259         { "ToolbarMessageForward",    MODEST_STOCK_FORWARD,     N_("mcen_me_inbox_forward"),      NULL, NULL,  G_CALLBACK (modest_ui_actions_on_forward) },
260         { "ToolbarDeleteMessage",     MODEST_STOCK_DELETE,     N_("qgn_toolb_gene_deletebutton"),             NULL, NULL,  G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
261         { "ToolbarMessageBack",       MODEST_TOOLBAR_ICON_PREV,    N_("qgn_toolb_gene_back"),         NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
262         { "ToolbarMessageNext",    MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"),      NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
263         { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"),      NULL, NULL,  G_CALLBACK (modest_ui_actions_on_fetch_images) },
264 };
265
266 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
267         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
268 };
269
270 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
271                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
272                                                     ModestMsgViewWindowPrivate))
273 /* globals */
274 static GtkWindowClass *parent_class = NULL;
275
276 /* uncomment the following if you have defined any signals */
277 static guint signals[LAST_SIGNAL] = {0};
278
279 GType
280 modest_msg_view_window_get_type (void)
281 {
282         static GType my_type = 0;
283         if (!my_type) {
284                 static const GTypeInfo my_info = {
285                         sizeof(ModestMsgViewWindowClass),
286                         NULL,           /* base init */
287                         NULL,           /* base finalize */
288                         (GClassInitFunc) modest_msg_view_window_class_init,
289                         NULL,           /* class finalize */
290                         NULL,           /* class data */
291                         sizeof(ModestMsgViewWindow),
292                         1,              /* n_preallocs */
293                         (GInstanceInitFunc) modest_msg_view_window_init,
294                         NULL
295                 };
296 #ifndef MODEST_TOOLKIT_HILDON2
297                 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
298                                                   "ModestMsgViewWindow",
299                                                   &my_info, 0);
300 #else
301                 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
302                                                   "ModestMsgViewWindow",
303                                                   &my_info, 0);
304 #endif
305
306                 static const GInterfaceInfo modest_header_view_observer_info = 
307                 {
308                         (GInterfaceInitFunc) modest_header_view_observer_init,
309                         NULL,         /* interface_finalize */
310                         NULL          /* interface_data */
311                 };
312
313                 g_type_add_interface_static (my_type,
314                                 MODEST_TYPE_HEADER_VIEW_OBSERVER,
315                                 &modest_header_view_observer_info);
316         }
317         return my_type;
318 }
319
320 static void
321 save_state (ModestWindow *self)
322 {
323         modest_widget_memory_save (modest_runtime_get_conf (),
324                                    G_OBJECT(self),
325                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
326 }
327
328 static gboolean
329 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
330                                      GtkScrollType scroll_type,
331                                      gboolean horizontal,
332                                      gpointer userdata)
333 {
334         ModestMsgViewWindowPrivate *priv;
335         gint step = 0;
336
337         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
338
339         switch (scroll_type) {
340         case GTK_SCROLL_STEP_UP:
341                 step = -1;
342                 break;
343         case GTK_SCROLL_STEP_DOWN:
344                 step = +1;
345                 break;
346         case GTK_SCROLL_PAGE_UP:
347                 step = -6;
348                 break;
349         case GTK_SCROLL_PAGE_DOWN:
350                 step = +6;
351                 break;
352         case GTK_SCROLL_START:
353                 step = -100;
354                 break;
355         case GTK_SCROLL_END:
356                 step = +100;
357                 break;
358         default:
359                 step = 0;
360         }
361
362         if (step)
363                 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
364
365         return (gboolean) step;
366 }
367
368 static void
369 add_scroll_binding (GtkBindingSet *binding_set,
370                     guint keyval,
371                     GtkScrollType scroll)
372 {
373         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
374
375         gtk_binding_entry_add_signal (binding_set, keyval, 0,
376                                       "scroll_child", 2,
377                                       GTK_TYPE_SCROLL_TYPE, scroll,
378                                       G_TYPE_BOOLEAN, FALSE);
379         gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
380                                       "scroll_child", 2,
381                                       GTK_TYPE_SCROLL_TYPE, scroll,
382                                       G_TYPE_BOOLEAN, FALSE);
383 }
384
385 static void
386 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
387 {
388         GObjectClass *gobject_class;
389         ModestWindowClass *modest_window_class;
390         GtkBindingSet *binding_set;
391
392         gobject_class = (GObjectClass*) klass;
393         modest_window_class = (ModestWindowClass *) klass;
394
395         parent_class            = g_type_class_peek_parent (klass);
396         gobject_class->finalize = modest_msg_view_window_finalize;
397
398         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
399         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
400         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
401         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
402         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
403         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
404
405         modest_window_class->save_state_func = save_state;
406
407         klass->scroll_child = modest_msg_view_window_scroll_child;
408
409         signals[MSG_CHANGED_SIGNAL] =
410                 g_signal_new ("msg-changed",
411                               G_TYPE_FROM_CLASS (gobject_class),
412                               G_SIGNAL_RUN_FIRST,
413                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
414                               NULL, NULL,
415                               modest_marshal_VOID__POINTER_POINTER,
416                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
417
418         signals[SCROLL_CHILD_SIGNAL] =
419                 g_signal_new ("scroll-child",
420                               G_TYPE_FROM_CLASS (gobject_class),
421                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
422                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
423                               NULL, NULL,
424                               modest_marshal_BOOLEAN__ENUM_BOOLEAN,
425                               G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
426
427         binding_set = gtk_binding_set_by_class (klass);
428         add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
429         add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
430         add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
431         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
432         add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
433         add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
434
435         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
436
437 }
438
439 static void modest_header_view_observer_init(
440                 ModestHeaderViewObserverIface *iface_class)
441 {
442         iface_class->update_func = modest_msg_view_window_update_model_replaced;
443 }
444
445 static void
446 modest_msg_view_window_init (ModestMsgViewWindow *obj)
447 {
448         ModestMsgViewWindowPrivate *priv;
449         ModestWindowPrivate *parent_priv = NULL;
450         GtkActionGroup *action_group = NULL;
451         GError *error = NULL;
452
453         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
454         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
455         parent_priv->ui_manager = gtk_ui_manager_new();
456
457         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
458         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
459
460         /* Add common actions */
461         gtk_action_group_add_actions (action_group,
462                                       msg_view_toolbar_action_entries,
463                                       G_N_ELEMENTS (msg_view_toolbar_action_entries),
464                                       obj);
465         gtk_action_group_add_toggle_actions (action_group,
466                                              msg_view_toggle_action_entries,
467                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
468                                              obj);
469
470         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
471         g_object_unref (action_group);
472
473         /* Load the UI definition */
474         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
475                                          &error);
476         if (error) {
477                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
478                 g_error_free (error);
479                 error = NULL;
480         }
481         
482         priv->is_search_result = FALSE;
483         priv->is_outbox = FALSE;
484
485         priv->msg_view      = NULL;
486         priv->header_model  = NULL;
487         priv->header_folder_id  = NULL;
488         priv->clipboard_change_handler = 0;
489         priv->queue_change_handler = 0;
490         priv->account_removed_handler = 0;
491         priv->row_changed_handler = 0;
492         priv->row_deleted_handler = 0;
493         priv->row_inserted_handler = 0;
494         priv->rows_reordered_handler = 0;
495         priv->progress_hint = FALSE;
496         priv->fetching_images = 0;
497
498         priv->optimized_view  = FALSE;
499         priv->purge_timeout = 0;
500         priv->remove_attachment_banner = NULL;
501         priv->msg_uid = NULL;
502         priv->other_body = NULL;
503         
504         priv->sighandlers = NULL;
505         
506         /* Init window */
507         init_window (MODEST_MSG_VIEW_WINDOW(obj));
508
509 }
510
511 static void
512 update_progress_hint (ModestMsgViewWindow *self)
513 {
514         ModestMsgViewWindowPrivate *priv;
515         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
516
517         if (GTK_WIDGET_VISIBLE (self)) {
518                 modest_window_show_progress (MODEST_WINDOW (self),
519                                              (priv->progress_hint || (priv->fetching_images > 0))?1:0);
520         }
521 }
522
523 static void 
524 set_progress_hint (ModestMsgViewWindow *self, 
525                    gboolean enabled)
526 {
527         ModestWindowPrivate *parent_priv;
528         ModestMsgViewWindowPrivate *priv;
529
530         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
531
532         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
533         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
534                         
535         /* Sets current progress hint */
536         priv->progress_hint = enabled;
537
538         update_progress_hint (self);
539
540 }
541
542
543 static void
544 init_window (ModestMsgViewWindow *obj)
545 {
546         GtkWidget *main_vbox;
547         ModestMsgViewWindowPrivate *priv;
548
549         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
550
551         priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
552         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
553         main_vbox = gtk_vbox_new  (FALSE, 6);
554
555         priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
556         modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
557         g_object_set (G_OBJECT (priv->main_scroll),
558                       "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
559                       "horizontal-max-overshoot", 0,
560                       NULL);
561         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
562         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
563         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
564
565         /* NULL-ize fields if the window is destroyed */
566         g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
567
568         gtk_widget_show_all (GTK_WIDGET(main_vbox));
569 }
570
571 static void
572 modest_msg_view_window_disconnect_signals (ModestWindow *self)
573 {
574         ModestMsgViewWindowPrivate *priv;
575         GtkWidget *header_view = NULL;
576         GtkWindow *parent_window = NULL;
577         
578         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
579
580         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
581             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582                                            priv->clipboard_change_handler)) 
583                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
584                                              priv->clipboard_change_handler);
585
586         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
587                                            priv->queue_change_handler))
588                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
589                                              priv->queue_change_handler);
590
591         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
592                                            priv->account_removed_handler))
593                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
594                                              priv->account_removed_handler);
595
596         if (priv->header_model) {
597                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
598                                                   priv->row_changed_handler))
599                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
600                                                     priv->row_changed_handler);
601                 
602                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
603                                                   priv->row_deleted_handler))
604                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
605                                              priv->row_deleted_handler);
606                 
607                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
608                                                   priv->row_inserted_handler))
609                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
610                                                     priv->row_inserted_handler);
611                 
612                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
613                                                   priv->rows_reordered_handler))
614                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
615                                                     priv->rows_reordered_handler);
616         }
617
618         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
619         priv->sighandlers = NULL;
620
621         parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
622         if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
623                 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
624                 if (header_view) {
625                         modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
626                                                            MODEST_HEADER_VIEW_OBSERVER(self));
627                 }
628         }
629 }
630
631 static void
632 modest_msg_view_window_finalize (GObject *obj)
633 {
634         ModestMsgViewWindowPrivate *priv;
635
636         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
637
638         /* Sanity check: shouldn't be needed, the window mgr should
639            call this function before */
640         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
641
642         if (priv->other_body != NULL) {
643                 g_object_unref (priv->other_body);
644                 priv->other_body = NULL;
645         }
646
647         if (priv->header_model != NULL) {
648                 g_object_unref (priv->header_model);
649                 priv->header_model = NULL;
650         }
651
652         if (priv->remove_attachment_banner) {
653                 gtk_widget_destroy (priv->remove_attachment_banner);
654                 g_object_unref (priv->remove_attachment_banner);
655                 priv->remove_attachment_banner = NULL;
656         }
657
658         if (priv->purge_timeout > 0) {
659                 g_source_remove (priv->purge_timeout);
660                 priv->purge_timeout = 0;
661         }
662
663         if (priv->row_reference) {
664                 gtk_tree_row_reference_free (priv->row_reference);
665                 priv->row_reference = NULL;
666         }
667
668         if (priv->next_row_reference) {
669                 gtk_tree_row_reference_free (priv->next_row_reference);
670                 priv->next_row_reference = NULL;
671         }
672
673         if (priv->msg_uid) {
674                 g_free (priv->msg_uid);
675                 priv->msg_uid = NULL;
676         }
677
678         G_OBJECT_CLASS(parent_class)->finalize (obj);
679 }
680
681 static gboolean
682 select_next_valid_row (GtkTreeModel *model,
683                        GtkTreeRowReference **row_reference,
684                        gboolean cycle,
685                        gboolean is_outbox)
686 {
687         GtkTreeIter tmp_iter;
688         GtkTreePath *path;
689         GtkTreePath *next = NULL;
690         gboolean retval = FALSE, finished;
691
692         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
693
694         path = gtk_tree_row_reference_get_path (*row_reference);
695         gtk_tree_model_get_iter (model, &tmp_iter, path);
696         gtk_tree_row_reference_free (*row_reference);
697         *row_reference = NULL;
698
699         finished = FALSE;
700         do {
701                 TnyHeader *header = NULL;
702
703                 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
704                         gtk_tree_model_get (model, &tmp_iter, 
705                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
706                                             &header, -1);
707
708                         if (header) {
709                                 if (msg_is_visible (header, is_outbox)) {
710                                         next = gtk_tree_model_get_path (model, &tmp_iter);
711                                         *row_reference = gtk_tree_row_reference_new (model, next);
712                                         gtk_tree_path_free (next);
713                                         retval = TRUE;
714                                         finished = TRUE;
715                                 }
716                                 g_object_unref (header);
717                                 header = NULL;
718                         }
719                 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
720                         next = gtk_tree_model_get_path (model, &tmp_iter);
721                         
722                         /* Ensure that we are not selecting the same */
723                         if (gtk_tree_path_compare (path, next) != 0) {
724                                 gtk_tree_model_get (model, &tmp_iter, 
725                                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
726                                                     &header, -1);                               
727                                 if (header) {
728                                         if (msg_is_visible (header, is_outbox)) {
729                                                 *row_reference = gtk_tree_row_reference_new (model, next);
730                                                 retval = TRUE;
731                                                 finished = TRUE;
732                                         }
733                                         g_object_unref (header);
734                                         header = NULL;
735                                 }
736                         } else {
737                                 /* If we ended up in the same message
738                                    then there is no valid next
739                                    message */
740                                 finished = TRUE;
741                         }
742                         gtk_tree_path_free (next);
743                 } else {
744                         /* If there are no more messages and we don't
745                            want to start again in the first one then
746                            there is no valid next message */
747                         finished = TRUE;
748                 }
749         } while (!finished);
750
751         /* Free */
752         gtk_tree_path_free (path);
753
754         return retval;
755 }
756
757 /* TODO: This should be in _init(), with the parameters as properties. */
758 static void
759 modest_msg_view_window_construct (ModestMsgViewWindow *self, 
760                                   const gchar *modest_account_name,
761                                   const gchar *mailbox,
762                                   const gchar *msg_uid)
763 {
764         GObject *obj = NULL;
765         ModestMsgViewWindowPrivate *priv = NULL;
766         ModestWindowPrivate *parent_priv = NULL;
767         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
768         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
769
770         obj = G_OBJECT (self);
771         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
772         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
773
774         priv->msg_uid = g_strdup (msg_uid);
775
776         /* Menubar */
777         parent_priv->menubar = NULL;
778
779         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
780         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
781
782         setup_menu (self);
783         /* Add common dimming rules */
784         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
785                                               modest_msg_view_toolbar_dimming_entries,
786                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
787                                               MODEST_WINDOW (self));
788         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
789                                               modest_msg_view_clipboard_dimming_entries,
790                                               G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
791                                               MODEST_WINDOW (self));
792
793         /* Insert dimming rules group for this window */
794         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
795         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
796         g_object_unref (toolbar_rules_group);
797         g_object_unref (clipboard_rules_group);
798
799         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
800
801         priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
802         g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
803                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
804         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
805                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
806         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
807                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
808         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
809                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
810         g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
811                           G_CALLBACK (modest_ui_actions_on_details), obj);
812         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
813                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
814         g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
815                           G_CALLBACK (modest_ui_actions_on_limit_error), obj);
816         g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
817                           G_CALLBACK (on_fetch_image), obj);
818
819         g_signal_connect (G_OBJECT (obj), "key-release-event",
820                           G_CALLBACK (modest_msg_view_window_key_event),
821                           NULL);
822
823         g_signal_connect (G_OBJECT (obj), "key-press-event",
824                           G_CALLBACK (modest_msg_view_window_key_event),
825                           NULL);
826
827         g_signal_connect (G_OBJECT (obj), "move-focus",
828                           G_CALLBACK (on_move_focus), obj);
829
830         g_signal_connect (G_OBJECT (obj), "map-event",
831                           G_CALLBACK (_modest_msg_view_window_map_event),
832                           G_OBJECT (obj));
833
834         /* Mail Operation Queue */
835         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
836                                                        "queue-changed",
837                                                        G_CALLBACK (on_queue_changed),
838                                                        obj);
839
840         /* Account manager */
841         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
842                                                           "account_removed",
843                                                           G_CALLBACK(on_account_removed),
844                                                           obj);
845
846         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
847         modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
848
849         /* First add out toolbar ... */
850         modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
851
852         priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
853                                                                                NULL);
854         modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
855         gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
856         g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close", 
857                           G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
858         g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search", 
859                           G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
860         priv->last_search = NULL;
861
862         /* Init the clipboard actions dim status */
863         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
864
865         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
866
867
868 }
869
870 /* FIXME: parameter checks */
871 ModestWindow *
872 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
873                                               const gchar *modest_account_name,
874                                               const gchar *mailbox,
875                                               const gchar *msg_uid,
876                                               GtkTreeModel *model, 
877                                               GtkTreeRowReference *row_reference)
878 {
879         ModestMsgViewWindow *window = NULL;
880         ModestMsgViewWindowPrivate *priv = NULL;
881         TnyFolder *header_folder = NULL;
882         ModestHeaderView *header_view = NULL;
883         ModestWindowMgr *mgr = NULL;
884
885         MODEST_DEBUG_BLOCK (
886                modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
887         );
888
889         mgr = modest_runtime_get_window_mgr ();
890         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
891         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
892
893         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
894
895         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
896
897         /* Remember the message list's TreeModel so we can detect changes
898          * and change the list selection when necessary: */
899         header_folder = modest_header_view_get_folder (header_view);
900         if (header_folder) {
901                 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
902                                    TNY_FOLDER_TYPE_OUTBOX);
903                 priv->header_folder_id = tny_folder_get_id (header_folder);
904                 g_object_unref(header_folder);
905         }
906
907         /* Setup row references and connect signals */
908         priv->header_model = g_object_ref (model);
909
910         if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
911                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
912                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
913                 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
914         } else {
915                 priv->row_reference = NULL;
916                 priv->next_row_reference = NULL;
917         }
918
919         /* Connect signals */
920         priv->row_changed_handler = 
921                 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
922                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
923                                   window);
924         priv->row_deleted_handler = 
925                 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
926                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
927                                   window);
928         priv->row_inserted_handler = 
929                 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
930                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
931                                   window);
932         priv->rows_reordered_handler = 
933                 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
934                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
935                                  window);
936
937         if (header_view != NULL){
938                 modest_header_view_add_observer(header_view,
939                                 MODEST_HEADER_VIEW_OBSERVER(window));
940         }
941
942         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
943         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
944         update_branding (MODEST_MSG_VIEW_WINDOW (window));
945
946         /* gtk_widget_show_all (GTK_WIDGET (window)); */
947         modest_msg_view_window_update_priority (window);
948         /* Check dimming rules */
949         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
950         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
951         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
952
953         return MODEST_WINDOW(window);
954 }
955
956 ModestWindow *
957 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
958                                      const gchar *mailbox,
959                                      const gchar *msg_uid)
960 {
961         ModestMsgViewWindow *window = NULL;
962         ModestMsgViewWindowPrivate *priv = NULL;
963         ModestWindowMgr *mgr = NULL;
964         gboolean is_merge;
965         TnyAccount *account = NULL;
966
967         mgr = modest_runtime_get_window_mgr ();
968         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
969         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
970
971         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
972
973         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
974
975         is_merge = g_str_has_prefix (msg_uid, "merge:");
976
977         /* Get the account */
978         if (!is_merge)
979                 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
980                                                           msg_uid);
981
982         if (is_merge || account) {
983                 TnyFolder *folder = NULL;
984
985                 /* Try to get the message, if it's already downloaded
986                    we don't need to connect */
987                 if (account) {
988                         folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
989                 } else {
990                         ModestTnyAccountStore *account_store;
991                         ModestTnyLocalFoldersAccount *local_folders_account;
992
993                         account_store = modest_runtime_get_account_store ();
994                         local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
995                                 modest_tny_account_store_get_local_folders_account (account_store));
996                         folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
997                         g_object_unref (local_folders_account);
998                 }
999                 if (folder) {
1000                         TnyDevice *device;
1001                         gboolean device_online;
1002
1003                         device = modest_runtime_get_device();
1004                         device_online = tny_device_is_online (device);
1005                         if (device_online) {
1006                                 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1007                         } else {
1008                                 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1009                                 if (msg) {
1010                                         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1011                                         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1012                                         update_branding (MODEST_MSG_VIEW_WINDOW (window));
1013                                         g_object_unref (msg);
1014                                         /* Sync flags to server */
1015                                         sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1016                                 } else {
1017                                         message_reader (window, priv, NULL, msg_uid, folder, NULL);
1018                                 }
1019                         }
1020                         g_object_unref (folder);
1021                 }
1022
1023         }
1024
1025         /* Check dimming rules */
1026         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1027         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1028         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1029
1030         return MODEST_WINDOW(window);
1031 }
1032
1033 ModestWindow *
1034 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1035                                              const gchar *modest_account_name,
1036                                              const gchar *mailbox,
1037                                              const gchar *msg_uid,
1038                                              GtkTreeRowReference *row_reference)
1039 {
1040         ModestMsgViewWindow *window = NULL;
1041         ModestMsgViewWindowPrivate *priv = NULL;
1042         TnyFolder *header_folder = NULL;
1043         ModestWindowMgr *mgr = NULL;
1044         GtkTreePath *path;
1045         GtkTreeIter iter;
1046
1047         mgr = modest_runtime_get_window_mgr ();
1048         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1049         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1050
1051         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1052
1053         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1054
1055         /* Remember the message list's TreeModel so we can detect changes 
1056          * and change the list selection when necessary: */
1057
1058         if (header_view != NULL){
1059                 header_folder = modest_header_view_get_folder(header_view);
1060                 /* This could happen if the header folder was
1061                    unseleted before opening this msg window (for
1062                    example if the user selects an account in the
1063                    folder view of the main window */
1064                 if (header_folder) {
1065                         priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == 
1066                                            TNY_FOLDER_TYPE_OUTBOX);
1067                         priv->header_folder_id = tny_folder_get_id(header_folder);
1068                         g_object_unref(header_folder);
1069                 }
1070         }
1071
1072         /* Setup row references and connect signals */
1073         priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1074         g_object_ref (priv->header_model);
1075
1076         if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1077                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1078                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1079                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1080         } else {
1081                 priv->row_reference = NULL;
1082                 priv->next_row_reference = NULL;
1083         }
1084
1085         /* Connect signals */
1086         priv->row_changed_handler = 
1087                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1088                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
1089                                   window);
1090         priv->row_deleted_handler = 
1091                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1092                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
1093                                   window);
1094         priv->row_inserted_handler = 
1095                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1096                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1097                                   window);
1098         priv->rows_reordered_handler = 
1099                 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1100                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
1101                                  window);
1102
1103         if (header_view != NULL){
1104                 modest_header_view_add_observer(header_view,
1105                                                 MODEST_HEADER_VIEW_OBSERVER(window));
1106         }
1107
1108         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1109         update_branding (MODEST_MSG_VIEW_WINDOW (window));
1110
1111         if (priv->row_reference) {
1112                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1113                 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1114                         TnyHeader *header;
1115                         gtk_tree_model_get (priv->header_model, &iter, 
1116                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1117                                             &header, -1);
1118                         message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1119                         g_object_unref (header);
1120                 }
1121                 gtk_tree_path_free (path);
1122         }
1123         /* Check dimming rules */
1124         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1125         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1126         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1127
1128         return MODEST_WINDOW(window);
1129 }
1130
1131 ModestWindow *
1132 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1133                                               const gchar *modest_account_name,
1134                                               const gchar *mailbox,
1135                                               const gchar *msg_uid)
1136 {
1137         ModestMsgViewWindow *window = NULL;
1138         ModestMsgViewWindowPrivate *priv = NULL;
1139         ModestWindowMgr *mgr = NULL;
1140
1141         mgr = modest_runtime_get_window_mgr ();
1142         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1143         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1144         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1145
1146         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1147
1148         /* Remember that this is a search result, 
1149          * so we can disable some UI appropriately: */
1150         priv->is_search_result = TRUE;
1151
1152         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1153         update_branding (MODEST_MSG_VIEW_WINDOW (window));
1154         
1155         update_window_title (window);
1156         /* gtk_widget_show_all (GTK_WIDGET (window));*/
1157         modest_msg_view_window_update_priority (window);
1158
1159         /* Check dimming rules */
1160         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1161         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1162         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1163
1164         return MODEST_WINDOW(window);
1165 }
1166
1167 gboolean
1168 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1169 {
1170         ModestMsgViewWindowPrivate *priv = NULL;
1171
1172         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1173         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1174
1175         return (priv->other_body != NULL);
1176 }
1177
1178 ModestWindow *
1179 modest_msg_view_window_new_with_other_body (TnyMsg *msg, 
1180                                             TnyMimePart *other_body,
1181                                             const gchar *modest_account_name,
1182                                             const gchar *mailbox,
1183                                             const gchar *msg_uid)
1184 {
1185         GObject *obj = NULL;
1186         ModestMsgViewWindowPrivate *priv;       
1187         ModestWindowMgr *mgr = NULL;
1188
1189         g_return_val_if_fail (msg, NULL);
1190         mgr = modest_runtime_get_window_mgr ();
1191         obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1192         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1193         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
1194                                           modest_account_name, mailbox, msg_uid);
1195
1196         if (other_body) {
1197                 priv->other_body = g_object_ref (other_body);
1198                 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1199         } else {
1200                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1201         }
1202         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1203         update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1204
1205         /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1206
1207         /* Check dimming rules */
1208         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1209         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1210         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1211
1212         return MODEST_WINDOW(obj);
1213 }
1214
1215 ModestWindow *
1216 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
1217                                            const gchar *modest_account_name,
1218                                            const gchar *mailbox,
1219                                            const gchar *msg_uid)
1220 {
1221         return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1222 }
1223
1224 static void
1225 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1226                                        GtkTreePath *arg1,
1227                                        GtkTreeIter *arg2,
1228                                        ModestMsgViewWindow *window)
1229 {
1230         check_dimming_rules_after_change (window);
1231 }
1232
1233 static void 
1234 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1235                                       GtkTreePath *arg1,
1236                                       ModestMsgViewWindow *window)
1237 {
1238         check_dimming_rules_after_change (window);
1239 }
1240         /* The window could have dissapeared */
1241
1242 static void
1243 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1244 {
1245         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1246         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1247 }
1248
1249
1250 /* On insertions we check if the folder still has the message we are
1251  * showing or do not. If do not, we do nothing. Which means we are still
1252  * not attached to any header folder and thus next/prev buttons are
1253  * still dimmed. Once the message that is shown by msg-view is found, the
1254  * new model of header-view will be attached and the references will be set.
1255  * On each further insertions dimming rules will be checked. However
1256  * this requires extra CPU time at least works.
1257  * (An message might be deleted from TnyFolder and thus will not be
1258  * inserted into the model again for example if it is removed by the
1259  * imap server and the header view is refreshed.)
1260  */
1261 static void 
1262 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1263                                         GtkTreePath *tree_path,
1264                                         GtkTreeIter *tree_iter,
1265                                         ModestMsgViewWindow *window)
1266 {
1267         ModestMsgViewWindowPrivate *priv = NULL; 
1268         TnyHeader *header = NULL;
1269
1270         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1271         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1272
1273         g_assert (model == priv->header_model);
1274         
1275         /* Check if the newly inserted message is the same we are actually
1276          * showing. IF not, we should remain detached from the header model
1277          * and thus prev and next toolbar buttons should remain dimmed. */
1278         gtk_tree_model_get (model, tree_iter, 
1279                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1280                             &header, -1);
1281
1282         if (TNY_IS_HEADER (header)) {
1283                 gchar *uid = NULL;
1284
1285                 uid = modest_tny_folder_get_header_unique_id (header);
1286                 if (!g_str_equal(priv->msg_uid, uid)) {
1287                         check_dimming_rules_after_change (window);
1288                         g_free(uid);
1289                         g_object_unref (G_OBJECT(header));
1290                         return;
1291                 }
1292                 g_free(uid);
1293                 g_object_unref(G_OBJECT(header));
1294         }
1295
1296         if (priv->row_reference) {
1297                 gtk_tree_row_reference_free (priv->row_reference); 
1298         }
1299
1300         /* Setup row_reference for the actual msg. */
1301         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1302         if (priv->row_reference == NULL) {
1303                 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1304                 return;
1305         }
1306
1307         /* Now set up next_row_reference. */
1308         if (priv->next_row_reference) {
1309                 gtk_tree_row_reference_free (priv->next_row_reference); 
1310         }
1311
1312         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1313         select_next_valid_row (priv->header_model,
1314                                &(priv->next_row_reference), FALSE, priv->is_outbox);
1315
1316         /* Connect the remaining callbacks to become able to detect
1317          * changes in header-view. */
1318         priv->row_changed_handler = 
1319                 g_signal_connect (priv->header_model, "row-changed",
1320                                   G_CALLBACK (modest_msg_view_window_on_row_changed),
1321                                   window);
1322         priv->row_deleted_handler = 
1323                 g_signal_connect (priv->header_model, "row-deleted",
1324                                   G_CALLBACK (modest_msg_view_window_on_row_deleted),
1325                                   window);
1326         priv->rows_reordered_handler = 
1327                 g_signal_connect (priv->header_model, "rows-reordered",
1328                                   G_CALLBACK (modest_msg_view_window_on_row_reordered),
1329                                   window);
1330
1331         check_dimming_rules_after_change (window);      
1332 }
1333
1334 static void 
1335 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1336                                          GtkTreePath *arg1,
1337                                          GtkTreeIter *arg2,
1338                                          gpointer arg3,
1339                                          ModestMsgViewWindow *window)
1340 {
1341         ModestMsgViewWindowPrivate *priv = NULL;
1342         gboolean already_changed = FALSE;
1343
1344         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1345
1346         /* If the current row was reordered select the proper next
1347            valid row. The same if the next row reference changes */
1348         if (!priv->row_reference ||
1349             !gtk_tree_row_reference_valid (priv->row_reference))
1350                 return;
1351
1352         if (priv->next_row_reference &&
1353             gtk_tree_row_reference_valid (priv->next_row_reference)) {
1354                 GtkTreePath *cur, *next;
1355                 /* Check that the order is still the correct one */
1356                 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1357                 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1358                 gtk_tree_path_next (cur);
1359                 if (gtk_tree_path_compare (cur, next) != 0) {
1360                         gtk_tree_row_reference_free (priv->next_row_reference);
1361                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1362                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1363                         already_changed = TRUE;
1364                 }
1365                 gtk_tree_path_free (cur);
1366                 gtk_tree_path_free (next);
1367         } else {
1368                 if (priv->next_row_reference)
1369                         gtk_tree_row_reference_free (priv->next_row_reference);
1370                 /* Update next row reference */
1371                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1372                 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1373                 already_changed = TRUE;
1374         }
1375
1376         check_dimming_rules_after_change (window);
1377 }
1378
1379 /* The modest_msg_view_window_update_model_replaced implements update
1380  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1381  * actually belongs to the header-view is the same as the TnyFolder of
1382  * the message of msg-view or not. If they are different, there is
1383  * nothing to do. If they are the same, then the model has replaced and
1384  * the reference in msg-view shall be replaced from the old model to
1385  * the new model. In this case the view will be detached from it's
1386  * header folder. From this point the next/prev buttons are dimmed.
1387  */
1388 static void 
1389 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1390                                               GtkTreeModel *model,
1391                                               const gchar *tny_folder_id)
1392 {
1393         ModestMsgViewWindowPrivate *priv = NULL; 
1394         ModestMsgViewWindow *window = NULL;
1395
1396         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1397         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1398
1399         window = MODEST_MSG_VIEW_WINDOW(observer);
1400         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1401
1402         /* If there is an other folder in the header-view then we do
1403          * not care about it's model (msg list). Else if the
1404          * header-view shows the folder the msg shown by us is in, we
1405          * shall replace our model reference and make some check. */
1406         if(model == NULL || tny_folder_id == NULL || 
1407            (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1408                 return;
1409
1410         /* Model is changed(replaced), so we should forget the old
1411          * one. Because there might be other references and there
1412          * might be some change on the model even if we unreferenced
1413          * it, we need to disconnect our signals here. */
1414         if (priv->header_model) {
1415                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1416                                                   priv->row_changed_handler))
1417                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1418                                                     priv->row_changed_handler);
1419                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1420                                                   priv->row_deleted_handler))
1421                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1422                                                     priv->row_deleted_handler);
1423                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1424                                                   priv->row_inserted_handler))
1425                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1426                                                     priv->row_inserted_handler);
1427                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1428                                                   priv->rows_reordered_handler))
1429                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1430                                                     priv->rows_reordered_handler);
1431
1432                 /* Frees */
1433                 if (priv->row_reference)
1434                         gtk_tree_row_reference_free (priv->row_reference);
1435                 if (priv->next_row_reference)
1436                         gtk_tree_row_reference_free (priv->next_row_reference);
1437                 g_object_unref(priv->header_model);
1438
1439                 /* Initialize */
1440                 priv->row_changed_handler = 0;
1441                 priv->row_deleted_handler = 0;
1442                 priv->row_inserted_handler = 0;
1443                 priv->rows_reordered_handler = 0;
1444                 priv->next_row_reference = NULL;
1445                 priv->row_reference = NULL;
1446                 priv->header_model = NULL;
1447         }
1448
1449         priv->header_model = g_object_ref (model);
1450
1451         /* Also we must connect to the new model for row insertions.
1452          * Only for insertions now. We will need other ones only after
1453          * the msg is show by msg-view is added to the new model. */
1454         priv->row_inserted_handler =
1455                 g_signal_connect (priv->header_model, "row-inserted",
1456                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1457                                   window);
1458
1459         modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1460         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1461 }
1462
1463 gboolean 
1464 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1465 {
1466         ModestMsgViewWindowPrivate *priv= NULL; 
1467
1468         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1469         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1470
1471         return priv->progress_hint;
1472 }
1473
1474 TnyHeader*
1475 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1476 {
1477         ModestMsgViewWindowPrivate *priv= NULL; 
1478         TnyMsg *msg = NULL;
1479         TnyHeader *header = NULL;
1480         GtkTreePath *path = NULL;
1481         GtkTreeIter iter;
1482
1483         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1484         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1485
1486         /* If the message was not obtained from a treemodel,
1487          * for instance if it was opened directly by the search UI:
1488          */
1489         if (priv->header_model == NULL || 
1490             priv->row_reference == NULL ||
1491             !gtk_tree_row_reference_valid (priv->row_reference)) {
1492                 msg = modest_msg_view_window_get_message (self);
1493                 if (msg) {
1494                         header = tny_msg_get_header (msg);
1495                         g_object_unref (msg);
1496                 }
1497                 return header;
1498         }
1499
1500         /* Get iter of the currently selected message in the header view: */
1501         path = gtk_tree_row_reference_get_path (priv->row_reference);
1502         g_return_val_if_fail (path != NULL, NULL);
1503         gtk_tree_model_get_iter (priv->header_model, 
1504                                  &iter, 
1505                                  path);
1506
1507         /* Get current message header */
1508         gtk_tree_model_get (priv->header_model, &iter, 
1509                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1510                             &header, -1);
1511
1512         gtk_tree_path_free (path);
1513         return header;
1514 }
1515
1516 TnyMsg*
1517 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1518 {
1519         ModestMsgViewWindowPrivate *priv;
1520         
1521         g_return_val_if_fail (self, NULL);
1522         
1523         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1524         
1525         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1526 }
1527
1528 const gchar*
1529 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1530 {
1531         ModestMsgViewWindowPrivate *priv;
1532
1533         g_return_val_if_fail (self, NULL);
1534         
1535         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1536
1537         return (const gchar*) priv->msg_uid;
1538 }
1539
1540 /* Used for the Ctrl+F accelerator */
1541 static void
1542 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1543                                                gpointer data)
1544 {
1545         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1546         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1547
1548         if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1549                 modest_msg_view_window_isearch_toolbar_close (obj, data);
1550        } else {
1551                 modest_msg_view_window_show_isearch_toolbar (obj, data);
1552        }
1553 }
1554
1555 /* Handler for menu option */
1556 static void
1557 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1558                                              gpointer data)
1559 {
1560         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1561         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1562
1563         gtk_widget_show (priv->isearch_toolbar);
1564         modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1565 }
1566
1567 /* Handler for click on the "X" close button in isearch toolbar */
1568 static void
1569 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1570                                               ModestMsgViewWindow *obj)
1571 {
1572         ModestMsgViewWindowPrivate *priv;
1573
1574         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1575
1576         /* Hide toolbar */
1577         gtk_widget_hide (priv->isearch_toolbar);
1578         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1579 }
1580
1581 static void
1582 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1583                                                ModestMsgViewWindow *obj)
1584 {
1585         const gchar *current_search;
1586         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1587
1588         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1589                 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1590                 return;
1591         }
1592
1593         current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1594
1595         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1596                 modest_platform_system_banner (NULL, NULL, _CS_FIND_REP_ENTER_TEXT);
1597                 return;
1598         }
1599
1600         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1601                 gboolean result;
1602                 g_free (priv->last_search);
1603                 priv->last_search = g_strdup (current_search);
1604                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1605                                                      priv->last_search);
1606                 if (!result) {
1607                         modest_platform_system_banner (NULL, NULL, 
1608                                                         _HL_IB_FIND_NO_MATCHES);
1609                         g_free (priv->last_search);
1610                         priv->last_search = NULL;
1611                 } else {
1612                         modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1613                 }
1614         } else {
1615                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1616                         modest_platform_system_banner (NULL, NULL, 
1617                                                         _HL_IB_FIND_COMPLETE);
1618                         g_free (priv->last_search);
1619                         priv->last_search = NULL;
1620                 } else {
1621                         modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1622                 }
1623         }
1624         
1625 }
1626
1627 static void
1628 modest_msg_view_window_set_zoom (ModestWindow *window,
1629                                  gdouble zoom)
1630 {
1631         ModestMsgViewWindowPrivate *priv;
1632         ModestWindowPrivate *parent_priv;
1633      
1634         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1635
1636         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1637         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1638         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1639
1640 }
1641
1642 static gdouble
1643 modest_msg_view_window_get_zoom (ModestWindow *window)
1644 {
1645         ModestMsgViewWindowPrivate *priv;
1646      
1647         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1648
1649         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1650         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1651 }
1652
1653 static gboolean
1654 modest_msg_view_window_zoom_plus (ModestWindow *window)
1655 {
1656         gdouble zoom_level;
1657         ModestMsgViewWindowPrivate *priv;
1658         gint int_zoom;
1659         gchar *banner_text;
1660      
1661         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1662         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1663   
1664         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1665
1666         if (zoom_level >= 2.0) {
1667                 modest_platform_system_banner (NULL, NULL, 
1668                                                 _CS_MAX_ZOOM_LEVEL_REACHED);
1669                 return FALSE;
1670         } else if (zoom_level >= 1.5) {
1671                 zoom_level = 2.0;
1672         } else if (zoom_level >= 1.2) {
1673                 zoom_level = 1.5;
1674         } else if (zoom_level >= 1.0) {
1675                 zoom_level = 1.2;
1676         } else if (zoom_level >= 0.8) {
1677                 zoom_level = 1.0;
1678         } else if (zoom_level >= 0.5) {
1679                 zoom_level = 0.8;
1680         } else {
1681                 zoom_level = 0.5;
1682         }
1683
1684         /* set zoom level */
1685         int_zoom = (gint) rint (zoom_level*100.0+0.1);
1686         banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1687         modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1688         g_free (banner_text);
1689         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1690
1691         return TRUE;
1692 }
1693
1694 static gboolean
1695 modest_msg_view_window_zoom_minus (ModestWindow *window)
1696 {
1697         gdouble zoom_level;
1698         ModestMsgViewWindowPrivate *priv;
1699         gint int_zoom;
1700         gchar *banner_text;
1701      
1702         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1703         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1704   
1705         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1706
1707         if (zoom_level <= 0.5) {
1708                 modest_platform_system_banner (NULL, NULL, 
1709                                                 _CS_MIN_ZOOM_LEVEL_REACHED);
1710                 return FALSE;
1711         } else if (zoom_level <= 0.8) {
1712                 zoom_level = 0.5;
1713         } else if (zoom_level <= 1.0) {
1714                 zoom_level = 0.8;
1715         } else if (zoom_level <= 1.2) {
1716                 zoom_level = 1.0;
1717         } else if (zoom_level <= 1.5) {
1718                 zoom_level = 1.2;
1719         } else if (zoom_level <= 2.0) {
1720                 zoom_level = 1.5;
1721         } else {
1722                 zoom_level = 2.0;
1723         }
1724
1725         /* set zoom level */
1726         int_zoom = (gint) rint (zoom_level*100.0+0.1);
1727         banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1728         modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1729         g_free (banner_text);
1730         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1731
1732         return TRUE;
1733 }
1734
1735 static gboolean
1736 modest_msg_view_window_key_event (GtkWidget *window,
1737                                   GdkEventKey *event,
1738                                   gpointer userdata)
1739 {
1740         GtkWidget *focus;
1741
1742         focus = gtk_window_get_focus (GTK_WINDOW (window));
1743
1744         /* for the isearch toolbar case */
1745         if (focus && GTK_IS_ENTRY (focus)) {
1746                 if (event->keyval == GDK_BackSpace) {
1747                         GdkEvent *copy;
1748                         copy = gdk_event_copy ((GdkEvent *) event);
1749                         gtk_widget_event (focus, copy);
1750                         gdk_event_free (copy);
1751                         return TRUE;
1752                 } else {
1753                         return FALSE;
1754                 }
1755         }
1756         return FALSE;
1757 }
1758
1759 gboolean
1760 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1761 {
1762         GtkTreePath *path;
1763         ModestMsgViewWindowPrivate *priv;
1764         GtkTreeIter tmp_iter;
1765         gboolean is_last_selected;
1766
1767         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1768         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1769
1770         /*if no model (so no rows at all), then virtually we are the last*/
1771         if (!priv->header_model || !priv->row_reference)
1772                 return TRUE;
1773
1774         if (!gtk_tree_row_reference_valid (priv->row_reference))
1775                 return TRUE;
1776
1777         path = gtk_tree_row_reference_get_path (priv->row_reference);
1778         if (path == NULL)
1779                 return TRUE;
1780
1781         is_last_selected = TRUE;
1782         while (is_last_selected) {
1783                 TnyHeader *header;
1784                 gtk_tree_path_next (path);
1785                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1786                         break;
1787                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1788                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1789                                 &header, -1);
1790                 if (header) {
1791                         if (msg_is_visible (header, priv->is_outbox))
1792                                 is_last_selected = FALSE;
1793                         g_object_unref(G_OBJECT(header));
1794                 }
1795         }
1796         gtk_tree_path_free (path);
1797         return is_last_selected;
1798 }
1799
1800 gboolean
1801 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1802 {
1803         ModestMsgViewWindowPrivate *priv;
1804
1805         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1806         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1807
1808         return priv->header_model != NULL;
1809 }
1810
1811 gboolean
1812 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1813 {
1814         ModestMsgViewWindowPrivate *priv;
1815
1816         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1817         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1818
1819         return priv->is_search_result;
1820 }
1821
1822 static gboolean
1823 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1824 {
1825         if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1826                 return FALSE;
1827         if (!check_outbox) {
1828                 return TRUE;
1829         } else {
1830                 ModestTnySendQueueStatus status;
1831                 status = modest_tny_all_send_queues_get_msg_status (header);
1832                 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1833                         (status != MODEST_TNY_SEND_QUEUE_SENDING));
1834         }
1835 }
1836
1837 gboolean
1838 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1839 {
1840         GtkTreePath *path;
1841         ModestMsgViewWindowPrivate *priv;
1842         gboolean is_first_selected;
1843         GtkTreeIter tmp_iter;
1844
1845         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1846         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1847
1848         /*if no model (so no rows at all), then virtually we are the first*/
1849         if (!priv->header_model || !priv->row_reference)
1850                 return TRUE;
1851
1852         if (!gtk_tree_row_reference_valid (priv->row_reference))
1853                 return TRUE;
1854
1855         path = gtk_tree_row_reference_get_path (priv->row_reference);
1856         if (!path)
1857                 return TRUE;
1858
1859         is_first_selected = TRUE;
1860         while (is_first_selected) {
1861                 TnyHeader *header;
1862                 if(!gtk_tree_path_prev (path))
1863                         break;
1864                 /* Here the 'if' is needless for logic, but let make sure
1865                  * iter is valid for gtk_tree_model_get. */
1866                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1867                         break;
1868                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1869                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1870                                 &header, -1);
1871                 if (header) {
1872                         if (msg_is_visible (header, priv->is_outbox))
1873                                 is_first_selected = FALSE;
1874                         g_object_unref(G_OBJECT(header));
1875                 }
1876         }
1877         gtk_tree_path_free (path);
1878         return is_first_selected;
1879 }
1880
1881 typedef struct {
1882         TnyHeader *header;
1883         gchar *msg_uid;
1884         TnyFolder *folder;
1885         GtkTreeRowReference *row_reference;
1886 } MsgReaderInfo;
1887
1888 static void
1889 message_reader_performer (gboolean canceled, 
1890                           GError *err,
1891                           GtkWindow *parent_window, 
1892                           TnyAccount *account, 
1893                           gpointer user_data)
1894 {
1895         ModestMailOperation *mail_op = NULL;
1896         MsgReaderInfo *info;
1897
1898         info = (MsgReaderInfo *) user_data;
1899         if (canceled || err) {
1900                 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1901                 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1902                 goto frees;
1903         }
1904
1905         /* Register the header - it'll be unregistered in the callback */
1906         if (info->header)
1907                 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1908
1909         /* New mail operation */
1910         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1911                                                                  modest_ui_actions_disk_operations_error_handler, 
1912                                                                  NULL, NULL);
1913
1914         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1915         if (info->header)
1916                 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1917         else
1918                 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1919         g_object_unref (mail_op);
1920
1921         /* Update dimming rules */
1922         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1923         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1924
1925  frees:
1926         /* Frees. The row_reference will be freed by the view_msg_cb callback */
1927         g_free (info->msg_uid);
1928         if (info->folder)
1929                 g_object_unref (info->folder);
1930         if (info->header)
1931                 g_object_unref (info->header);
1932         g_slice_free (MsgReaderInfo, info);
1933 }
1934
1935
1936 /**
1937  * Reads the message whose summary item is @header. It takes care of
1938  * several things, among others:
1939  *
1940  * If the message was not previously downloaded then ask the user
1941  * before downloading. If there is no connection launch the connection
1942  * dialog. Update toolbar dimming rules.
1943  *
1944  * Returns: TRUE if the mail operation was started, otherwise if the
1945  * user do not want to download the message, or if the user do not
1946  * want to connect, then the operation is not issued
1947  **/
1948 static gboolean
1949 message_reader (ModestMsgViewWindow *window,
1950                 ModestMsgViewWindowPrivate *priv,
1951                 TnyHeader *header,
1952                 const gchar *msg_uid,
1953                 TnyFolder *folder,
1954                 GtkTreeRowReference *row_reference)
1955 {
1956         ModestWindowMgr *mgr;
1957         TnyAccount *account = NULL;
1958         MsgReaderInfo *info;
1959
1960         /* We set the header from model while we're loading */
1961         tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1962         modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
1963
1964         if (header)
1965                 folder = NULL;
1966
1967         if (folder)
1968                 g_object_ref (folder);
1969
1970         mgr = modest_runtime_get_window_mgr ();
1971         /* Msg download completed */
1972         if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1973
1974                 /* Ask the user if he wants to download the message if
1975                    we're not online */
1976                 if (!tny_device_is_online (modest_runtime_get_device())) {
1977                         GtkResponseType response;
1978
1979                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1980                                                                             _("mcen_nc_get_msg"));
1981                         if (response == GTK_RESPONSE_CANCEL) {
1982                                 update_window_title (window);
1983                                 return FALSE;
1984                         }
1985
1986                         if (header) {
1987                                 folder = tny_header_get_folder (header);
1988                         }
1989                         info = g_slice_new (MsgReaderInfo);
1990                         info->msg_uid = g_strdup (msg_uid);
1991                         if (header)
1992                                 info->header = g_object_ref (header);
1993                         else
1994                                 info->header = NULL;    
1995                         if (folder)
1996                                 info->folder = g_object_ref (folder);
1997                         else
1998                                 info->folder = NULL;
1999                         if (row_reference) {
2000                                 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2001                         } else {
2002                                 info->row_reference = NULL;
2003                         }
2004
2005                         /* Offer the connection dialog if necessary */
2006                         modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
2007                                                                        TRUE,
2008                                                                        TNY_FOLDER_STORE (folder),
2009                                                                        message_reader_performer, 
2010                                                                        info);
2011                         if (folder)
2012                                 g_object_unref (folder);
2013                         return TRUE;
2014                 }
2015         }
2016
2017         if (header) {
2018                 folder = tny_header_get_folder (header);
2019         }
2020         if (folder)
2021                 account = tny_folder_get_account (folder);
2022
2023         info = g_slice_new (MsgReaderInfo);
2024         info->msg_uid = g_strdup (msg_uid);
2025         if (folder)
2026                 info->folder = g_object_ref (folder);
2027         else
2028                 info->folder = NULL;
2029         if (header)
2030                 info->header = g_object_ref (header);
2031         else
2032                 info->header = NULL;
2033         if (row_reference)
2034                 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2035         else
2036                 info->row_reference = NULL;
2037
2038         message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2039         if (account)
2040                 g_object_unref (account);
2041         if (folder)
2042                 g_object_unref (folder);
2043
2044         return TRUE;
2045 }
2046
2047 gboolean
2048 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2049 {
2050         ModestMsgViewWindowPrivate *priv;
2051         GtkTreePath *path= NULL;
2052         GtkTreeIter tmp_iter;
2053         TnyHeader *header;
2054         gboolean retval = TRUE;
2055         GtkTreeRowReference *row_reference = NULL;
2056
2057         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2058         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2059
2060         if (!priv->row_reference)
2061                 return FALSE;
2062
2063         /* Update the next row reference if it's not valid. This could
2064            happen if for example the header which it was pointing to,
2065            was deleted. The best place to do it is in the row-deleted
2066            handler but the tinymail model do not work like the glib
2067            tree models and reports the deletion when the row is still
2068            there */
2069         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2070                 if (priv->next_row_reference) {
2071                         gtk_tree_row_reference_free (priv->next_row_reference);
2072                 }
2073                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2074                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2075                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2076                 } else {
2077                         priv->next_row_reference = NULL;
2078                 }
2079         }
2080         if (priv->next_row_reference)
2081                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2082         if (path == NULL)
2083                 return FALSE;
2084
2085         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2086
2087         gtk_tree_model_get_iter (priv->header_model,
2088                                  &tmp_iter,
2089                                  path);
2090         gtk_tree_path_free (path);
2091
2092         gtk_tree_model_get (priv->header_model, &tmp_iter, 
2093                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2094                             &header, -1);
2095         
2096         /* Read the message & show it */
2097         if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2098                 retval = FALSE;
2099         }
2100         gtk_tree_row_reference_free (row_reference);
2101
2102         /* Free */
2103         g_object_unref (header);
2104
2105         return retval;
2106 }
2107
2108 gboolean        
2109 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2110 {
2111         ModestMsgViewWindowPrivate *priv = NULL;
2112         GtkTreePath *path;
2113         gboolean finished = FALSE;
2114         gboolean retval = FALSE;
2115
2116         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2117         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2118
2119         if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2120                 gtk_tree_row_reference_free (priv->row_reference);
2121                 priv->row_reference = NULL;
2122         }
2123
2124         /* Return inmediatly if there is no header model */
2125         if (!priv->header_model || !priv->row_reference)
2126                 return FALSE;
2127
2128         path = gtk_tree_row_reference_get_path (priv->row_reference);
2129         while (!finished && gtk_tree_path_prev (path)) {
2130                 TnyHeader *header;
2131                 GtkTreeIter iter;
2132
2133                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2134                 gtk_tree_model_get (priv->header_model, &iter, 
2135                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2136                                     &header, -1);
2137                 finished = TRUE;
2138                 if (header) {
2139                         if (msg_is_visible (header, priv->is_outbox)) {
2140                                 GtkTreeRowReference *row_reference;
2141                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2142                                 /* Read the message & show it */
2143                                 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2144                                 gtk_tree_row_reference_free (row_reference);
2145                         } else {
2146                                 finished = FALSE;
2147                         }
2148                         g_object_unref (header);
2149                 }
2150         }
2151
2152         gtk_tree_path_free (path);
2153         return retval;
2154 }
2155
2156 static void
2157 view_msg_cb (ModestMailOperation *mail_op, 
2158              TnyHeader *header, 
2159              gboolean canceled,
2160              TnyMsg *msg, 
2161              GError *error,
2162              gpointer user_data)
2163 {
2164         ModestMsgViewWindow *self = NULL;
2165         ModestMsgViewWindowPrivate *priv = NULL;
2166         GtkTreeRowReference *row_reference = NULL;
2167
2168         /* Unregister the header (it was registered before creating the mail operation) */
2169         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2170
2171         row_reference = (GtkTreeRowReference *) user_data;
2172         if (canceled) {
2173                 if (row_reference)
2174                         gtk_tree_row_reference_free (row_reference);
2175                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2176                 if (self) {
2177                         /* Restore window title */
2178                         update_window_title (self);
2179                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2180                         g_object_unref (self);
2181                 }
2182                 return;
2183         }
2184
2185         /* If there was any error */
2186         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2187                 if (row_reference)
2188                         gtk_tree_row_reference_free (row_reference);
2189                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2190                 if (self) {
2191                         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2192                         /* First we check if the parent is a folder window */
2193                         if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2194                                 gboolean is_merge;
2195                                 TnyAccount *account = NULL;
2196                                 GtkWidget *header_window = NULL;
2197
2198                                 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2199
2200                                 /* Get the account */
2201                                 if (!is_merge)
2202                                         account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2203                                                                                   priv->msg_uid);
2204
2205                                 if (is_merge || account) {
2206                                         TnyFolder *folder = NULL;
2207
2208                                         /* Try to get the message, if it's already downloaded
2209                                            we don't need to connect */
2210                                         if (account) {
2211                                                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), 
2212                                                                                                        priv->msg_uid);
2213                                         } else {
2214                                                 ModestTnyAccountStore *account_store;
2215                                                 ModestTnyLocalFoldersAccount *local_folders_account;
2216
2217                                                 account_store = modest_runtime_get_account_store ();
2218                                                 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2219                                                         modest_tny_account_store_get_local_folders_account (account_store));
2220                                                 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2221                                                 g_object_unref (local_folders_account);
2222                                         }
2223                                         if (account) g_object_unref (account);
2224
2225                                         if (folder) {
2226                                                 header_window = (GtkWidget *)
2227                                                         modest_header_window_new (
2228                                                                 folder, 
2229                                                                 modest_window_get_active_account (MODEST_WINDOW (self)), 
2230                                                                 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2231                                                 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2232                                                                                         MODEST_WINDOW (header_window),
2233                                                                                         NULL)) {
2234                                                         gtk_widget_destroy (GTK_WIDGET (header_window));
2235                                                 } else {
2236                                                         gtk_widget_show_all (GTK_WIDGET (header_window));
2237                                                 }
2238                                                 g_object_unref (folder);
2239                                         }
2240                                 }
2241                         }
2242
2243
2244                         /* Restore window title */
2245                         update_window_title (self);
2246                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2247                         g_object_unref (self);
2248                 }
2249                 return;
2250         }
2251
2252         if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2253                 TnyMimePart *body;
2254                 body = modest_tny_msg_find_body_part (msg, TRUE);
2255                 
2256                 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2257                         /* We have body structure but not the body mime part. What we do
2258                          * is restarting load of message */
2259                         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2260                         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2261
2262                         tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2263
2264                         modest_msg_view_window_reload (self);
2265
2266                         if (row_reference)
2267                                 gtk_tree_row_reference_free (row_reference);
2268                         g_object_unref (body);
2269                         return;
2270                 }
2271
2272                 if (body)
2273                         g_object_unref  (body);
2274         }
2275
2276         /* Get the window */ 
2277         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2278         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2279         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2280
2281         /* Update the row reference */
2282         if (priv->row_reference != NULL) {
2283                 gtk_tree_row_reference_free (priv->row_reference);
2284                 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2285                 if (priv->next_row_reference != NULL) {
2286                         gtk_tree_row_reference_free (priv->next_row_reference);
2287                 }
2288                 if (priv->row_reference) {
2289                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2290                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2291                 } else {
2292                         priv->next_row_reference = NULL;
2293                 }
2294         }
2295
2296         /* Mark header as read */
2297         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2298                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2299
2300         /* Set new message */
2301         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2302                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2303                 modest_msg_view_window_update_priority (self);
2304                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2305                 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2306                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2307         }
2308
2309         /* Set the new message uid of the window  */
2310         if (priv->msg_uid) {
2311                 g_free (priv->msg_uid);
2312                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2313         }
2314
2315         /* Notify the observers */
2316         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2317                        0, priv->header_model, priv->row_reference);
2318
2319         /* Sync the flags if the message is not opened from a header
2320            model, i.e, if it's opened from a notification */
2321         if (!priv->header_model)
2322                 sync_flags (self);
2323
2324         /* Frees */
2325         g_object_unref (self);
2326         if (row_reference)
2327                 gtk_tree_row_reference_free (row_reference);
2328 }
2329
2330 TnyFolderType
2331 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2332 {
2333         ModestMsgViewWindowPrivate *priv;
2334         TnyMsg *msg;
2335         TnyFolderType folder_type;
2336
2337         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2338
2339         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2340
2341         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2342         if (msg) {
2343                 TnyFolder *folder;
2344
2345                 folder = tny_msg_get_folder (msg);
2346                 if (folder) {
2347                         folder_type = modest_tny_folder_guess_folder_type (folder);
2348                         g_object_unref (folder);
2349                 }
2350                 g_object_unref (msg);
2351         }
2352
2353         return folder_type;
2354 }
2355
2356
2357 static void
2358 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2359 {
2360         ModestMsgViewWindowPrivate *priv;
2361         TnyHeader *header = NULL;
2362         TnyHeaderFlags flags = 0;
2363
2364         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2365
2366         if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2367                 GtkTreeIter iter;
2368                 GtkTreePath *path = NULL;
2369
2370                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2371                 g_return_if_fail (path != NULL);
2372                 gtk_tree_model_get_iter (priv->header_model, 
2373                                          &iter, 
2374                                          gtk_tree_row_reference_get_path (priv->row_reference));
2375
2376                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2377                                     &header, -1);
2378                 gtk_tree_path_free (path);
2379         } else {
2380                 TnyMsg *msg;
2381                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2382                 if (msg) {
2383                         header = tny_msg_get_header (msg);
2384                         g_object_unref (msg);
2385                 }
2386         }
2387
2388         if (header) {
2389                 flags = tny_header_get_flags (header);
2390                 g_object_unref(G_OBJECT(header));
2391         }
2392
2393         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2394
2395 }
2396
2397 static void
2398 toolbar_resize (ModestMsgViewWindow *self)
2399 {
2400         ModestMsgViewWindowPrivate *priv = NULL;
2401         ModestWindowPrivate *parent_priv = NULL;
2402         GtkWidget *widget;
2403         gint static_button_size;
2404         ModestWindowMgr *mgr;
2405
2406         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2407         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2408         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2409
2410         mgr = modest_runtime_get_window_mgr ();
2411         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2412
2413         if (parent_priv->toolbar) {
2414                 /* Set expandable and homogeneous tool buttons */
2415                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2416                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2417                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2418                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2419                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2420                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2421                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2422                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2423                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2424                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2425                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2426                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2427                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2428                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2429                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2430                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2431                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2432                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2433                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2434         }
2435 }
2436
2437 static void
2438 modest_msg_view_window_show_toolbar (ModestWindow *self,
2439                                      gboolean show_toolbar)
2440 {
2441         ModestMsgViewWindowPrivate *priv = NULL;
2442         ModestWindowPrivate *parent_priv;
2443
2444         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2445         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2446
2447         /* Set optimized view status */
2448         priv->optimized_view = !show_toolbar;
2449
2450         if (!parent_priv->toolbar) {
2451                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2452                                                                   "/ToolBar");
2453
2454 #ifdef MODEST_TOOLKIT_HILDON2
2455                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2456 #else
2457                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2458 #endif
2459                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2460
2461                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2462                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2463                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2464
2465                 modest_window_add_toolbar (MODEST_WINDOW (self), 
2466                                            GTK_TOOLBAR (parent_priv->toolbar));
2467
2468         }
2469
2470         if (show_toolbar) {
2471                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2472                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2473                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2474
2475                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2476                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2477                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2478                 else
2479                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2480
2481         } else {
2482                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2483                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2484         }
2485 }
2486
2487 static void 
2488 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2489                                                GdkEvent *event,
2490                                                ModestMsgViewWindow *window)
2491 {
2492         if (!GTK_WIDGET_VISIBLE (window))
2493                 return;
2494
2495         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2496 }
2497
2498 gboolean 
2499 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2500 {
2501         ModestMsgViewWindowPrivate *priv;
2502         
2503         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2504         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2505
2506         return priv->progress_hint;
2507 }
2508
2509 static gboolean
2510 observers_empty (ModestMsgViewWindow *self)
2511 {
2512         GSList *tmp = NULL;
2513         ModestMsgViewWindowPrivate *priv;
2514         gboolean is_empty = TRUE;
2515         guint pending_ops = 0;
2516  
2517         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2518         tmp = priv->progress_widgets;
2519
2520         /* Check all observers */
2521         while (tmp && is_empty)  {
2522                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2523                 is_empty = pending_ops == 0;
2524                 
2525                 tmp = g_slist_next(tmp);
2526         }
2527         
2528         return is_empty;
2529 }
2530
2531 static void
2532 on_account_removed (TnyAccountStore *account_store, 
2533                     TnyAccount *account,
2534                     gpointer user_data)
2535 {
2536         /* Do nothing if it's a transport account, because we only
2537            show the messages of a store account */
2538         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2539                 const gchar *parent_acc = NULL;
2540                 const gchar *our_acc = NULL;
2541
2542                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2543                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2544
2545                 /* Close this window if I'm showing a message of the removed account */
2546                 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2547                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2548         }
2549 }
2550
2551 static void 
2552 on_mail_operation_started (ModestMailOperation *mail_op,
2553                            gpointer user_data)
2554 {
2555         ModestMsgViewWindow *self;
2556         ModestMailOperationTypeOperation op_type;
2557         GSList *tmp;
2558         ModestMsgViewWindowPrivate *priv;
2559         GObject *source = NULL;
2560
2561         self = MODEST_MSG_VIEW_WINDOW (user_data);
2562         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2563         op_type = modest_mail_operation_get_type_operation (mail_op);
2564         tmp = priv->progress_widgets;
2565         source = modest_mail_operation_get_source(mail_op);
2566         if (G_OBJECT (self) == source) {
2567                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2568                     op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2569                     op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2570                         set_progress_hint (self, TRUE);
2571                         while (tmp) {
2572                                 modest_progress_object_add_operation (
2573                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2574                                                 mail_op);
2575                                 tmp = g_slist_next (tmp);
2576                         }
2577                 }
2578         }
2579         g_object_unref (source);
2580
2581         /* Update dimming rules */
2582         check_dimming_rules_after_change (self);
2583 }
2584
2585 static void
2586 on_mail_operation_finished (ModestMailOperation *mail_op,
2587                             gpointer user_data)
2588 {
2589         ModestMsgViewWindow *self;
2590         ModestMailOperationTypeOperation op_type;
2591         GSList *tmp;
2592         ModestMsgViewWindowPrivate *priv;
2593
2594         self = MODEST_MSG_VIEW_WINDOW (user_data);
2595         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2596         op_type = modest_mail_operation_get_type_operation (mail_op);
2597         tmp = priv->progress_widgets;
2598
2599         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2600             op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2601             op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2602                 while (tmp) {
2603                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2604                                                                  mail_op);
2605                         tmp = g_slist_next (tmp);
2606                 }
2607
2608                 /* If no more operations are being observed, NORMAL mode is enabled again */
2609                 if (observers_empty (self)) {
2610                         set_progress_hint (self, FALSE);
2611                 }
2612         }
2613
2614         /* Update dimming rules. We have to do this right here
2615            and not in view_msg_cb because at that point the
2616            transfer mode is still enabled so the dimming rule
2617            won't let the user delete the message that has been
2618            readed for example */
2619         check_dimming_rules_after_change (self);
2620 }
2621
2622 static void
2623 on_queue_changed (ModestMailOperationQueue *queue,
2624                   ModestMailOperation *mail_op,
2625                   ModestMailOperationQueueNotification type,
2626                   ModestMsgViewWindow *self)
2627 {       
2628         ModestMsgViewWindowPrivate *priv;
2629
2630         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2631
2632         /* If this operations was created by another window, do nothing */
2633         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2634             return;
2635
2636         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2637                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2638                                                                G_OBJECT (mail_op),
2639                                                                "operation-started",
2640                                                                G_CALLBACK (on_mail_operation_started),
2641                                                                self);
2642                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2643                                                                G_OBJECT (mail_op),
2644                                                                "operation-finished",
2645                                                                G_CALLBACK (on_mail_operation_finished),
2646                                                                self);
2647         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2648                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2649                                                                   G_OBJECT (mail_op),
2650                                                                   "operation-started");
2651                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2652                                                                   G_OBJECT (mail_op),
2653                                                                   "operation-finished");
2654         }
2655 }
2656
2657 TnyList *
2658 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2659 {
2660         ModestMsgViewWindowPrivate *priv;
2661         TnyList *selected_attachments = NULL;
2662         
2663         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2664         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2665
2666         /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2667         selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2668         
2669         return selected_attachments;
2670 }
2671
2672 typedef struct {
2673         ModestMsgViewWindow *self;
2674         gchar *file_path;
2675         gchar *attachment_uid;
2676 } DecodeAsyncHelper;
2677
2678 static void
2679 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2680                                    gboolean cancelled, 
2681                                    TnyStream *stream, 
2682                                    GError *err, 
2683                                    gpointer user_data)
2684 {
2685         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2686         const gchar *content_type;
2687
2688         if (cancelled || err) {
2689                 if (err) {
2690                         gchar *msg;
2691                         if ((err->domain == TNY_ERROR_DOMAIN) && 
2692                             (err->code == TNY_IO_ERROR_WRITE) &&
2693                             (errno == ENOSPC)) {
2694                                 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2695                         } else {
2696                                 msg = g_strdup (_("mail_ib_file_operation_failed"));
2697                         }
2698                         modest_platform_information_banner (NULL, NULL, msg);
2699                         g_free (msg);
2700                 }
2701                 goto free;
2702         }
2703
2704         /* It could happen that the window was closed. So we
2705            assume it is a cancelation */
2706         if (!GTK_WIDGET_VISIBLE (helper->self))
2707                 goto free;
2708
2709         /* Remove the progress hint */
2710         set_progress_hint (helper->self, FALSE);
2711
2712         content_type = tny_mime_part_get_content_type (mime_part);
2713         if (g_str_has_prefix (content_type, "message/rfc822")) {
2714                 ModestWindowMgr *mgr;
2715                 ModestWindow *msg_win = NULL;
2716                 TnyMsg * msg;
2717                 gchar *account;
2718                 const gchar *mailbox;
2719                 TnyStream *file_stream;
2720                 gint fd;
2721
2722                 fd = g_open (helper->file_path, O_RDONLY, 0644);
2723                 if (fd != -1) {
2724                         file_stream = tny_fs_stream_new (fd);
2725
2726                         mgr = modest_runtime_get_window_mgr ();
2727
2728                         account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2729                         mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2730
2731                         if (!account)
2732                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2733
2734                         msg = tny_camel_msg_new ();
2735                         tny_camel_msg_parse (msg, file_stream);
2736                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2737                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2738                                                 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2739                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2740                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2741                         else
2742                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2743                         g_object_unref (msg);
2744                         g_object_unref (file_stream);
2745                 } else {
2746                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2747                 }
2748
2749         } else {
2750
2751                 /* make the file read-only */
2752                 g_chmod(helper->file_path, 0444);
2753
2754                 /* Activate the file */
2755                 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2756         }
2757
2758  free:
2759         /* Frees */
2760         g_object_unref (helper->self);
2761         g_free (helper->file_path);
2762         g_free (helper->attachment_uid);
2763         g_slice_free (DecodeAsyncHelper, helper);
2764 }
2765
2766 static void
2767 view_attachment_connect_handler (gboolean canceled,
2768                                  GError *err,
2769                                  GtkWindow *parent_window,
2770                                  TnyAccount *account,
2771                                  TnyMimePart *part)
2772 {
2773
2774         if (canceled || err) {
2775                 g_object_unref (part);
2776                 return;
2777         }
2778
2779         modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2780                                                 part);
2781         g_object_unref (part);
2782 }
2783
2784 void
2785 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2786                                         TnyMimePart *mime_part)
2787 {
2788         ModestMsgViewWindowPrivate *priv;
2789         const gchar *msg_uid;
2790         gchar *attachment_uid = NULL;
2791         gint attachment_index = 0;
2792         TnyList *attachments;
2793
2794         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2795         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2796         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2797
2798         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2799         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2800         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2801         g_object_unref (attachments);
2802
2803         if (msg_uid && attachment_index >= 0) {
2804                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2805         }
2806
2807         if (mime_part == NULL) {
2808                 gboolean error = FALSE;
2809                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2810                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2811                         error = TRUE;
2812                 } else if (tny_list_get_length (selected_attachments) > 1) {
2813                         modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2814                         error = TRUE;
2815                 } else {
2816                         TnyIterator *iter;
2817                         iter = tny_list_create_iterator (selected_attachments);
2818                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2819                         g_object_unref (iter);
2820                 }
2821                 if (selected_attachments)
2822                         g_object_unref (selected_attachments);
2823
2824                 if (error)
2825                         goto frees;
2826         } else {
2827                 g_object_ref (mime_part);
2828         }
2829
2830         if (tny_mime_part_is_purged (mime_part))
2831                 goto frees;
2832
2833         if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2834             !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2835                 gboolean is_merge;
2836                 TnyAccount *account;
2837
2838                 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2839                 account = NULL;
2840                 /* Get the account */
2841                 if (!is_merge)
2842                         account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2843                                                                   priv->msg_uid);
2844
2845                 if (!tny_device_is_online (modest_runtime_get_device())) {
2846                         modest_platform_connect_and_perform (GTK_WINDOW (window),
2847                                                              TRUE,
2848                                                              TNY_ACCOUNT (account),
2849                                                              (ModestConnectedPerformer) view_attachment_connect_handler,
2850                                                              g_object_ref (mime_part));
2851                         goto frees;
2852                 }
2853         }
2854
2855         if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2856                 gchar *filepath = NULL;
2857                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2858                 gboolean show_error_banner = FALSE;
2859                 TnyFsStream *temp_stream = NULL;
2860                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2861                                                                &filepath);
2862
2863                 if (temp_stream != NULL) {
2864                         ModestAccountMgr *mgr;
2865                         DecodeAsyncHelper *helper;
2866                         gboolean decode_in_provider;
2867                         ModestProtocol *protocol;
2868                         const gchar *account; 
2869
2870                         /* Activate progress hint */
2871                         set_progress_hint (window, TRUE);
2872
2873                         helper = g_slice_new0 (DecodeAsyncHelper);
2874                         helper->self = g_object_ref (window);
2875                         helper->file_path = g_strdup (filepath);
2876                         helper->attachment_uid = g_strdup (attachment_uid);
2877
2878                         decode_in_provider = FALSE;
2879                         mgr = modest_runtime_get_account_mgr ();
2880                         account = modest_window_get_active_account (MODEST_WINDOW (window));
2881                         if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2882                                 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2883                                         gchar *uri;
2884                                         uri = g_strconcat ("file://", filepath, NULL);
2885                                         decode_in_provider = 
2886                                                 modest_account_protocol_decode_part_to_stream_async (
2887                                                         MODEST_ACCOUNT_PROTOCOL (protocol),
2888                                                         mime_part,
2889                                                         filepath,
2890                                                         TNY_STREAM (temp_stream),
2891                                                         on_decode_to_stream_async_handler,
2892                                                         NULL,
2893                                                         helper);
2894                                         g_free (uri);
2895                                 }
2896                         }
2897
2898                         if (!decode_in_provider)
2899                                 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2900                                                                       on_decode_to_stream_async_handler,
2901                                                                       NULL,
2902                                                                       helper);
2903                         g_object_unref (temp_stream);
2904                         /* NOTE: files in the temporary area will be automatically
2905                          * cleaned after some time if they are no longer in use */
2906                 } else {
2907                         if (filepath) {
2908                                 const gchar *content_type;
2909                                 /* the file may already exist but it isn't writable,
2910                                  * let's try to open it anyway */
2911                                 content_type = tny_mime_part_get_content_type (mime_part);
2912                                 modest_platform_activate_file (filepath, content_type);
2913                         } else {
2914                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2915                                 show_error_banner = TRUE;
2916                         }
2917                 }
2918                 if (filepath)
2919                         g_free (filepath);
2920                 if (show_error_banner)
2921                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2922         } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2923                 ModestWindowMgr *mgr;
2924                 ModestWindow *msg_win = NULL;
2925                 TnyMsg *current_msg;
2926                 gboolean found;
2927                 TnyHeader *header;
2928
2929                 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2930                 mgr = modest_runtime_get_window_mgr ();
2931                 header = tny_msg_get_header (TNY_MSG (current_msg));
2932                 found = modest_window_mgr_find_registered_message_uid (mgr,
2933                                                                        attachment_uid,
2934                                                                        &msg_win);
2935                 
2936                 if (found) {
2937                         g_debug ("window for this body is already being created");
2938                 } else {
2939
2940                         /* it's not found, so create a new window for it */
2941                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2942                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2943                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2944                         if (!account)
2945                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2946                         
2947                         msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2948                                                                               account, mailbox, attachment_uid);
2949                         
2950                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2951                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2952                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2953                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2954                         else
2955                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2956                 }
2957                 g_object_unref (current_msg);           
2958         } else {
2959                 /* message attachment */
2960                 TnyHeader *header = NULL;
2961                 ModestWindowMgr *mgr;
2962                 ModestWindow *msg_win = NULL;
2963                 gboolean found;
2964
2965                 header = tny_msg_get_header (TNY_MSG (mime_part));
2966                 mgr = modest_runtime_get_window_mgr ();
2967                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2968
2969                 if (found) {
2970                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
2971                          * thus, we don't do anything */
2972                         g_debug ("window for is already being created");
2973                 } else {
2974                         /* it's not found, so create a new window for it */
2975                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2976                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2977                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2978                         if (!account)
2979                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2980                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, 
2981                                                                              mailbox, attachment_uid);
2982                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2983                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2984                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2985                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2986                         else
2987                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2988                 }
2989         }
2990
2991  frees:
2992         if (attachment_uid)
2993                 g_free (attachment_uid);
2994         if (mime_part)
2995                 g_object_unref (mime_part);
2996 }
2997
2998 typedef struct
2999 {
3000         gchar *filename;
3001         TnyMimePart *part;
3002 } SaveMimePartPair;
3003
3004 typedef struct
3005 {
3006         GList *pairs;
3007         GnomeVFSResult result;
3008         gchar *uri;
3009         ModestMsgViewWindow *window;
3010 } SaveMimePartInfo;
3011
3012 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3013 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3014 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3015 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3016
3017 static void
3018 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3019 {
3020         GList *node;
3021
3022         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3023                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3024                 g_free (pair->filename);
3025                 g_object_unref (pair->part);
3026                 g_slice_free (SaveMimePartPair, pair);
3027         }
3028         g_list_free (info->pairs);
3029         info->pairs = NULL;
3030         g_free (info->uri);
3031         g_object_unref (info->window);
3032         info->window = NULL;
3033         if (with_struct) {
3034                 g_slice_free (SaveMimePartInfo, info);
3035         }
3036 }
3037
3038 static gboolean
3039 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3040 {
3041         /* This is a GDK lock because we are an idle callback and
3042          * modest_platform_system_banner is or does Gtk+ code */
3043
3044         gdk_threads_enter (); /* CHECKED */
3045         if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3046                 /* nothing */
3047         } else if (info->result == GNOME_VFS_OK) {
3048                 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3049         } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3050                 gchar *msg = NULL;
3051
3052                 /* Check if the uri belongs to the external mmc */
3053                 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3054                         msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3055                 else
3056                         msg = g_strdup (_KR("cerm_memory_card_full"));
3057                 modest_platform_information_banner (NULL, NULL, msg);
3058                 g_free (msg);
3059         } else {
3060                 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3061         }
3062         save_mime_part_info_free (info, FALSE);
3063         gdk_threads_leave (); /* CHECKED */
3064
3065         return FALSE;
3066 }
3067
3068 static void
3069 save_mime_part_to_file_connect_handler (gboolean canceled,
3070                                         GError *err,
3071                                         GtkWindow *parent_window,
3072                                         TnyAccount *account,
3073                                         SaveMimePartInfo *info)
3074 {
3075         if (canceled || err) {
3076                 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3077         } else {
3078                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3079         }
3080 }
3081
3082 static gboolean
3083 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3084 {
3085         gboolean is_merge;
3086         TnyAccount *account;
3087         ModestMsgViewWindowPrivate *priv;
3088
3089         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3090
3091         is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3092         account = NULL;
3093
3094         /* Get the account */
3095         if (!is_merge)
3096                 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3097                                                           priv->msg_uid);
3098
3099         modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3100                                              TRUE,
3101                                              TNY_ACCOUNT (account),
3102                                              (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3103                                              info);
3104         return FALSE;
3105 }
3106
3107 static gpointer
3108 save_mime_part_to_file (SaveMimePartInfo *info)
3109 {
3110         GnomeVFSHandle *handle;
3111         TnyStream *stream;
3112         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3113
3114         if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3115             !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3116                 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3117                 return NULL;
3118         }
3119
3120         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3121         if (info->result == GNOME_VFS_OK) {
3122                 GError *error = NULL;
3123                 gboolean decode_in_provider;
3124                 gssize written;
3125                 ModestAccountMgr *mgr;
3126                 const gchar *account;
3127                 ModestProtocol *protocol = NULL;
3128
3129                 stream = tny_vfs_stream_new (handle);
3130
3131                 decode_in_provider = FALSE;
3132                 mgr = modest_runtime_get_account_mgr ();
3133                 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3134                 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3135                         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3136                                 decode_in_provider = 
3137                                         modest_account_protocol_decode_part_to_stream (
3138                                                 MODEST_ACCOUNT_PROTOCOL (protocol),
3139                                                 pair->part,
3140                                                 pair->filename,
3141                                                 stream,
3142                                                 &written,
3143                                                 &error);
3144                         }
3145                 }
3146                 if (!decode_in_provider)
3147                         written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3148
3149                 if (written < 0) {
3150                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3151
3152                         if ((error->domain == TNY_ERROR_DOMAIN) && 
3153                             (error->code == TNY_IO_ERROR_WRITE) &&
3154                             (errno == ENOSPC)) {
3155                                 info->result = GNOME_VFS_ERROR_NO_SPACE;
3156                         } else {
3157                                 info->result = GNOME_VFS_ERROR_IO;
3158                         }
3159                 }
3160                 g_object_unref (G_OBJECT (stream));
3161         } else {
3162                 g_warning ("Could not create save attachment %s: %s\n", 
3163                            pair->filename, gnome_vfs_result_to_string (info->result));
3164         }
3165
3166         /* Go on saving remaining files */
3167         info->pairs = g_list_remove_link (info->pairs, info->pairs);
3168         if (info->pairs != NULL) {
3169                 save_mime_part_to_file (info);
3170         } else {
3171                 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3172         }
3173
3174         return NULL;
3175 }
3176
3177 static void
3178 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3179                                      SaveMimePartInfo *info)
3180 {
3181         gboolean is_ok = TRUE;
3182         gint replaced_files = 0;
3183         const GList *files = info->pairs;
3184         const GList *iter, *to_replace = NULL;
3185
3186         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3187                 SaveMimePartPair *pair = iter->data;
3188                 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3189
3190                 if (modest_utils_file_exists (unescaped)) {
3191                         replaced_files++;
3192                         if (replaced_files == 1)
3193                                 to_replace = iter;
3194                 }
3195                 g_free (unescaped);
3196         }
3197         if (replaced_files) {
3198                 gint response;
3199
3200                 if (replaced_files == 1) {
3201                         SaveMimePartPair *pair = to_replace->data;
3202                         const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3203                         gchar *escaped_basename, *message;
3204
3205                         escaped_basename = g_uri_unescape_string (basename, NULL);
3206                         message = g_strdup_printf ("%s\n%s",
3207                                                    _FM_REPLACE_FILE,
3208                                                    (escaped_basename) ? escaped_basename : "");
3209                         response = modest_platform_run_confirmation_dialog (parent, message);
3210                         g_free (message);
3211                         g_free (escaped_basename);
3212                 } else {
3213                         response = modest_platform_run_confirmation_dialog (parent,
3214                                                                             _FM_REPLACE_MULTIPLE);
3215                 }
3216                 if (response != GTK_RESPONSE_OK)
3217                         is_ok = FALSE;
3218         }
3219
3220         if (!is_ok) {
3221                 save_mime_part_info_free (info, TRUE);
3222         } else {
3223                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3224         }
3225
3226 }
3227
3228 typedef struct _SaveAttachmentsInfo {
3229         TnyList *attachments_list;
3230         ModestMsgViewWindow *window;
3231 } SaveAttachmentsInfo;
3232
3233 static void
3234 save_attachments_response (GtkDialog *dialog,
3235                            gint       arg1,
3236                            gpointer   user_data)  
3237 {
3238         TnyList *mime_parts;
3239         gchar *chooser_uri;
3240         GList *files_to_save = NULL;
3241         gchar *current_folder;
3242         SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3243
3244         mime_parts = TNY_LIST (sa_info->attachments_list);
3245
3246         if (arg1 != GTK_RESPONSE_OK)
3247                 goto end;
3248
3249         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3250         current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3251         if (current_folder && *current_folder != '\0') {
3252                 GError *err = NULL;
3253                 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3254                                         current_folder,&err);
3255                 if (err != NULL) {
3256                         g_debug ("Error storing latest used folder: %s", err->message);
3257                         g_error_free (err);
3258                 }
3259         }
3260         g_free (current_folder);
3261
3262         if (!modest_utils_folder_writable (chooser_uri)) {
3263                 const gchar *err_msg;
3264
3265 #ifdef MODEST_PLATFORM_MAEMO
3266                 if (modest_maemo_utils_in_usb_mode ()) {
3267                         err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3268                 } else {
3269                         err_msg = _FM_READ_ONLY_LOCATION;
3270                 }
3271 #else
3272                 err_msg = _FM_READ_ONLY_LOCATION;
3273 #endif
3274                 modest_platform_system_banner (NULL, NULL, err_msg);
3275         } else {
3276                 TnyIterator *iter;
3277
3278                 iter = tny_list_create_iterator (mime_parts);
3279                 while (!tny_iterator_is_done (iter)) {
3280                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3281
3282                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3283                             !tny_mime_part_is_purged (mime_part) &&
3284                             (tny_mime_part_get_filename (mime_part) != NULL)) {
3285                                 SaveMimePartPair *pair;
3286
3287                                 pair = g_slice_new0 (SaveMimePartPair);
3288
3289                                 if (tny_list_get_length (mime_parts) > 1) {
3290                                         gchar *escaped = 
3291                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3292                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3293                                         g_free (escaped);
3294                                 } else {
3295                                         pair->filename = g_strdup (chooser_uri);
3296                                 }
3297                                 pair->part = mime_part;
3298                                 files_to_save = g_list_prepend (files_to_save, pair);
3299                         }
3300                         tny_iterator_next (iter);
3301                 }
3302                 g_object_unref (iter);
3303         }
3304
3305         if (files_to_save != NULL) {
3306                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3307                 info->pairs = files_to_save;
3308                 info->result = TRUE;
3309                 info->uri = g_strdup (chooser_uri);
3310                 info->window = g_object_ref (sa_info->window);
3311                 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3312         }
3313         g_free (chooser_uri);
3314
3315  end:
3316         /* Free and close the dialog */
3317         g_object_unref (mime_parts);
3318         g_object_unref (sa_info->window);
3319         g_slice_free (SaveAttachmentsInfo, sa_info);
3320         gtk_widget_destroy (GTK_WIDGET (dialog));
3321 }
3322
3323 void
3324 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, 
3325                                          TnyList *mime_parts)
3326 {
3327         ModestMsgViewWindowPrivate *priv;
3328         GtkWidget *save_dialog = NULL;
3329         gchar *conf_folder = NULL;
3330         gchar *filename = NULL;
3331         gchar *save_multiple_str = NULL;
3332         const gchar *root_folder = "file:///";
3333
3334         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3335         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3336
3337         if (mime_parts == NULL) {
3338                 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3339                  * selection available */
3340                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3341                 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (window)), mime_parts, FALSE)) {
3342                         g_object_unref (mime_parts);
3343                         return;
3344                 }
3345                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3346                         if (mime_parts) {
3347                                 g_object_unref (mime_parts);
3348                                 mime_parts = NULL;
3349                         }
3350                         return;
3351                 }
3352         } else {
3353                 g_object_ref (mime_parts);
3354         }
3355
3356         /* prepare dialog */
3357         if (tny_list_get_length (mime_parts) == 1) {
3358                 TnyIterator *iter;
3359                 /* only one attachment selected */
3360                 iter = tny_list_create_iterator (mime_parts);
3361                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3362                 g_object_unref (iter);
3363                 if (!modest_tny_mime_part_is_msg (mime_part) && 
3364                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3365                     !tny_mime_part_is_purged (mime_part)) {
3366                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
3367                 } else {
3368                         /* TODO: show any error? */
3369                         g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3370                         g_object_unref (mime_parts);
3371                         return;
3372                 }
3373                 g_object_unref (mime_part);
3374         } else {
3375                 gint num = tny_list_get_length (mime_parts);
3376                 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3377                                                                "sfil_va_number_of_objects_attachment",
3378                                                               "sfil_va_number_of_objects_attachments",
3379                                                               num), num);
3380         }
3381
3382         /* Creation of hildon file chooser dialog for saving */
3383         save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3384                                                                          "",
3385                                                                          (GtkWindow *) window,
3386                                                                          GTK_FILE_CHOOSER_ACTION_SAVE);
3387
3388         /* Get last used folder */
3389         conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3390                                               MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3391
3392         /* File chooser stops working if we select "file:///" as current folder */
3393         if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3394                 g_free (conf_folder);
3395                 conf_folder = NULL;
3396         }
3397
3398         if (conf_folder && conf_folder[0] != '\0') {
3399                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3400         } else {
3401                 gchar *docs_folder;
3402                 /* Set the default folder to documents folder */
3403                 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3404                 if (!docs_folder) {
3405                         /* fallback */
3406                         docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3407                 }
3408                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3409                 g_free (docs_folder);
3410         }
3411         g_free (conf_folder);
3412
3413         /* set filename */
3414         if (filename) {
3415                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
3416                                                    filename);
3417                 g_free (filename);
3418         }
3419
3420         /* if multiple, set multiple string */
3421         if (save_multiple_str) {
3422                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3423                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3424                 g_free (save_multiple_str);
3425         }
3426
3427         /* We must run this asynchronously, because the hildon dialog
3428            performs a gtk_dialog_run by itself which leads to gdk
3429            deadlocks */
3430         SaveAttachmentsInfo *sa_info;
3431         sa_info = g_slice_new (SaveAttachmentsInfo);
3432         sa_info->attachments_list = mime_parts;
3433         sa_info->window = g_object_ref (window);
3434         g_signal_connect (save_dialog, "response", 
3435                           G_CALLBACK (save_attachments_response), sa_info);
3436
3437         gtk_widget_show_all (save_dialog);
3438 }
3439
3440 static gboolean
3441 show_remove_attachment_information (gpointer userdata)
3442 {
3443         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3444         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3445
3446         /* We're outside the main lock */
3447         gdk_threads_enter ();
3448
3449         if (priv->remove_attachment_banner != NULL) {
3450                 gtk_widget_destroy (priv->remove_attachment_banner);
3451                 g_object_unref (priv->remove_attachment_banner);
3452         }
3453
3454         priv->remove_attachment_banner = g_object_ref (
3455                 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3456
3457         gdk_threads_leave ();
3458
3459         return FALSE;
3460 }
3461
3462 void
3463 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3464 {
3465         ModestMsgViewWindowPrivate *priv;
3466         TnyList *mime_parts = NULL, *tmp;
3467         gchar *confirmation_message;
3468         gint response;
3469         gint n_attachments;
3470         TnyMsg *msg;
3471         TnyIterator *iter;
3472
3473         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3474         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3475
3476         /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3477          * because we don't have selection
3478          */
3479         mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3480
3481         /* Remove already purged messages from mime parts list. We use
3482            a copy of the list to remove items in the original one */
3483         tmp = tny_list_copy (mime_parts);
3484         iter = tny_list_create_iterator (tmp);
3485         while (!tny_iterator_is_done (iter)) {
3486                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3487                 if (tny_mime_part_is_purged (part))
3488                         tny_list_remove (mime_parts, (GObject *) part);
3489
3490                 g_object_unref (part);
3491                 tny_iterator_next (iter);
3492         }
3493         g_object_unref (tmp);
3494         g_object_unref (iter);
3495
3496         if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3497             tny_list_get_length (mime_parts) == 0) {
3498                 g_object_unref (mime_parts);
3499                 return;
3500         }
3501
3502         n_attachments = tny_list_get_length (mime_parts);
3503         if (n_attachments == 1) {
3504                 gchar *filename;
3505                 TnyMimePart *part;
3506
3507                 iter = tny_list_create_iterator (mime_parts);
3508                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3509                 g_object_unref (iter);
3510                 if (modest_tny_mime_part_is_msg (part)) {
3511                         TnyHeader *header;
3512                         header = tny_msg_get_header (TNY_MSG (part));
3513                         filename = tny_header_dup_subject (header);
3514                         g_object_unref (header);
3515                         if (filename == NULL)
3516                                 filename = g_strdup (_("mail_va_no_subject"));
3517                 } else {
3518                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3519                 }
3520                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3521                 g_free (filename);
3522                 g_object_unref (part);
3523         } else {
3524                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
3525                                                                  "mcen_nc_purge_files_text", 
3526                                                                  n_attachments), n_attachments);
3527         }
3528         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3529                                                             confirmation_message);
3530         g_free (confirmation_message);
3531
3532         if (response != GTK_RESPONSE_OK) {
3533                 g_object_unref (mime_parts);
3534                 return;
3535         }
3536
3537         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3538
3539         iter = tny_list_create_iterator (mime_parts);
3540         while (!tny_iterator_is_done (iter)) {
3541                 TnyMimePart *part;
3542
3543                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3544                 tny_mime_part_set_purged (TNY_MIME_PART (part));
3545                 g_object_unref (part);
3546                 tny_iterator_next (iter);
3547         }
3548         g_object_unref (iter);
3549
3550         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3551         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3552         tny_msg_rewrite_cache (msg);
3553         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3554         g_object_unref (msg);
3555         update_branding (MODEST_MSG_VIEW_WINDOW (window));
3556
3557         g_object_unref (mime_parts);
3558
3559         if (priv->purge_timeout > 0) {
3560                 g_source_remove (priv->purge_timeout);
3561                 priv->purge_timeout = 0;
3562         }
3563
3564         if (priv->remove_attachment_banner) {
3565                 gtk_widget_destroy (priv->remove_attachment_banner);
3566                 g_object_unref (priv->remove_attachment_banner);
3567                 priv->remove_attachment_banner = NULL;
3568         }
3569 }
3570
3571
3572 static void
3573 update_window_title (ModestMsgViewWindow *window)
3574 {
3575         ModestMsgViewWindowPrivate *priv;
3576         TnyMsg *msg = NULL;
3577         TnyHeader *header = NULL;
3578         gchar *subject = NULL;
3579
3580         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3581
3582         /* Note that if the window is closed while we're retrieving
3583            the message, this widget could de deleted */
3584         if (!priv->msg_view)
3585                 return;
3586
3587         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3588
3589         if (priv->other_body) {
3590                 gchar *description;
3591
3592                 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3593                 if (description) {
3594                         g_strstrip (description);
3595                         subject = description;
3596                 }
3597         } else if (msg != NULL) {
3598                 header = tny_msg_get_header (msg);
3599                 subject = tny_header_dup_subject (header);
3600                 g_object_unref (header);
3601                 g_object_unref (msg);
3602         }
3603
3604         if ((subject == NULL)||(subject[0] == '\0')) {
3605                 g_free (subject);
3606                 subject = g_strdup (_("mail_va_no_subject"));
3607         }
3608
3609         modest_window_set_title (MODEST_WINDOW (window), subject);
3610 }
3611
3612
3613 static void
3614 on_move_focus (GtkWidget *widget,
3615                GtkDirectionType direction,
3616                gpointer userdata)
3617 {
3618         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3619 }
3620
3621 static TnyStream *
3622 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3623 {
3624         GnomeVFSResult result;
3625         GnomeVFSHandle *handle = NULL;
3626         GnomeVFSFileInfo *info = NULL;
3627         TnyStream *stream;
3628
3629         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3630         if (result != GNOME_VFS_OK) {
3631                 *expected_size = 0;
3632                 return NULL;
3633         }
3634         
3635         info = gnome_vfs_file_info_new ();
3636         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3637         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3638                 /* We put a "safe" default size for going to cache */
3639                 *expected_size = (300*1024);
3640         } else {
3641                 *expected_size = info->size;
3642         }
3643         gnome_vfs_file_info_unref (info);
3644
3645         stream = tny_vfs_stream_new (handle);
3646
3647         return stream;
3648
3649 }
3650
3651 typedef struct {
3652         gchar *uri;
3653         gchar *cache_id;
3654         TnyStream *output_stream;
3655         GtkWidget *msg_view;
3656         GtkWidget *window;
3657 } FetchImageData;
3658
3659 gboolean
3660 on_fetch_image_idle_refresh_view (gpointer userdata)
3661 {
3662
3663         FetchImageData *fidata = (FetchImageData *) userdata;
3664
3665         gdk_threads_enter ();
3666         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3667                 ModestMsgViewWindowPrivate *priv;
3668
3669                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3670                 priv->fetching_images--;
3671                 gtk_widget_queue_draw (fidata->msg_view);
3672                 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3673         }
3674         gdk_threads_leave ();
3675
3676         g_object_unref (fidata->msg_view);
3677         g_object_unref (fidata->window);
3678         g_slice_free (FetchImageData, fidata);
3679         return FALSE;
3680 }
3681
3682 static gpointer
3683 on_fetch_image_thread (gpointer userdata)
3684 {
3685         FetchImageData *fidata = (FetchImageData *) userdata;
3686         TnyStreamCache *cache;
3687         TnyStream *cache_stream;
3688
3689         cache = modest_runtime_get_images_cache ();
3690         cache_stream = 
3691                 tny_stream_cache_get_stream (cache, 
3692                                              fidata->cache_id, 
3693                                              (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, 
3694                                              (gpointer) fidata->uri);
3695         g_free (fidata->cache_id);
3696         g_free (fidata->uri);
3697
3698         if (cache_stream != NULL) {
3699                 char buffer[4096];
3700
3701                 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3702                         gssize nb_read;
3703
3704                         nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3705                         if (G_UNLIKELY (nb_read < 0)) {
3706                                 break;
3707                         } else if (G_LIKELY (nb_read > 0)) {
3708                                 gssize nb_written = 0;
3709
3710                                 while (G_UNLIKELY (nb_written < nb_read)) {
3711                                         gssize len;
3712
3713                                         len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3714                                                                 nb_read - nb_written);
3715                                         if (G_UNLIKELY (len < 0))
3716                                                 break;
3717                                         nb_written += len;
3718                                 }
3719                         }
3720                 }
3721                 tny_stream_close (cache_stream);
3722                 g_object_unref (cache_stream);
3723         }
3724
3725         tny_stream_close (fidata->output_stream);
3726         g_object_unref (fidata->output_stream);
3727
3728         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3729
3730         return NULL;
3731 }
3732
3733 static gboolean
3734 on_fetch_image (ModestMsgView *msgview,
3735                 const gchar *uri,
3736                 TnyStream *stream,
3737                 ModestMsgViewWindow *window)
3738 {
3739         const gchar *current_account;
3740         ModestMsgViewWindowPrivate *priv;
3741         FetchImageData *fidata;
3742
3743         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3744
3745         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3746
3747         fidata = g_slice_new0 (FetchImageData);
3748         fidata->msg_view = g_object_ref (msgview);
3749         fidata->window = g_object_ref (window);
3750         fidata->uri = g_strdup (uri);
3751         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3752         fidata->output_stream = g_object_ref (stream);
3753
3754         priv->fetching_images++;
3755         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3756                 g_object_unref (fidata->output_stream);
3757                 g_free (fidata->cache_id);
3758                 g_free (fidata->uri);
3759                 g_object_unref (fidata->msg_view);
3760                 g_slice_free (FetchImageData, fidata);
3761                 tny_stream_close (stream);
3762                 priv->fetching_images--;
3763                 update_progress_hint (window);
3764                 return FALSE;
3765         }
3766         update_progress_hint (window);
3767
3768         return TRUE;
3769 }
3770
3771 static void 
3772 setup_menu (ModestMsgViewWindow *self)
3773 {
3774         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3775
3776         /* Settings menu buttons */
3777         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3778                                    MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3779                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3780
3781         modest_window_add_to_menu (MODEST_WINDOW (self),
3782                                    dngettext(GETTEXT_PACKAGE,
3783                                              "mcen_me_move_message",
3784                                              "mcen_me_move_messages",
3785                                              1),
3786                                    NULL,
3787                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3788                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3789
3790         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3791                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3792                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3793
3794         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3795                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3796                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3797
3798         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3799                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3800                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3801         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3802                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3803                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3804
3805         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3806                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3807                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3808         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3809                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3810                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3811
3812         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3813                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3814                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3815 }
3816
3817 void
3818 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3819 {
3820         ModestMsgViewWindowPrivate *priv;
3821         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3822         GSList *recipients = NULL;
3823         TnyMsg *msg = NULL;
3824
3825         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3826         if (msg == NULL) {
3827                 TnyHeader *header;
3828
3829                 header = modest_msg_view_window_get_header (self);
3830                 if (header == NULL)
3831                         return;
3832                 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3833                 g_object_unref (header);
3834         } else {
3835                 recipients = modest_tny_msg_get_all_recipients_list (msg);
3836                 g_object_unref (msg);
3837         }
3838
3839         if (recipients) {
3840                 /* Offer the user to add recipients to the address book */
3841                 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3842                 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3843         }
3844 }
3845
3846 static gboolean 
3847 _modest_msg_view_window_map_event (GtkWidget *widget,
3848                                    GdkEvent *event,
3849                                    gpointer userdata)
3850 {
3851         ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3852
3853         update_progress_hint (self);
3854
3855         return FALSE;
3856 }
3857
3858 void
3859 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3860 {
3861         ModestMsgViewWindowPrivate *priv;
3862         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3863
3864         modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3865 }
3866
3867 gboolean 
3868 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3869 {
3870         ModestMsgViewWindowPrivate *priv;
3871         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3872
3873         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3874
3875         return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3876 }
3877
3878 void 
3879 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3880 {
3881         ModestMsgViewWindowPrivate *priv;
3882         const gchar *msg_uid;
3883         TnyHeader *header = NULL;
3884         TnyFolder *folder = NULL;
3885
3886         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3887
3888         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3889
3890         header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3891         if (!header)
3892                 return;
3893
3894         folder = tny_header_get_folder (header);
3895         g_object_unref (header);
3896
3897         if (!folder)
3898                 return;
3899
3900         msg_uid = modest_msg_view_window_get_message_uid (self);
3901         if (msg_uid) {
3902                 GtkTreeRowReference *row_reference;
3903
3904                 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3905                         row_reference = priv->row_reference;
3906                 } else {
3907                         row_reference = NULL;
3908                 }
3909                 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3910                         g_warning ("Shouldn't happen, trying to reload a message failed");
3911         }
3912
3913         g_object_unref (folder);
3914 }
3915
3916 static void
3917 update_branding (ModestMsgViewWindow *self)
3918 {
3919         const gchar *account; 
3920         const gchar *mailbox;
3921         ModestAccountMgr *mgr;
3922         ModestProtocol *protocol = NULL;
3923         gchar *service_name = NULL;
3924         const GdkPixbuf *service_icon = NULL;
3925         ModestMsgViewWindowPrivate *priv;
3926
3927         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3928
3929         account = modest_window_get_active_account (MODEST_WINDOW (self));
3930         mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3931
3932         mgr = modest_runtime_get_account_mgr ();
3933
3934         if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3935                 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3936                         service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3937                                                                                  account, mailbox);
3938                         service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3939                                                                                  account, mailbox, MODEST_ICON_SIZE_SMALL);
3940                 }
3941         }
3942
3943         modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3944         g_free (service_name);
3945 }
3946
3947 static void
3948 sync_flags (ModestMsgViewWindow *self)
3949 {
3950         TnyHeader *header = NULL;
3951
3952         header = modest_msg_view_window_get_header (self);
3953         if (!header) {
3954                 TnyMsg *msg = modest_msg_view_window_get_message (self);
3955                 if (msg) {
3956                         header = tny_msg_get_header (msg);
3957                         g_object_unref (msg);
3958                 }
3959         }
3960
3961         if (header) {
3962                 TnyFolder *folder = tny_header_get_folder (header);
3963
3964                 if (folder) {
3965                         ModestMailOperation *mail_op;
3966
3967                         /* Sync folder, we need this to save the seen flag */
3968                         mail_op = modest_mail_operation_new (NULL);
3969                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3970                                                          mail_op);
3971                         modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3972                         g_object_unref (mail_op);
3973                         g_object_unref (folder);
3974                 }
3975                 g_object_unref (header);
3976         }
3977 }