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