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