Fixes an infinite loop while saving BS mime parts
[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                           ModestWindow *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                         GtkWindow *toplevel;
1986
1987                         toplevel = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) window);
1988                         response = modest_platform_run_confirmation_dialog (toplevel, _("mcen_nc_get_msg"));
1989                         if (response == GTK_RESPONSE_CANCEL) {
1990                                 update_window_title (window);
1991                                 return FALSE;
1992                         }
1993
1994                         if (header) {
1995                                 folder = tny_header_get_folder (header);
1996                         }
1997                         info = g_slice_new (MsgReaderInfo);
1998                         info->msg_uid = g_strdup (msg_uid);
1999                         if (header)
2000                                 info->header = g_object_ref (header);
2001                         else
2002                                 info->header = NULL;    
2003                         if (folder)
2004                                 info->folder = g_object_ref (folder);
2005                         else
2006                                 info->folder = NULL;
2007                         if (row_reference) {
2008                                 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2009                         } else {
2010                                 info->row_reference = NULL;
2011                         }
2012
2013                         /* Offer the connection dialog if necessary */
2014                         modest_platform_connect_if_remote_and_perform ((ModestWindow *) window,
2015                                                                        TRUE,
2016                                                                        TNY_FOLDER_STORE (folder),
2017                                                                        message_reader_performer, 
2018                                                                        info);
2019                         if (folder)
2020                                 g_object_unref (folder);
2021                         return TRUE;
2022                 }
2023         }
2024
2025         if (header) {
2026                 folder = tny_header_get_folder (header);
2027         }
2028         if (folder)
2029                 account = tny_folder_get_account (folder);
2030
2031         info = g_slice_new (MsgReaderInfo);
2032         info->msg_uid = g_strdup (msg_uid);
2033         if (folder)
2034                 info->folder = g_object_ref (folder);
2035         else
2036                 info->folder = NULL;
2037         if (header)
2038                 info->header = g_object_ref (header);
2039         else
2040                 info->header = NULL;
2041         if (row_reference)
2042                 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2043         else
2044                 info->row_reference = NULL;
2045
2046         message_reader_performer (FALSE, NULL, (ModestWindow *) window, account, info);
2047         if (account)
2048                 g_object_unref (account);
2049         if (folder)
2050                 g_object_unref (folder);
2051
2052         return TRUE;
2053 }
2054
2055 gboolean
2056 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2057 {
2058         ModestMsgViewWindowPrivate *priv;
2059         GtkTreePath *path= NULL;
2060         GtkTreeIter tmp_iter;
2061         TnyHeader *header;
2062         gboolean retval = TRUE;
2063         GtkTreeRowReference *row_reference = NULL;
2064
2065         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2066         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2067
2068         if (!priv->row_reference)
2069                 return FALSE;
2070
2071         /* Update the next row reference if it's not valid. This could
2072            happen if for example the header which it was pointing to,
2073            was deleted. The best place to do it is in the row-deleted
2074            handler but the tinymail model do not work like the glib
2075            tree models and reports the deletion when the row is still
2076            there */
2077         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2078                 if (priv->next_row_reference) {
2079                         gtk_tree_row_reference_free (priv->next_row_reference);
2080                 }
2081                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2082                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2083                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2084                 } else {
2085                         priv->next_row_reference = NULL;
2086                 }
2087         }
2088         if (priv->next_row_reference)
2089                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2090         if (path == NULL)
2091                 return FALSE;
2092
2093         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2094
2095         gtk_tree_model_get_iter (priv->header_model,
2096                                  &tmp_iter,
2097                                  path);
2098         gtk_tree_path_free (path);
2099
2100         gtk_tree_model_get (priv->header_model, &tmp_iter, 
2101                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2102                             &header, -1);
2103         
2104         /* Read the message & show it */
2105         if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2106                 retval = FALSE;
2107         }
2108         gtk_tree_row_reference_free (row_reference);
2109
2110         /* Free */
2111         g_object_unref (header);
2112
2113         return retval;
2114 }
2115
2116 gboolean        
2117 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2118 {
2119         ModestMsgViewWindowPrivate *priv = NULL;
2120         GtkTreePath *path;
2121         gboolean finished = FALSE;
2122         gboolean retval = FALSE;
2123
2124         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2125         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2126
2127         if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2128                 gtk_tree_row_reference_free (priv->row_reference);
2129                 priv->row_reference = NULL;
2130         }
2131
2132         /* Return inmediatly if there is no header model */
2133         if (!priv->header_model || !priv->row_reference)
2134                 return FALSE;
2135
2136         path = gtk_tree_row_reference_get_path (priv->row_reference);
2137         while (!finished && gtk_tree_path_prev (path)) {
2138                 TnyHeader *header;
2139                 GtkTreeIter iter;
2140
2141                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2142                 gtk_tree_model_get (priv->header_model, &iter, 
2143                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2144                                     &header, -1);
2145                 finished = TRUE;
2146                 if (header) {
2147                         if (msg_is_visible (header, priv->is_outbox)) {
2148                                 GtkTreeRowReference *row_reference;
2149                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2150                                 /* Read the message & show it */
2151                                 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2152                                 gtk_tree_row_reference_free (row_reference);
2153                         } else {
2154                                 finished = FALSE;
2155                         }
2156                         g_object_unref (header);
2157                 }
2158         }
2159
2160         gtk_tree_path_free (path);
2161         return retval;
2162 }
2163
2164 static void
2165 view_msg_cb (ModestMailOperation *mail_op, 
2166              TnyHeader *header, 
2167              gboolean canceled,
2168              TnyMsg *msg, 
2169              GError *error,
2170              gpointer user_data)
2171 {
2172         ModestMsgViewWindow *self = NULL;
2173         ModestMsgViewWindowPrivate *priv = NULL;
2174         GtkTreeRowReference *row_reference = NULL;
2175
2176         /* Unregister the header (it was registered before creating the mail operation) */
2177         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2178
2179         row_reference = (GtkTreeRowReference *) user_data;
2180         if (canceled) {
2181                 if (row_reference)
2182                         gtk_tree_row_reference_free (row_reference);
2183                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2184                 if (self) {
2185                         /* Restore window title */
2186                         update_window_title (self);
2187                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2188                         g_object_unref (self);
2189                 }
2190                 return;
2191         }
2192
2193         /* If there was any error */
2194         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2195                 if (row_reference)
2196                         gtk_tree_row_reference_free (row_reference);
2197                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2198                 if (self) {
2199                         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2200                         /* First we check if the parent is a folder window */
2201                         if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2202                                 gboolean is_merge;
2203                                 TnyAccount *account = NULL;
2204                                 GtkWidget *header_window = NULL;
2205
2206                                 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2207
2208                                 /* Get the account */
2209                                 if (!is_merge)
2210                                         account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2211                                                                                   priv->msg_uid);
2212
2213                                 if (is_merge || account) {
2214                                         TnyFolder *folder = NULL;
2215
2216                                         /* Try to get the message, if it's already downloaded
2217                                            we don't need to connect */
2218                                         if (account) {
2219                                                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), 
2220                                                                                                        priv->msg_uid);
2221                                         } else {
2222                                                 ModestTnyAccountStore *account_store;
2223                                                 ModestTnyLocalFoldersAccount *local_folders_account;
2224
2225                                                 account_store = modest_runtime_get_account_store ();
2226                                                 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2227                                                         modest_tny_account_store_get_local_folders_account (account_store));
2228                                                 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2229                                                 g_object_unref (local_folders_account);
2230                                         }
2231                                         if (account) g_object_unref (account);
2232
2233                                         if (folder) {
2234                                                 header_window = (GtkWidget *)
2235                                                         modest_header_window_new (
2236                                                                 folder, 
2237                                                                 modest_window_get_active_account (MODEST_WINDOW (self)), 
2238                                                                 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2239                                                 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2240                                                                                         MODEST_WINDOW (header_window),
2241                                                                                         NULL)) {
2242                                                         gtk_widget_destroy (GTK_WIDGET (header_window));
2243                                                 } else {
2244                                                         gtk_widget_show_all (GTK_WIDGET (header_window));
2245                                                 }
2246                                                 g_object_unref (folder);
2247                                         }
2248                                 }
2249                         }
2250
2251
2252                         /* Restore window title */
2253                         update_window_title (self);
2254                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2255                         g_object_unref (self);
2256                 }
2257                 return;
2258         }
2259
2260         /* Get the window */ 
2261         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2262         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2263         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2264
2265         /* Update the row reference */
2266         if (priv->row_reference != NULL) {
2267                 gtk_tree_row_reference_free (priv->row_reference);
2268                 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2269                 if (priv->next_row_reference != NULL) {
2270                         gtk_tree_row_reference_free (priv->next_row_reference);
2271                 }
2272                 if (priv->row_reference) {
2273                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2274                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2275                 } else {
2276                         priv->next_row_reference = NULL;
2277                 }
2278         }
2279
2280         /* Mark header as read */
2281         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2282                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2283
2284         /* Set new message */
2285         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2286                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2287                 modest_msg_view_window_update_priority (self);
2288                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2289                 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2290                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2291         }
2292
2293         /* Set the new message uid of the window  */
2294         if (priv->msg_uid) {
2295                 g_free (priv->msg_uid);
2296                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2297         }
2298
2299         /* Notify the observers */
2300         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2301                        0, priv->header_model, priv->row_reference);
2302
2303         /* Sync the flags if the message is not opened from a header
2304            model, i.e, if it's opened from a notification */
2305         if (!priv->header_model)
2306                 sync_flags (self);
2307
2308         /* Frees */
2309         g_object_unref (self);
2310         if (row_reference)
2311                 gtk_tree_row_reference_free (row_reference);
2312 }
2313
2314 TnyFolderType
2315 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2316 {
2317         ModestMsgViewWindowPrivate *priv;
2318         TnyMsg *msg;
2319         TnyFolderType folder_type;
2320
2321         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2322
2323         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2324
2325         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2326         if (msg) {
2327                 TnyFolder *folder;
2328
2329                 folder = tny_msg_get_folder (msg);
2330                 if (folder) {
2331                         folder_type = modest_tny_folder_guess_folder_type (folder);
2332                         g_object_unref (folder);
2333                 }
2334                 g_object_unref (msg);
2335         }
2336
2337         return folder_type;
2338 }
2339
2340
2341 static void
2342 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2343 {
2344         ModestMsgViewWindowPrivate *priv;
2345         TnyHeader *header = NULL;
2346         TnyHeaderFlags flags = 0;
2347
2348         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2349
2350         if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2351                 GtkTreeIter iter;
2352                 GtkTreePath *path = NULL;
2353
2354                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2355                 g_return_if_fail (path != NULL);
2356                 gtk_tree_model_get_iter (priv->header_model, 
2357                                          &iter, 
2358                                          gtk_tree_row_reference_get_path (priv->row_reference));
2359
2360                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2361                                     &header, -1);
2362                 gtk_tree_path_free (path);
2363         } else {
2364                 TnyMsg *msg;
2365                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2366                 if (msg) {
2367                         header = tny_msg_get_header (msg);
2368                         g_object_unref (msg);
2369                 }
2370         }
2371
2372         if (header) {
2373                 flags = tny_header_get_flags (header);
2374                 g_object_unref(G_OBJECT(header));
2375         }
2376
2377         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2378
2379 }
2380
2381 static void
2382 toolbar_resize (ModestMsgViewWindow *self)
2383 {
2384         ModestMsgViewWindowPrivate *priv = NULL;
2385         ModestWindowPrivate *parent_priv = NULL;
2386         GtkWidget *widget;
2387         gint static_button_size;
2388         ModestWindowMgr *mgr;
2389
2390         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2391         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2392         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2393
2394         mgr = modest_runtime_get_window_mgr ();
2395         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2396
2397         if (parent_priv->toolbar) {
2398                 /* Set expandable and homogeneous tool buttons */
2399                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2400                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2401                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2402                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2403                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2404                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2405                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2406                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2407                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2408                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2409                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2410                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2411                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2412                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2413                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2414                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2415                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2416                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2417                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2418         }
2419 }
2420
2421 static void
2422 modest_msg_view_window_show_toolbar (ModestWindow *self,
2423                                      gboolean show_toolbar)
2424 {
2425         ModestMsgViewWindowPrivate *priv = NULL;
2426         ModestWindowPrivate *parent_priv;
2427
2428         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2429         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2430
2431         /* Set optimized view status */
2432         priv->optimized_view = !show_toolbar;
2433
2434         if (!parent_priv->toolbar) {
2435                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2436                                                                   "/ToolBar");
2437
2438 #ifdef MODEST_TOOLKIT_HILDON2
2439                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2440 #else
2441                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2442 #endif
2443                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2444
2445                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2446                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2447                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2448
2449                 modest_window_add_toolbar (MODEST_WINDOW (self), 
2450                                            GTK_TOOLBAR (parent_priv->toolbar));
2451
2452         }
2453
2454         if (show_toolbar) {
2455                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2456                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2457                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2458
2459                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2460                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2461                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2462                 else
2463                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2464
2465         } else {
2466                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2467                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2468         }
2469 }
2470
2471 static void 
2472 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2473                                                GdkEvent *event,
2474                                                ModestMsgViewWindow *window)
2475 {
2476         if (!GTK_WIDGET_VISIBLE (window))
2477                 return;
2478
2479         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2480 }
2481
2482 gboolean 
2483 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2484 {
2485         ModestMsgViewWindowPrivate *priv;
2486         
2487         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2488         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2489
2490         return priv->progress_hint;
2491 }
2492
2493 static gboolean
2494 observers_empty (ModestMsgViewWindow *self)
2495 {
2496         GSList *tmp = NULL;
2497         ModestMsgViewWindowPrivate *priv;
2498         gboolean is_empty = TRUE;
2499         guint pending_ops = 0;
2500  
2501         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2502         tmp = priv->progress_widgets;
2503
2504         /* Check all observers */
2505         while (tmp && is_empty)  {
2506                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2507                 is_empty = pending_ops == 0;
2508                 
2509                 tmp = g_slist_next(tmp);
2510         }
2511         
2512         return is_empty;
2513 }
2514
2515 static void
2516 on_account_removed (TnyAccountStore *account_store, 
2517                     TnyAccount *account,
2518                     gpointer user_data)
2519 {
2520         /* Do nothing if it's a transport account, because we only
2521            show the messages of a store account */
2522         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2523                 const gchar *parent_acc = NULL;
2524                 const gchar *our_acc = NULL;
2525
2526                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2527                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2528
2529                 /* Close this window if I'm showing a message of the removed account */
2530                 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2531                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2532         }
2533 }
2534
2535 static void 
2536 on_mail_operation_started (ModestMailOperation *mail_op,
2537                            gpointer user_data)
2538 {
2539         ModestMsgViewWindow *self;
2540         ModestMailOperationTypeOperation op_type;
2541         GSList *tmp;
2542         ModestMsgViewWindowPrivate *priv;
2543         GObject *source = NULL;
2544
2545         self = MODEST_MSG_VIEW_WINDOW (user_data);
2546         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2547         op_type = modest_mail_operation_get_type_operation (mail_op);
2548         tmp = priv->progress_widgets;
2549         source = modest_mail_operation_get_source(mail_op);
2550         if (G_OBJECT (self) == source) {
2551                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2552                     op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2553                     op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2554                         set_progress_hint (self, TRUE);
2555                         while (tmp) {
2556                                 modest_progress_object_add_operation (
2557                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2558                                                 mail_op);
2559                                 tmp = g_slist_next (tmp);
2560                         }
2561                 }
2562         }
2563         g_object_unref (source);
2564
2565         /* Update dimming rules */
2566         check_dimming_rules_after_change (self);
2567 }
2568
2569 static void
2570 on_mail_operation_finished (ModestMailOperation *mail_op,
2571                             gpointer user_data)
2572 {
2573         ModestMsgViewWindow *self;
2574         ModestMailOperationTypeOperation op_type;
2575         GSList *tmp;
2576         ModestMsgViewWindowPrivate *priv;
2577
2578         self = MODEST_MSG_VIEW_WINDOW (user_data);
2579         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2580         op_type = modest_mail_operation_get_type_operation (mail_op);
2581         tmp = priv->progress_widgets;
2582
2583         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2584             op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2585             op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2586                 while (tmp) {
2587                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2588                                                                  mail_op);
2589                         tmp = g_slist_next (tmp);
2590                 }
2591
2592                 /* If no more operations are being observed, NORMAL mode is enabled again */
2593                 if (observers_empty (self)) {
2594                         set_progress_hint (self, FALSE);
2595                 }
2596         }
2597
2598         /* Update dimming rules. We have to do this right here
2599            and not in view_msg_cb because at that point the
2600            transfer mode is still enabled so the dimming rule
2601            won't let the user delete the message that has been
2602            readed for example */
2603         check_dimming_rules_after_change (self);
2604 }
2605
2606 static void
2607 on_queue_changed (ModestMailOperationQueue *queue,
2608                   ModestMailOperation *mail_op,
2609                   ModestMailOperationQueueNotification type,
2610                   ModestMsgViewWindow *self)
2611 {       
2612         ModestMsgViewWindowPrivate *priv;
2613
2614         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2615
2616         /* If this operations was created by another window, do nothing */
2617         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2618             return;
2619
2620         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2621                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2622                                                                G_OBJECT (mail_op),
2623                                                                "operation-started",
2624                                                                G_CALLBACK (on_mail_operation_started),
2625                                                                self);
2626                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2627                                                                G_OBJECT (mail_op),
2628                                                                "operation-finished",
2629                                                                G_CALLBACK (on_mail_operation_finished),
2630                                                                self);
2631         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2632                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2633                                                                   G_OBJECT (mail_op),
2634                                                                   "operation-started");
2635                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2636                                                                   G_OBJECT (mail_op),
2637                                                                   "operation-finished");
2638         }
2639 }
2640
2641 TnyList *
2642 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2643 {
2644         ModestMsgViewWindowPrivate *priv;
2645         TnyList *selected_attachments = NULL;
2646         
2647         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2648         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2649
2650         /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2651         selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2652         
2653         return selected_attachments;
2654 }
2655
2656 typedef struct {
2657         ModestMsgViewWindow *self;
2658         gchar *file_path;
2659         gchar *attachment_uid;
2660 } DecodeAsyncHelper;
2661
2662 static void
2663 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2664                                    gboolean cancelled, 
2665                                    TnyStream *stream, 
2666                                    GError *err, 
2667                                    gpointer user_data)
2668 {
2669         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2670         const gchar *content_type;
2671
2672         if (cancelled || err) {
2673                 if (err) {
2674                         gchar *msg;
2675                         if ((err->domain == TNY_ERROR_DOMAIN) && 
2676                             (err->code == TNY_IO_ERROR_WRITE) &&
2677                             (errno == ENOSPC)) {
2678                                 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2679                         } else {
2680                                 msg = g_strdup (_("mail_ib_file_operation_failed"));
2681                         }
2682                         modest_platform_information_banner (NULL, NULL, msg);
2683                         g_free (msg);
2684                 }
2685                 goto free;
2686         }
2687
2688         /* It could happen that the window was closed. So we
2689            assume it is a cancelation */
2690         if (!GTK_WIDGET_VISIBLE (helper->self))
2691                 goto free;
2692
2693         /* Remove the progress hint */
2694         set_progress_hint (helper->self, FALSE);
2695
2696         content_type = tny_mime_part_get_content_type (mime_part);
2697         if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2698                 ModestWindowMgr *mgr;
2699                 ModestWindow *msg_win = NULL;
2700                 TnyMsg * msg;
2701                 gchar *account;
2702                 const gchar *mailbox;
2703                 TnyStream *file_stream;
2704                 gint fd;
2705
2706                 fd = g_open (helper->file_path, O_RDONLY, 0644);
2707                 if (fd != -1) {
2708                         file_stream = tny_fs_stream_new (fd);
2709
2710                         mgr = modest_runtime_get_window_mgr ();
2711
2712                         account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2713                         mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2714
2715                         if (!account)
2716                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2717
2718                         msg = tny_camel_msg_new ();
2719                         tny_camel_msg_parse ((TnyCamelMsg *) msg, file_stream);
2720                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2721                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2722                                                 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2723                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2724                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2725                         else
2726                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2727                         g_object_unref (msg);
2728                         g_object_unref (file_stream);
2729                 } else {
2730                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2731                 }
2732
2733         } else {
2734
2735                 /* make the file read-only */
2736                 g_chmod(helper->file_path, 0444);
2737
2738                 /* Activate the file */
2739                 modest_platform_activate_file (helper->file_path, content_type);
2740         }
2741
2742  free:
2743         /* Frees */
2744         g_object_unref (helper->self);
2745         g_free (helper->file_path);
2746         g_free (helper->attachment_uid);
2747         g_slice_free (DecodeAsyncHelper, helper);
2748 }
2749
2750 static void
2751 view_attachment_connect_handler (gboolean canceled,
2752                                  GError *err,
2753                                  GtkWindow *parent_window,
2754                                  TnyAccount *account,
2755                                  TnyMimePart *part)
2756 {
2757
2758         if (canceled || err) {
2759                 g_object_unref (part);
2760                 return;
2761         }
2762
2763         modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2764                                                 part);
2765         g_object_unref (part);
2766 }
2767
2768 void
2769 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2770                                         TnyMimePart *mime_part)
2771 {
2772         ModestMsgViewWindowPrivate *priv;
2773         const gchar *msg_uid;
2774         gchar *attachment_uid = NULL;
2775         gint attachment_index = 0;
2776         TnyList *attachments;
2777
2778         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2779         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2780         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2781
2782         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2783         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2784         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2785         g_object_unref (attachments);
2786
2787         if (msg_uid && attachment_index >= 0) {
2788                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2789         }
2790
2791         if (mime_part == NULL) {
2792                 gboolean error = FALSE;
2793                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2794                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2795                         error = TRUE;
2796                 } else if (tny_list_get_length (selected_attachments) > 1) {
2797                         modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2798                         error = TRUE;
2799                 } else {
2800                         TnyIterator *iter;
2801                         iter = tny_list_create_iterator (selected_attachments);
2802                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2803                         g_object_unref (iter);
2804                 }
2805                 if (selected_attachments)
2806                         g_object_unref (selected_attachments);
2807
2808                 if (error)
2809                         goto frees;
2810         } else {
2811                 g_object_ref (mime_part);
2812         }
2813
2814         if (tny_mime_part_is_purged (mime_part))
2815                 goto frees;
2816
2817         if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2818             !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2819                 gboolean is_merge;
2820                 TnyAccount *account;
2821
2822                 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2823                 account = NULL;
2824                 /* Get the account */
2825                 if (!is_merge)
2826                         account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2827                                                                   priv->msg_uid);
2828
2829                 if (!tny_device_is_online (modest_runtime_get_device())) {
2830                         modest_platform_connect_and_perform ((ModestWindow *) window,
2831                                                              TRUE,
2832                                                              TNY_ACCOUNT (account),
2833                                                              (ModestConnectedPerformer) view_attachment_connect_handler,
2834                                                              g_object_ref (mime_part));
2835                         goto frees;
2836                 }
2837         }
2838
2839         if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2840                 gchar *filepath = NULL;
2841                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2842                 gboolean show_error_banner = FALSE;
2843                 TnyFsStream *temp_stream = NULL;
2844                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2845                                                                &filepath);
2846
2847                 if (temp_stream != NULL) {
2848                         ModestAccountMgr *mgr;
2849                         DecodeAsyncHelper *helper;
2850                         gboolean decode_in_provider;
2851                         ModestProtocol *protocol;
2852                         const gchar *account; 
2853
2854                         /* Activate progress hint */
2855                         set_progress_hint (window, TRUE);
2856
2857                         helper = g_slice_new0 (DecodeAsyncHelper);
2858                         helper->self = g_object_ref (window);
2859                         helper->file_path = g_strdup (filepath);
2860                         helper->attachment_uid = g_strdup (attachment_uid);
2861
2862                         decode_in_provider = FALSE;
2863                         mgr = modest_runtime_get_account_mgr ();
2864                         account = modest_window_get_active_account (MODEST_WINDOW (window));
2865                         if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2866                                 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2867                                         gchar *uri;
2868                                         uri = g_strconcat ("file://", filepath, NULL);
2869                                         decode_in_provider = 
2870                                                 modest_account_protocol_decode_part_to_stream_async (
2871                                                         MODEST_ACCOUNT_PROTOCOL (protocol),
2872                                                         mime_part,
2873                                                         filepath,
2874                                                         TNY_STREAM (temp_stream),
2875                                                         on_decode_to_stream_async_handler,
2876                                                         NULL,
2877                                                         helper);
2878                                         g_free (uri);
2879                                 }
2880                         }
2881
2882                         if (!decode_in_provider)
2883                                 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2884                                                                       on_decode_to_stream_async_handler,
2885                                                                       NULL,
2886                                                                       helper);
2887                         g_object_unref (temp_stream);
2888                         /* NOTE: files in the temporary area will be automatically
2889                          * cleaned after some time if they are no longer in use */
2890                 } else {
2891                         if (filepath) {
2892                                 const gchar *content_type;
2893                                 /* the file may already exist but it isn't writable,
2894                                  * let's try to open it anyway */
2895                                 content_type = tny_mime_part_get_content_type (mime_part);
2896                                 modest_platform_activate_file (filepath, content_type);
2897                         } else {
2898                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2899                                 show_error_banner = TRUE;
2900                         }
2901                 }
2902                 if (filepath)
2903                         g_free (filepath);
2904                 if (show_error_banner)
2905                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2906         } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2907                 ModestWindowMgr *mgr;
2908                 ModestWindow *msg_win = NULL;
2909                 TnyMsg *current_msg;
2910                 gboolean found;
2911                 TnyHeader *header;
2912
2913                 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2914                 mgr = modest_runtime_get_window_mgr ();
2915                 header = tny_msg_get_header (TNY_MSG (current_msg));
2916                 found = modest_window_mgr_find_registered_message_uid (mgr,
2917                                                                        attachment_uid,
2918                                                                        &msg_win);
2919                 
2920                 if (found) {
2921                         g_debug ("window for this body is already being created");
2922                 } else {
2923
2924                         /* it's not found, so create a new window for it */
2925                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2926                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2927                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2928                         if (!account)
2929                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2930                         
2931                         msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2932                                                                               account, mailbox, attachment_uid);
2933                         
2934                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2935                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2936                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2937                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2938                         else
2939                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2940                 }
2941                 g_object_unref (current_msg);           
2942         } else {
2943                 /* message attachment */
2944                 TnyHeader *header = NULL;
2945                 ModestWindowMgr *mgr;
2946                 ModestWindow *msg_win = NULL;
2947                 gboolean found;
2948
2949                 header = tny_msg_get_header (TNY_MSG (mime_part));
2950                 mgr = modest_runtime_get_window_mgr ();
2951                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2952
2953                 if (found) {
2954                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
2955                          * thus, we don't do anything */
2956                         g_debug ("window for is already being created");
2957                 } else {
2958                         /* it's not found, so create a new window for it */
2959                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2960                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2961                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2962                         if (!account)
2963                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2964                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, 
2965                                                                              mailbox, attachment_uid);
2966                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2967                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2968                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2969                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2970                         else
2971                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2972                 }
2973         }
2974
2975  frees:
2976         if (attachment_uid)
2977                 g_free (attachment_uid);
2978         if (mime_part)
2979                 g_object_unref (mime_part);
2980 }
2981
2982 typedef struct
2983 {
2984         gchar *filename;
2985         TnyMimePart *part;
2986 } SaveMimePartPair;
2987
2988 typedef struct
2989 {
2990         GList *pairs;
2991         GnomeVFSResult result;
2992         gchar *uri;
2993         ModestMsgViewWindow *window;
2994 } SaveMimePartInfo;
2995
2996 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2997 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2998 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2999 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3000
3001 static void
3002 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3003 {
3004         GList *node;
3005
3006         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3007                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3008                 g_free (pair->filename);
3009                 g_object_unref (pair->part);
3010                 g_slice_free (SaveMimePartPair, pair);
3011         }
3012         g_list_free (info->pairs);
3013         info->pairs = NULL;
3014         g_free (info->uri);
3015         g_object_unref (info->window);
3016         info->window = NULL;
3017         if (with_struct) {
3018                 g_slice_free (SaveMimePartInfo, info);
3019         }
3020 }
3021
3022 static gboolean
3023 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3024 {
3025         /* This is a GDK lock because we are an idle callback and
3026          * modest_platform_system_banner is or does Gtk+ code */
3027
3028         gdk_threads_enter (); /* CHECKED */
3029         if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3030                 /* nothing */
3031         } else if (info->result == GNOME_VFS_OK) {
3032                 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3033         } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3034                 gchar *msg = NULL;
3035
3036                 /* Check if the uri belongs to the external mmc */
3037                 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3038                         msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3039                 else
3040                         msg = g_strdup (_KR("cerm_memory_card_full"));
3041                 modest_platform_information_banner (NULL, NULL, msg);
3042                 g_free (msg);
3043         } else {
3044                 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3045         }
3046         save_mime_part_info_free (info, FALSE);
3047         gdk_threads_leave (); /* CHECKED */
3048
3049         return FALSE;
3050 }
3051
3052 static void
3053 save_mime_part_to_file_connect_handler (gboolean canceled,
3054                                         GError *err,
3055                                         GtkWindow *parent_window,
3056                                         TnyAccount *account,
3057                                         SaveMimePartInfo *info)
3058 {
3059         if (canceled || err) {
3060                 if (canceled && !err) {
3061                         info->result = GNOME_VFS_ERROR_CANCELLED;
3062                 }
3063                 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3064         } else {
3065                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3066         }
3067 }
3068
3069 static gboolean
3070 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3071 {
3072         gboolean is_merge;
3073         TnyAccount *account;
3074         ModestMsgViewWindowPrivate *priv;
3075
3076         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3077
3078         is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3079         account = NULL;
3080
3081         /* Get the account */
3082         if (!is_merge)
3083                 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3084                                                           priv->msg_uid);
3085
3086         modest_platform_connect_and_perform ((ModestWindow *) info->window,
3087                                              TRUE,
3088                                              TNY_ACCOUNT (account),
3089                                              (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3090                                              info);
3091
3092         if (account)
3093                 g_object_unref (account);
3094
3095         return FALSE;
3096 }
3097
3098 static gpointer
3099 save_mime_part_to_file (SaveMimePartInfo *info)
3100 {
3101         GnomeVFSHandle *handle;
3102         TnyStream *stream;
3103         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3104
3105         if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3106             !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3107                 gboolean check_online = TRUE;
3108                 ModestMsgViewWindowPrivate *priv = NULL;
3109
3110                 /* Check if we really need to connect to save the mime part */
3111                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3112                 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3113                         check_online = FALSE;
3114                 } else {
3115                         TnyAccountStore *acc_store;
3116                         TnyAccount *account = NULL;
3117
3118                         acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3119                         account = tny_account_store_find_account (acc_store, priv->msg_uid);
3120
3121                         if (account) {
3122                                 if (tny_account_get_connection_status (account) ==
3123                                     TNY_CONNECTION_STATUS_CONNECTED)
3124                                         check_online = FALSE;
3125                                 g_object_unref (account);
3126                         } else {
3127                                 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3128                         }
3129                 }
3130
3131                 if (check_online) {
3132                         g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3133                         return NULL;
3134                 }
3135         }
3136
3137         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3138         if (info->result == GNOME_VFS_OK) {
3139                 GError *error = NULL;
3140                 gboolean decode_in_provider;
3141                 gssize written;
3142                 ModestAccountMgr *mgr;
3143                 const gchar *account;
3144                 ModestProtocol *protocol = NULL;
3145
3146                 stream = tny_vfs_stream_new (handle);
3147
3148                 decode_in_provider = FALSE;
3149                 mgr = modest_runtime_get_account_mgr ();
3150                 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3151                 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3152                         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3153                                 decode_in_provider = 
3154                                         modest_account_protocol_decode_part_to_stream (
3155                                                 MODEST_ACCOUNT_PROTOCOL (protocol),
3156                                                 pair->part,
3157                                                 pair->filename,
3158                                                 stream,
3159                                                 &written,
3160                                                 &error);
3161                         }
3162                 }
3163                 if (!decode_in_provider)
3164                         written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3165
3166                 if (written < 0) {
3167                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3168
3169                         if ((error->domain == TNY_ERROR_DOMAIN) && 
3170                             (error->code == TNY_IO_ERROR_WRITE) &&
3171                             (errno == ENOSPC)) {
3172                                 info->result = GNOME_VFS_ERROR_NO_SPACE;
3173                         } else {
3174                                 info->result = GNOME_VFS_ERROR_IO;
3175                         }
3176                 }
3177                 g_object_unref (G_OBJECT (stream));
3178         } else {
3179                 g_warning ("Could not create save attachment %s: %s\n", 
3180                            pair->filename, gnome_vfs_result_to_string (info->result));
3181         }
3182
3183         /* Go on saving remaining files */
3184         info->pairs = g_list_remove_link (info->pairs, info->pairs);
3185         if (info->pairs != NULL) {
3186                 save_mime_part_to_file (info);
3187         } else {
3188                 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3189         }
3190
3191         return NULL;
3192 }
3193
3194 static void
3195 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3196                                      SaveMimePartInfo *info)
3197 {
3198         gboolean is_ok = TRUE;
3199         gint replaced_files = 0;
3200         const GList *files = info->pairs;
3201         const GList *iter, *to_replace = NULL;
3202
3203         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3204                 SaveMimePartPair *pair = iter->data;
3205                 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3206
3207                 if (modest_utils_file_exists (unescaped)) {
3208                         replaced_files++;
3209                         if (replaced_files == 1)
3210                                 to_replace = iter;
3211                 }
3212                 g_free (unescaped);
3213         }
3214         if (replaced_files) {
3215                 gint response;
3216
3217                 if (replaced_files == 1) {
3218                         SaveMimePartPair *pair = to_replace->data;
3219                         const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3220                         gchar *escaped_basename, *message;
3221
3222                         escaped_basename = g_uri_unescape_string (basename, NULL);
3223                         message = g_strdup_printf ("%s\n%s",
3224                                                    _FM_REPLACE_FILE,
3225                                                    (escaped_basename) ? escaped_basename : "");
3226                         response = modest_platform_run_confirmation_dialog (parent, message);
3227                         g_free (message);
3228                         g_free (escaped_basename);
3229                 } else {
3230                         response = modest_platform_run_confirmation_dialog (parent,
3231                                                                             _FM_REPLACE_MULTIPLE);
3232                 }
3233                 if (response != GTK_RESPONSE_OK)
3234                         is_ok = FALSE;
3235         }
3236
3237         if (!is_ok) {
3238                 save_mime_part_info_free (info, TRUE);
3239         } else {
3240                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3241         }
3242
3243 }
3244
3245 typedef struct _SaveAttachmentsInfo {
3246         TnyList *attachments_list;
3247         ModestMsgViewWindow *window;
3248 } SaveAttachmentsInfo;
3249
3250 static void
3251 save_attachments_response (GtkDialog *dialog,
3252                            gint       arg1,
3253                            gpointer   user_data)  
3254 {
3255         TnyList *mime_parts;
3256         gchar *chooser_uri;
3257         GList *files_to_save = NULL;
3258         gchar *current_folder;
3259         SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3260
3261         mime_parts = TNY_LIST (sa_info->attachments_list);
3262
3263         if (arg1 != GTK_RESPONSE_OK)
3264                 goto end;
3265
3266         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3267         current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3268         if (current_folder && *current_folder != '\0') {
3269                 GError *err = NULL;
3270                 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3271                                         current_folder,&err);
3272                 if (err != NULL) {
3273                         g_debug ("Error storing latest used folder: %s", err->message);
3274                         g_error_free (err);
3275                 }
3276         }
3277         g_free (current_folder);
3278
3279         if (!modest_utils_folder_writable (chooser_uri)) {
3280                 const gchar *err_msg;
3281
3282 #ifdef MODEST_PLATFORM_MAEMO
3283                 if (modest_maemo_utils_in_usb_mode ()) {
3284                         err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3285                 } else {
3286                         err_msg = _FM_READ_ONLY_LOCATION;
3287                 }
3288 #else
3289                 err_msg = _FM_READ_ONLY_LOCATION;
3290 #endif
3291                 modest_platform_system_banner (NULL, NULL, err_msg);
3292         } else {
3293                 TnyIterator *iter;
3294
3295                 iter = tny_list_create_iterator (mime_parts);
3296                 while (!tny_iterator_is_done (iter)) {
3297                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3298
3299                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3300                             !tny_mime_part_is_purged (mime_part) &&
3301                             (tny_mime_part_get_filename (mime_part) != NULL)) {
3302                                 SaveMimePartPair *pair;
3303
3304                                 pair = g_slice_new0 (SaveMimePartPair);
3305
3306                                 if (tny_list_get_length (mime_parts) > 1) {
3307                                         gchar *escaped = 
3308                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3309                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3310                                         g_free (escaped);
3311                                 } else {
3312                                         pair->filename = g_strdup (chooser_uri);
3313                                 }
3314                                 pair->part = mime_part;
3315                                 files_to_save = g_list_prepend (files_to_save, pair);
3316                         }
3317                         tny_iterator_next (iter);
3318                 }
3319                 g_object_unref (iter);
3320         }
3321
3322         if (files_to_save != NULL) {
3323                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3324                 info->pairs = files_to_save;
3325                 info->result = TRUE;
3326                 info->uri = g_strdup (chooser_uri);
3327                 info->window = g_object_ref (sa_info->window);
3328                 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3329         }
3330         g_free (chooser_uri);
3331
3332  end:
3333         /* Free and close the dialog */
3334         g_object_unref (mime_parts);
3335         g_object_unref (sa_info->window);
3336         g_slice_free (SaveAttachmentsInfo, sa_info);
3337         gtk_widget_destroy (GTK_WIDGET (dialog));
3338 }
3339
3340 void
3341 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, 
3342                                          TnyList *mime_parts)
3343 {
3344         ModestMsgViewWindowPrivate *priv;
3345         GtkWidget *save_dialog = NULL;
3346         gchar *conf_folder = NULL;
3347         gchar *filename = NULL;
3348         gchar *save_multiple_str = NULL;
3349         const gchar *root_folder = "file:///";
3350
3351         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3352         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3353
3354         if (mime_parts == NULL) {
3355                 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3356                  * selection available */
3357                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3358                 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))), mime_parts, FALSE)) {
3359                         g_object_unref (mime_parts);
3360                         return;
3361                 }
3362                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3363                         if (mime_parts) {
3364                                 g_object_unref (mime_parts);
3365                                 mime_parts = NULL;
3366                         }
3367                         return;
3368                 }
3369         } else {
3370                 g_object_ref (mime_parts);
3371         }
3372
3373         /* prepare dialog */
3374         if (tny_list_get_length (mime_parts) == 1) {
3375                 TnyIterator *iter;
3376                 /* only one attachment selected */
3377                 iter = tny_list_create_iterator (mime_parts);
3378                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3379                 g_object_unref (iter);
3380                 if (!modest_tny_mime_part_is_msg (mime_part) && 
3381                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3382                     !tny_mime_part_is_purged (mime_part)) {
3383                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
3384                 } else {
3385                         /* TODO: show any error? */
3386                         g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3387                         g_object_unref (mime_parts);
3388                         return;
3389                 }
3390                 g_object_unref (mime_part);
3391         } else {
3392                 gint num = tny_list_get_length (mime_parts);
3393                 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3394                                                                "sfil_va_number_of_objects_attachment",
3395                                                               "sfil_va_number_of_objects_attachments",
3396                                                               num), num);
3397         }
3398
3399         /* Creation of hildon file chooser dialog for saving */
3400         save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3401                                                                          "",
3402                                                                          (GtkWindow *) window,
3403                                                                          GTK_FILE_CHOOSER_ACTION_SAVE);
3404
3405         /* Get last used folder */
3406         conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3407                                               MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3408
3409         /* File chooser stops working if we select "file:///" as current folder */
3410         if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3411                 g_free (conf_folder);
3412                 conf_folder = NULL;
3413         }
3414
3415         if (conf_folder && conf_folder[0] != '\0') {
3416                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3417         } else {
3418                 gchar *docs_folder;
3419                 /* Set the default folder to documents folder */
3420                 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3421                 if (!docs_folder) {
3422                         /* fallback */
3423                         docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3424                 }
3425                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3426                 g_free (docs_folder);
3427         }
3428         g_free (conf_folder);
3429
3430         /* set filename */
3431         if (filename) {
3432                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
3433                                                    filename);
3434                 g_free (filename);
3435         }
3436
3437         /* if multiple, set multiple string */
3438         if (save_multiple_str) {
3439                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3440                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3441                 g_free (save_multiple_str);
3442         }
3443
3444         /* We must run this asynchronously, because the hildon dialog
3445            performs a gtk_dialog_run by itself which leads to gdk
3446            deadlocks */
3447         SaveAttachmentsInfo *sa_info;
3448         sa_info = g_slice_new (SaveAttachmentsInfo);
3449         sa_info->attachments_list = mime_parts;
3450         sa_info->window = g_object_ref (window);
3451         g_signal_connect (save_dialog, "response", 
3452                           G_CALLBACK (save_attachments_response), sa_info);
3453
3454         gtk_widget_show_all (save_dialog);
3455 }
3456
3457 static gboolean
3458 show_remove_attachment_information (gpointer userdata)
3459 {
3460         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3461         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3462
3463         /* We're outside the main lock */
3464         gdk_threads_enter ();
3465
3466         if (priv->remove_attachment_banner != NULL) {
3467                 gtk_widget_destroy (priv->remove_attachment_banner);
3468                 g_object_unref (priv->remove_attachment_banner);
3469         }
3470
3471         priv->remove_attachment_banner = g_object_ref (
3472                 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3473
3474         gdk_threads_leave ();
3475
3476         return FALSE;
3477 }
3478
3479 void
3480 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3481 {
3482         ModestMsgViewWindowPrivate *priv;
3483         TnyList *mime_parts = NULL, *tmp;
3484         gchar *confirmation_message;
3485         gint response;
3486         gint n_attachments;
3487         TnyMsg *msg;
3488         TnyIterator *iter;
3489
3490         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3491         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3492
3493 #ifdef MODEST_TOOLKIT_HILDON2
3494         /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3495          * because we don't have selection
3496          */
3497         mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3498
3499         /* Remove already purged messages from mime parts list. We use
3500            a copy of the list to remove items in the original one */
3501         tmp = tny_list_copy (mime_parts);
3502         iter = tny_list_create_iterator (tmp);
3503         while (!tny_iterator_is_done (iter)) {
3504                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3505                 if (tny_mime_part_is_purged (part))
3506                         tny_list_remove (mime_parts, (GObject *) part);
3507
3508                 g_object_unref (part);
3509                 tny_iterator_next (iter);
3510         }
3511         g_object_unref (tmp);
3512         g_object_unref (iter);
3513
3514         if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3515             tny_list_get_length (mime_parts) == 0) {
3516                 g_object_unref (mime_parts);
3517                 return;
3518         }
3519 #else
3520         /* In gtk we get only selected attachments for the operation.
3521          */
3522         mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
3523
3524         /* Remove already purged messages from mime parts list. We use
3525            a copy of the list to remove items in the original one */
3526         tmp = tny_list_copy (mime_parts);
3527         iter = tny_list_create_iterator (tmp);
3528         while (!tny_iterator_is_done (iter)) {
3529                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3530                 if (tny_mime_part_is_purged (part))
3531                         tny_list_remove (mime_parts, (GObject *) part);
3532
3533                 g_object_unref (part);
3534                 tny_iterator_next (iter);
3535         }
3536         g_object_unref (tmp);
3537         g_object_unref (iter);
3538
3539         if (tny_list_get_length (mime_parts) == 0) {
3540                 g_object_unref (mime_parts);
3541                 return;
3542         }
3543 #endif
3544
3545         n_attachments = tny_list_get_length (mime_parts);
3546         if (n_attachments == 1) {
3547                 gchar *filename;
3548                 TnyMimePart *part;
3549
3550                 iter = tny_list_create_iterator (mime_parts);
3551                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3552                 g_object_unref (iter);
3553                 if (modest_tny_mime_part_is_msg (part)) {
3554                         TnyHeader *header;
3555                         header = tny_msg_get_header (TNY_MSG (part));
3556                         filename = tny_header_dup_subject (header);
3557                         g_object_unref (header);
3558                         if (filename == NULL)
3559                                 filename = g_strdup (_("mail_va_no_subject"));
3560                 } else {
3561                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3562                 }
3563                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3564                 g_free (filename);
3565                 g_object_unref (part);
3566         } else {
3567                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
3568                                                                  "mcen_nc_purge_files_text", 
3569                                                                  n_attachments), n_attachments);
3570         }
3571         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))),
3572                                                             confirmation_message);
3573         g_free (confirmation_message);
3574
3575         if (response != GTK_RESPONSE_OK) {
3576                 g_object_unref (mime_parts);
3577                 return;
3578         }
3579
3580         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3581
3582         iter = tny_list_create_iterator (mime_parts);
3583         while (!tny_iterator_is_done (iter)) {
3584                 TnyMimePart *part;
3585
3586                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3587                 tny_mime_part_set_purged (TNY_MIME_PART (part));
3588                 g_object_unref (part);
3589                 tny_iterator_next (iter);
3590         }
3591         g_object_unref (iter);
3592
3593         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3594         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3595         tny_msg_rewrite_cache (msg);
3596         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3597         g_object_unref (msg);
3598         update_branding (MODEST_MSG_VIEW_WINDOW (window));
3599
3600         g_object_unref (mime_parts);
3601
3602         if (priv->purge_timeout > 0) {
3603                 g_source_remove (priv->purge_timeout);
3604                 priv->purge_timeout = 0;
3605         }
3606
3607         if (priv->remove_attachment_banner) {
3608                 gtk_widget_destroy (priv->remove_attachment_banner);
3609                 g_object_unref (priv->remove_attachment_banner);
3610                 priv->remove_attachment_banner = NULL;
3611         }
3612 }
3613
3614
3615 static void
3616 update_window_title (ModestMsgViewWindow *window)
3617 {
3618         ModestMsgViewWindowPrivate *priv;
3619         TnyMsg *msg = NULL;
3620         TnyHeader *header = NULL;
3621         gchar *subject = NULL;
3622
3623         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3624
3625         /* Note that if the window is closed while we're retrieving
3626            the message, this widget could de deleted */
3627         if (!priv->msg_view)
3628                 return;
3629
3630         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3631
3632         if (priv->other_body) {
3633                 gchar *description;
3634
3635                 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3636                 if (description) {
3637                         g_strstrip (description);
3638                         subject = description;
3639                 }
3640         } else if (msg != NULL) {
3641                 header = tny_msg_get_header (msg);
3642                 subject = tny_header_dup_subject (header);
3643                 g_object_unref (header);
3644                 g_object_unref (msg);
3645         }
3646
3647         if ((subject == NULL)||(subject[0] == '\0')) {
3648                 g_free (subject);
3649                 subject = g_strdup (_("mail_va_no_subject"));
3650         }
3651
3652         modest_window_set_title (MODEST_WINDOW (window), subject);
3653 }
3654
3655
3656 static void
3657 on_move_focus (GtkWidget *widget,
3658                GtkDirectionType direction,
3659                gpointer userdata)
3660 {
3661         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3662 }
3663
3664 static TnyStream *
3665 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3666 {
3667         GnomeVFSResult result;
3668         GnomeVFSHandle *handle = NULL;
3669         GnomeVFSFileInfo *info = NULL;
3670         TnyStream *stream;
3671
3672         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3673         if (result != GNOME_VFS_OK) {
3674                 *expected_size = 0;
3675                 return NULL;
3676         }
3677         
3678         info = gnome_vfs_file_info_new ();
3679         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3680         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3681                 /* We put a "safe" default size for going to cache */
3682                 *expected_size = (300*1024);
3683         } else {
3684                 *expected_size = info->size;
3685         }
3686         gnome_vfs_file_info_unref (info);
3687
3688         stream = tny_vfs_stream_new (handle);
3689
3690         return stream;
3691
3692 }
3693
3694 typedef struct {
3695         gchar *uri;
3696         gchar *cache_id;
3697         TnyStream *output_stream;
3698         GtkWidget *msg_view;
3699         GtkWidget *window;
3700 } FetchImageData;
3701
3702 gboolean
3703 on_fetch_image_timeout_refresh_view (gpointer userdata)
3704 {
3705         ModestMsgViewWindowPrivate *priv;
3706
3707         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3708         update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3709         /* Note that priv->msg_view is set to NULL when this window is
3710            distroyed */
3711         if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3712                 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3713         }
3714         priv->fetch_image_redraw_handler = 0;
3715         g_object_unref (userdata);
3716         return FALSE;
3717 }
3718
3719 gboolean
3720 on_fetch_image_idle_refresh_view (gpointer userdata)
3721 {
3722
3723         FetchImageData *fidata = (FetchImageData *) userdata;
3724
3725         gdk_threads_enter ();
3726         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3727                 ModestMsgViewWindowPrivate *priv;
3728
3729                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3730                 priv->fetching_images--;
3731                 if (priv->fetch_image_redraw_handler == 0) {
3732                         priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3733                 }
3734
3735         }
3736         gdk_threads_leave ();
3737
3738         g_object_unref (fidata->msg_view);
3739         g_object_unref (fidata->window);
3740         g_slice_free (FetchImageData, fidata);
3741         return FALSE;
3742 }
3743
3744 static gpointer
3745 on_fetch_image_thread (gpointer userdata)
3746 {
3747         FetchImageData *fidata = (FetchImageData *) userdata;
3748         TnyStreamCache *cache;
3749         TnyStream *cache_stream;
3750
3751         cache = modest_runtime_get_images_cache ();
3752         cache_stream = 
3753                 tny_stream_cache_get_stream (cache, 
3754                                              fidata->cache_id, 
3755                                              (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, 
3756                                              (gpointer) fidata->uri);
3757         g_free (fidata->cache_id);
3758         g_free (fidata->uri);
3759
3760         if (cache_stream != NULL) {
3761                 char buffer[4096];
3762
3763                 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3764                         gssize nb_read;
3765
3766                         nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3767                         if (G_UNLIKELY (nb_read < 0)) {
3768                                 break;
3769                         } else if (G_LIKELY (nb_read > 0)) {
3770                                 gssize nb_written = 0;
3771
3772                                 while (G_UNLIKELY (nb_written < nb_read)) {
3773                                         gssize len;
3774
3775                                         len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3776                                                                 nb_read - nb_written);
3777                                         if (G_UNLIKELY (len < 0))
3778                                                 break;
3779                                         nb_written += len;
3780                                 }
3781                         }
3782                 }
3783                 tny_stream_close (cache_stream);
3784                 g_object_unref (cache_stream);
3785         }
3786
3787         tny_stream_close (fidata->output_stream);
3788         g_object_unref (fidata->output_stream);
3789
3790         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3791
3792         return NULL;
3793 }
3794
3795 static gboolean
3796 on_fetch_image (ModestMsgView *msgview,
3797                 const gchar *uri,
3798                 TnyStream *stream,
3799                 ModestMsgViewWindow *window)
3800 {
3801         const gchar *current_account;
3802         ModestMsgViewWindowPrivate *priv;
3803         FetchImageData *fidata;
3804
3805         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3806
3807         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3808
3809         fidata = g_slice_new0 (FetchImageData);
3810         fidata->msg_view = g_object_ref (msgview);
3811         fidata->window = g_object_ref (window);
3812         fidata->uri = g_strdup (uri);
3813         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3814         fidata->output_stream = g_object_ref (stream);
3815
3816         priv->fetching_images++;
3817         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3818                 g_object_unref (fidata->output_stream);
3819                 g_free (fidata->cache_id);
3820                 g_free (fidata->uri);
3821                 g_object_unref (fidata->msg_view);
3822                 g_slice_free (FetchImageData, fidata);
3823                 tny_stream_close (stream);
3824                 priv->fetching_images--;
3825                 update_progress_hint (window);
3826                 return FALSE;
3827         }
3828         update_progress_hint (window);
3829
3830         return TRUE;
3831 }
3832
3833 static void 
3834 setup_menu (ModestMsgViewWindow *self)
3835 {
3836         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3837
3838         /* Settings menu buttons */
3839         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3840                                    MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3841                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3842
3843         modest_window_add_to_menu (MODEST_WINDOW (self),
3844                                    dngettext(GETTEXT_PACKAGE,
3845                                              "mcen_me_move_message",
3846                                              "mcen_me_move_messages",
3847                                              1),
3848                                    NULL,
3849                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3850                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3851
3852         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3853                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3854                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3855
3856         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3857                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3858                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3859
3860         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3861                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3862                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3863         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3864                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3865                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3866
3867         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3868                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3869                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3870         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3871                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3872                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3873
3874         modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3875                                    MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3876                                    MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3877 }
3878
3879 void
3880 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3881 {
3882         ModestMsgViewWindowPrivate *priv;
3883         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3884         GSList *recipients = NULL;
3885         TnyMsg *msg = NULL;
3886
3887         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3888         if (msg == NULL) {
3889                 TnyHeader *header;
3890
3891                 header = modest_msg_view_window_get_header (self);
3892                 if (header == NULL)
3893                         return;
3894                 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3895                 g_object_unref (header);
3896         } else {
3897                 recipients = modest_tny_msg_get_all_recipients_list (msg);
3898                 g_object_unref (msg);
3899         }
3900
3901         if (recipients) {
3902                 /* Offer the user to add recipients to the address book */
3903                 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3904                 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3905         }
3906 }
3907
3908 static gboolean 
3909 _modest_msg_view_window_map_event (GtkWidget *widget,
3910                                    GdkEvent *event,
3911                                    gpointer userdata)
3912 {
3913         ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3914
3915         update_progress_hint (self);
3916
3917         return FALSE;
3918 }
3919
3920 void
3921 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3922 {
3923         ModestMsgViewWindowPrivate *priv;
3924         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3925
3926         modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3927 }
3928
3929 gboolean 
3930 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3931 {
3932         ModestMsgViewWindowPrivate *priv;
3933         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3934
3935         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3936
3937         return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3938 }
3939
3940 void 
3941 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3942 {
3943         ModestMsgViewWindowPrivate *priv;
3944         const gchar *msg_uid;
3945         TnyHeader *header = NULL;
3946         TnyFolder *folder = NULL;
3947
3948         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3949
3950         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3951
3952         header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3953         if (!header)
3954                 return;
3955
3956         folder = tny_header_get_folder (header);
3957         g_object_unref (header);
3958
3959         if (!folder)
3960                 return;
3961
3962         msg_uid = modest_msg_view_window_get_message_uid (self);
3963         if (msg_uid) {
3964                 GtkTreeRowReference *row_reference;
3965
3966                 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3967                         row_reference = priv->row_reference;
3968                 } else {
3969                         row_reference = NULL;
3970                 }
3971                 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3972                         g_warning ("Shouldn't happen, trying to reload a message failed");
3973         }
3974
3975         g_object_unref (folder);
3976 }
3977
3978 static void
3979 update_branding (ModestMsgViewWindow *self)
3980 {
3981         const gchar *account; 
3982         const gchar *mailbox;
3983         ModestAccountMgr *mgr;
3984         ModestProtocol *protocol = NULL;
3985         gchar *service_name = NULL;
3986         const GdkPixbuf *service_icon = NULL;
3987         ModestMsgViewWindowPrivate *priv;
3988
3989         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3990
3991         account = modest_window_get_active_account (MODEST_WINDOW (self));
3992         mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3993
3994         mgr = modest_runtime_get_account_mgr ();
3995
3996         if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3997                 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3998                         service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3999                                                                                  account, mailbox);
4000                         service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4001                                                                                  account, mailbox, MODEST_ICON_SIZE_SMALL);
4002                 }
4003         }
4004
4005         modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4006         g_free (service_name);
4007 }
4008
4009 static void
4010 sync_flags (ModestMsgViewWindow *self)
4011 {
4012         TnyHeader *header = NULL;
4013
4014         header = modest_msg_view_window_get_header (self);
4015         if (!header) {
4016                 TnyMsg *msg = modest_msg_view_window_get_message (self);
4017                 if (msg) {
4018                         header = tny_msg_get_header (msg);
4019                         g_object_unref (msg);
4020                 }
4021         }
4022
4023         if (header) {
4024                 TnyFolder *folder = tny_header_get_folder (header);
4025
4026                 if (folder) {
4027                         ModestMailOperation *mail_op;
4028
4029                         /* Sync folder, we need this to save the seen flag */
4030                         mail_op = modest_mail_operation_new (NULL);
4031                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4032                                                          mail_op);
4033                         modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4034                         g_object_unref (mail_op);
4035                         g_object_unref (folder);
4036                 }
4037                 g_object_unref (header);
4038         }
4039 }