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