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