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