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