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