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