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