modest-ui-actions.c: some alignment in the code
[modest] / src / maemo / 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 "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-maemo-utils.h>
39 #include <modest-tny-msg.h>
40 #include <modest-msg-view-window.h>
41 #include <modest-attachments-view.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar-widget.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include <gtkhtml/gtkhtml-search.h>
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57
58 #define DEFAULT_FOLDER "MyDocs/.documents"
59
60 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
61 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
62 static void  modest_msg_view_window_finalize     (GObject *obj);
63 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
64                                                          gpointer data);
65 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
66                                                         ModestMsgViewWindow *obj);
67 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
68                                                         ModestMsgViewWindow *obj);
69
70 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
71 static void modest_msg_view_window_set_zoom (ModestWindow *window,
72                                              gdouble zoom);
73 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
74 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
75 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
76 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
77                                                           GdkEventKey *event,
78                                                           gpointer userdata);
79 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, 
80                                                            GdkEventWindowState *event, 
81                                                            gpointer userdata);
82 static void modest_msg_view_window_scroll_up (ModestWindow *window);
83 static void modest_msg_view_window_scroll_down (ModestWindow *window);
84 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
85
86 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
87                                                    gboolean show_toolbar);
88
89 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
90                                                            GdkEvent *event,
91                                                            ModestMsgViewWindow *window);
92 void modest_msg_view_window_on_row_changed(
93                 GtkTreeModel *header_model,
94                 GtkTreePath *arg1,
95                 GtkTreeIter *arg2,
96                 ModestMsgViewWindow *window);
97
98 void modest_msg_view_window_on_row_deleted(
99                 GtkTreeModel *header_model,
100                 GtkTreePath *arg1,
101                 ModestMsgViewWindow *window);
102
103 void modest_msg_view_window_on_row_inserted(
104                 GtkTreeModel *header_model,
105                 GtkTreePath *arg1,
106                 GtkTreeIter *arg2,
107                 ModestMsgViewWindow *window);
108
109 void modest_msg_view_window_on_row_reordered(
110                 GtkTreeModel *header_model,
111                 GtkTreePath *arg1,
112                 GtkTreeIter *arg2,
113                 gpointer arg3,
114                 ModestMsgViewWindow *window);
115
116 static void cancel_progressbar  (GtkToolButton *toolbutton,
117                                  ModestMsgViewWindow *self);
118
119 static void on_queue_changed    (ModestMailOperationQueue *queue,
120                                  ModestMailOperation *mail_op,
121                                  ModestMailOperationQueueNotification type,
122                                  ModestMsgViewWindow *self);
123
124 static void on_account_removed  (TnyAccountStore *account_store, 
125                                  TnyAccount *account,
126                                  gpointer user_data);
127
128 static void view_msg_cb         (ModestMailOperation *mail_op, 
129                                  TnyHeader *header, 
130                                  TnyMsg *msg, 
131                                  gpointer user_data);
132
133 static void set_toolbar_mode    (ModestMsgViewWindow *self, 
134                                  ModestToolBarModes mode);
135
136 static void update_window_title (ModestMsgViewWindow *window);
137
138 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
139
140
141 /* list my signals */
142 enum {
143         MSG_CHANGED_SIGNAL,
144         LAST_SIGNAL
145 };
146
147 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
148         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
149         { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
150 };
151
152 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
153         { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
154         { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
155         { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
156         { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
157         { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
158         { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
159 };
160
161 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
162 struct _ModestMsgViewWindowPrivate {
163
164         GtkWidget   *msg_view;
165         GtkWidget   *main_scroll;
166         GtkWidget   *find_toolbar;
167         gchar       *last_search;
168
169         /* Progress observers */
170         GtkWidget        *progress_bar;
171         GSList           *progress_widgets;
172
173         /* Tollbar items */
174         GtkWidget   *progress_toolitem;
175         GtkWidget   *cancel_toolitem;
176         GtkWidget   *prev_toolitem;
177         GtkWidget   *next_toolitem;
178         ModestToolBarModes current_toolbar_mode;
179
180         /* Optimized view enabled */
181         gboolean optimized_view;
182
183         /* A reference to the @model of the header view 
184          * to allow selecting previous/next messages,
185          * if the message is currently selected in the header view.
186          */
187         GtkTreeModel *header_model;
188         GtkTreeRowReference *row_reference;
189         GtkTreeRowReference *next_row_reference;
190
191         guint clipboard_change_handler;
192         guint queue_change_handler;
193         guint account_removed_handler;
194
195         guint purge_timeout;
196         GtkWidget *remove_attachment_banner;
197
198         guint progress_bar_timeout;
199
200         gchar *msg_uid;
201 };
202
203 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
204                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
205                                                     ModestMsgViewWindowPrivate))
206 /* globals */
207 static GtkWindowClass *parent_class = NULL;
208
209 /* uncomment the following if you have defined any signals */
210 static guint signals[LAST_SIGNAL] = {0};
211
212 GType
213 modest_msg_view_window_get_type (void)
214 {
215         static GType my_type = 0;
216         if (!my_type) {
217                 static const GTypeInfo my_info = {
218                         sizeof(ModestMsgViewWindowClass),
219                         NULL,           /* base init */
220                         NULL,           /* base finalize */
221                         (GClassInitFunc) modest_msg_view_window_class_init,
222                         NULL,           /* class finalize */
223                         NULL,           /* class data */
224                         sizeof(ModestMsgViewWindow),
225                         1,              /* n_preallocs */
226                         (GInstanceInitFunc) modest_msg_view_window_init,
227                         NULL
228                 };
229                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
230                                                   "ModestMsgViewWindow",
231                                                   &my_info, 0);
232         }
233         return my_type;
234 }
235
236 static void
237 save_state (ModestWindow *self)
238 {
239         modest_widget_memory_save (modest_runtime_get_conf (),
240                                    G_OBJECT(self), 
241                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
242 }
243
244
245 static void
246 restore_settings (ModestMsgViewWindow *self)
247 {
248         modest_widget_memory_restore (modest_runtime_get_conf (),
249                                       G_OBJECT(self), 
250                                       MODEST_CONF_MSG_VIEW_WINDOW_KEY);
251 }
252
253 static void
254 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
255 {
256         GObjectClass *gobject_class;
257         ModestWindowClass *modest_window_class;
258         gobject_class = (GObjectClass*) klass;
259         modest_window_class = (ModestWindowClass *) klass;
260
261         parent_class            = g_type_class_peek_parent (klass);
262         gobject_class->finalize = modest_msg_view_window_finalize;
263
264         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
265         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
266         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
267         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
268         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
269         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
270
271         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
272
273         modest_window_class->save_state_func = save_state;
274
275         signals[MSG_CHANGED_SIGNAL] =
276                 g_signal_new ("msg-changed",
277                               G_TYPE_FROM_CLASS (gobject_class),
278                               G_SIGNAL_RUN_FIRST,
279                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
280                               NULL, NULL,
281                               modest_marshal_VOID__POINTER_POINTER,
282                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
283 }
284
285 static void
286 modest_msg_view_window_init (ModestMsgViewWindow *obj)
287 {
288         ModestMsgViewWindowPrivate *priv;
289         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
290
291         priv->msg_view      = NULL;
292         priv->header_model  = NULL;
293         priv->clipboard_change_handler = 0;
294         priv->queue_change_handler = 0;
295         priv->account_removed_handler = 0;
296         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
297
298         priv->optimized_view  = FALSE;
299         priv->progress_bar_timeout = 0;
300         priv->purge_timeout = 0;
301         priv->remove_attachment_banner = NULL;
302         priv->msg_uid = NULL;
303 }
304
305
306 static gboolean
307 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
308 {
309         ModestMsgViewWindowPrivate *priv = NULL;
310         
311         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
312
313         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
314
315         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
316         
317         if (priv->progress_bar_timeout > 0) {
318                 g_source_remove (priv->progress_bar_timeout);
319                 priv->progress_bar_timeout = 0;
320         }
321         
322         return FALSE;
323 }
324
325 static void 
326 set_toolbar_mode (ModestMsgViewWindow *self, 
327                   ModestToolBarModes mode)
328 {
329         ModestWindowPrivate *parent_priv;
330         ModestMsgViewWindowPrivate *priv;
331 /*      GtkWidget *widget = NULL; */
332
333         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
334
335         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
336         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
337                         
338         /* Sets current toolbar mode */
339         priv->current_toolbar_mode = mode;
340
341         /* Update toolbar dimming state */
342         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
343
344         switch (mode) {
345         case TOOLBAR_MODE_NORMAL:               
346 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
347 /*              gtk_action_set_sensitive (widget, TRUE); */
348 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
349 /*              gtk_action_set_sensitive (widget, TRUE); */
350 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
351 /*              gtk_action_set_sensitive (widget, TRUE); */
352 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
353 /*              gtk_action_set_sensitive (widget, TRUE); */
354
355                 if (priv->prev_toolitem)
356                         gtk_widget_show (priv->prev_toolitem);
357                 
358                 if (priv->next_toolitem)
359                         gtk_widget_show (priv->next_toolitem);
360                         
361                 if (priv->progress_toolitem)
362                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
363                 if (priv->progress_bar)
364                         gtk_widget_hide (priv->progress_bar);
365                         
366                 if (priv->cancel_toolitem)
367                         gtk_widget_hide (priv->cancel_toolitem);
368
369                 /* Hide toolbar if optimized view is enabled */
370                 if (priv->optimized_view) {
371                         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
372                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
373                 }
374
375                 break;
376         case TOOLBAR_MODE_TRANSFER:
377 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
378 /*              gtk_action_set_sensitive (widget, FALSE); */
379 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
380 /*              gtk_action_set_sensitive (widget, FALSE); */
381 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
382 /*              gtk_action_set_sensitive (widget, FALSE); */
383 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
384 /*              gtk_action_set_sensitive (widget, FALSE); */
385
386                 if (priv->prev_toolitem)
387                         gtk_widget_hide (priv->prev_toolitem);
388                 
389                 if (priv->next_toolitem)
390                         gtk_widget_hide (priv->next_toolitem);
391                 
392                 if (priv->progress_toolitem)
393                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
394                 if (priv->progress_bar)
395                         gtk_widget_show (priv->progress_bar);
396                         
397                 if (priv->cancel_toolitem)
398                         gtk_widget_show (priv->cancel_toolitem);
399
400                 /* Show toolbar if it's hiden (optimized view ) */
401                 if (priv->optimized_view) {
402                         gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
403                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
404                 }
405
406                 break;
407         default:
408                 g_return_if_reached ();
409         }
410
411 }
412
413
414 static GtkWidget *
415 menubar_to_menu (GtkUIManager *ui_manager)
416 {
417         GtkWidget *main_menu;
418         GtkWidget *menubar;
419         GList *iter;
420
421         /* Create new main menu */
422         main_menu = gtk_menu_new();
423
424         /* Get the menubar from the UI manager */
425         menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
426
427         iter = gtk_container_get_children (GTK_CONTAINER (menubar));
428         while (iter) {
429                 GtkWidget *menu;
430
431                 menu = GTK_WIDGET (iter->data);
432                 gtk_widget_reparent(menu, main_menu);
433
434                 iter = g_list_next (iter);
435         }
436         return main_menu;
437 }
438
439 static void
440 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
441 {
442         GtkWidget *main_vbox;
443         ModestMsgViewWindowPrivate *priv;
444         ModestWindowPrivate *parent_priv;
445         
446         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
447         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
448
449         priv->msg_view = modest_msg_view_new (msg);
450         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
451         main_vbox = gtk_vbox_new  (FALSE, 6);
452
453         /* Menubar */
454         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
455         gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
456         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
457
458         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
459         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
460         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
461         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
462
463         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
464         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
465         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
466
467         priv->find_toolbar = hildon_find_toolbar_new (NULL);
468         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
469         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
470         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
471         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
472         
473         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);
474         gtk_widget_show_all (GTK_WIDGET(main_vbox));
475 }
476
477 static void
478 modest_msg_view_window_disconnect_signals (ModestWindow *self)
479 {
480         ModestMsgViewWindowPrivate *priv;
481
482         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
483
484         if (g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
485                                            priv->clipboard_change_handler)) 
486                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
487                                              priv->clipboard_change_handler);
488
489         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
490                                            priv->queue_change_handler))
491                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
492                                              priv->queue_change_handler);
493
494         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
495                                            priv->account_removed_handler))
496                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
497                                              priv->account_removed_handler);
498 }       
499
500 static void
501 modest_msg_view_window_finalize (GObject *obj)
502 {
503         ModestMsgViewWindowPrivate *priv;
504
505         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
506
507         /* Sanity check: shouldn't be needed, the window mgr should
508            call this function before */
509         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
510
511         if (priv->header_model != NULL) {
512                 g_object_unref (priv->header_model);
513                 priv->header_model = NULL;
514         }
515
516         if (priv->progress_bar_timeout > 0) {
517                 g_source_remove (priv->progress_bar_timeout);
518                 priv->progress_bar_timeout = 0;
519         }
520
521         if (priv->remove_attachment_banner) {
522                 gtk_widget_destroy (priv->remove_attachment_banner);
523                 g_object_unref (priv->remove_attachment_banner);
524                 priv->remove_attachment_banner = NULL;
525         }
526
527         if (priv->purge_timeout > 0) {
528                 g_source_remove (priv->purge_timeout);
529                 priv->purge_timeout = 0;
530         }
531
532         if (priv->row_reference) {
533                 gtk_tree_row_reference_free (priv->row_reference);
534                 priv->row_reference = NULL;
535         }
536
537         if (priv->next_row_reference) {
538                 gtk_tree_row_reference_free (priv->next_row_reference);
539                 priv->next_row_reference = NULL;
540         }
541
542         if (priv->msg_uid) {
543                 g_free (priv->msg_uid);
544                 priv->msg_uid = NULL;
545         }
546
547         G_OBJECT_CLASS(parent_class)->finalize (obj);
548 }
549
550 static gboolean
551 select_next_valid_row (GtkTreeModel *model,
552                        GtkTreeRowReference **row_reference,
553                        gboolean cycle)
554 {
555         GtkTreeIter tmp_iter;
556         GtkTreePath *path, *next;
557         gboolean retval = FALSE;
558
559         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
560
561         path = gtk_tree_row_reference_get_path (*row_reference);
562         gtk_tree_model_get_iter (model, &tmp_iter, path);
563         gtk_tree_row_reference_free (*row_reference);
564         *row_reference = NULL;
565
566         if (gtk_tree_model_iter_next (model, &tmp_iter)) {
567                 next = gtk_tree_model_get_path (model, &tmp_iter);
568                 *row_reference = gtk_tree_row_reference_new (model, next);
569                 retval = TRUE;
570         } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
571                 next = gtk_tree_model_get_path (model, &tmp_iter);
572
573                 /* Ensure that we are not selecting the same */
574                 if (gtk_tree_path_compare (path, next) != 0) {
575                         *row_reference = gtk_tree_row_reference_new (model, next);
576                         retval = TRUE;
577                 }
578         }
579
580         /* Free */
581         gtk_tree_path_free (path);
582
583         return retval;
584 }
585
586 ModestWindow *
587 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
588                                               const gchar *modest_account_name,
589                                               const gchar *msg_uid,
590                                               GtkTreeModel *model, 
591                                               GtkTreeRowReference *row_reference)
592 {
593         ModestMsgViewWindow *window = NULL;
594         ModestMsgViewWindowPrivate *priv = NULL;
595
596         window = MODEST_MSG_VIEW_WINDOW(modest_msg_view_window_new (msg, modest_account_name, msg_uid));
597         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
598
599         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
600
601         g_object_ref (model);
602         priv->header_model = model;
603         priv->row_reference = gtk_tree_row_reference_copy (row_reference);
604         priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
605         select_next_valid_row (model, &(priv->next_row_reference), TRUE);
606
607         g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
608                           G_CALLBACK (modest_msg_view_window_on_row_changed),
609                           window);
610         g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
611                           G_CALLBACK (modest_msg_view_window_on_row_deleted),
612                           window);
613         g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
614                           G_CALLBACK (modest_msg_view_window_on_row_inserted),
615                           window);
616         g_signal_connect (GTK_TREE_MODEL(model), "rows-reordered",
617                           G_CALLBACK (modest_msg_view_window_on_row_reordered),
618                           window);
619
620         modest_msg_view_window_update_priority (window);
621
622         /* Check toolbar dimming rules */
623         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
624
625         return MODEST_WINDOW(window);
626 }
627
628 ModestWindow *
629 modest_msg_view_window_new (TnyMsg *msg, 
630                             const gchar *modest_account_name,
631                             const gchar *msg_uid)
632 {
633         ModestMsgViewWindow *self = NULL;
634         GObject *obj = NULL;
635         ModestMsgViewWindowPrivate *priv = NULL;
636         ModestWindowPrivate *parent_priv = NULL;
637         ModestDimmingRulesGroup *menu_rules_group = NULL;
638         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
639         GtkActionGroup *action_group = NULL;
640         GError *error = NULL;
641         GdkPixbuf *window_icon;
642
643         g_return_val_if_fail (msg, NULL);
644         
645         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
646         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
647         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
648         self = MODEST_MSG_VIEW_WINDOW (obj);
649
650         priv->msg_uid = g_strdup (msg_uid);
651
652         parent_priv->ui_manager = gtk_ui_manager_new();
653         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
654
655         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
656         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
657
658         menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
659         toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
660
661         /* Add common actions */
662         gtk_action_group_add_actions (action_group,
663                                       modest_action_entries,
664                                       G_N_ELEMENTS (modest_action_entries),
665                                       obj);
666         gtk_action_group_add_toggle_actions (action_group,
667                                              modest_toggle_action_entries,
668                                              G_N_ELEMENTS (modest_toggle_action_entries),
669                                              obj);
670         gtk_action_group_add_toggle_actions (action_group,
671                                              msg_view_toggle_action_entries,
672                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
673                                              obj);
674         gtk_action_group_add_radio_actions (action_group,
675                                             msg_view_zoom_action_entries,
676                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
677                                             100,
678                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
679                                             obj);
680
681         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
682         g_object_unref (action_group);
683
684         /* Load the UI definition */
685         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
686                                          &error);
687         if (error) {
688                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
689                 g_error_free (error);
690                 error = NULL;
691         }
692         /* ****** */
693
694         /* Add common dimming rules */
695         modest_dimming_rules_group_add_rules (menu_rules_group, 
696                                               modest_msg_view_menu_dimming_entries,
697                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
698                                               self);
699         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
700                                               modest_msg_view_toolbar_dimming_entries,
701                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
702                                               self);
703
704         /* Insert dimming rules group for this window */
705         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
706         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
707         g_object_unref (menu_rules_group);
708         g_object_unref (toolbar_rules_group);
709
710         /* Add accelerators */
711         gtk_window_add_accel_group (GTK_WINDOW (obj), 
712                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
713         
714         /* Init window */
715         init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
716         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
717         
718         /* Set window icon */
719         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON); 
720         if (window_icon) {
721                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
722                 g_object_unref (window_icon);
723         }
724
725         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
726
727         g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
728                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
729         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
730                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
731         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
732                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
733         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
734                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
735         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
736                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
737
738         g_signal_connect (G_OBJECT (obj), "key-release-event",
739                           G_CALLBACK (modest_msg_view_window_key_release_event),
740                           NULL);
741
742         g_signal_connect (G_OBJECT (obj), "window-state-event",
743                           G_CALLBACK (modest_msg_view_window_window_state_event),
744                           NULL);
745
746         /* Mail Operation Queue */
747         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
748                                                        "queue-changed",
749                                                        G_CALLBACK (on_queue_changed),
750                                                        obj);
751
752         /* Account manager */
753         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
754                                                           "account_removed",
755                                                           G_CALLBACK(on_account_removed),
756                                                           obj);
757
758         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
759
760         priv->last_search = NULL;
761
762         /* Init the clipboard actions dim status */
763         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
764
765         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
766
767         /* Check toolbar dimming rules */
768         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
769
770         return MODEST_WINDOW(obj);
771 }
772
773 void modest_msg_view_window_on_row_changed(
774                 GtkTreeModel *header_model,
775                 GtkTreePath *arg1,
776                 GtkTreeIter *arg2,
777                 ModestMsgViewWindow *window){
778         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
779 }
780
781 void modest_msg_view_window_on_row_deleted(
782                 GtkTreeModel *header_model,
783                 GtkTreePath *arg1,
784                 ModestMsgViewWindow *window){
785         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
786 }
787
788 void modest_msg_view_window_on_row_inserted(
789                 GtkTreeModel *header_model,
790                 GtkTreePath *arg1,
791                 GtkTreeIter *arg2,
792                 ModestMsgViewWindow *window){
793         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
794 }
795
796 void modest_msg_view_window_on_row_reordered(
797                 GtkTreeModel *header_model,
798                 GtkTreePath *arg1,
799                 GtkTreeIter *arg2,
800                 gpointer arg3,
801                 ModestMsgViewWindow *window){
802         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
803 }
804
805 gboolean 
806 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
807 {
808         ModestMsgViewWindowPrivate *priv= NULL; 
809
810         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
811         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
812
813         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
814 }
815
816 TnyHeader*
817 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
818 {
819         ModestMsgViewWindowPrivate *priv= NULL; 
820         TnyMsg *msg = NULL;
821         TnyHeader *header = NULL;
822         GtkTreePath *path = NULL;
823         GtkTreeIter iter;
824
825         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
826         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
827
828         /* If the message ws not obtained from a treemodel,
829          * for instance if it was opened directly by the search UI:
830          */
831         if (priv->header_model == NULL) {
832                 msg = modest_msg_view_window_get_message (self);
833                 header = tny_msg_get_header (msg);
834                 g_object_unref (msg);
835                 return header;
836         }
837
838         /* Get iter of the currently selected message in the header view: */
839         /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
840          * instead of sometimes retrieving it from the header view?
841          * Then we wouldn't be dependent on the message actually still being selected 
842          * in the header view. murrayc. */
843         path = gtk_tree_row_reference_get_path (priv->row_reference);
844         g_return_val_if_fail (path != NULL, NULL);
845         gtk_tree_model_get_iter (priv->header_model, 
846                                  &iter, 
847                                  path);
848
849         /* Get current message header */
850         gtk_tree_model_get (priv->header_model, &iter, 
851                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
852                             &header, -1);
853
854         gtk_tree_path_free (path);
855         return header;
856 }
857
858 TnyMsg*
859 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
860 {
861         ModestMsgView *msg_view;
862         ModestMsgViewWindowPrivate *priv;
863
864         g_return_val_if_fail (self, NULL);
865
866         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
867
868         msg_view = MODEST_MSG_VIEW (priv->msg_view);
869
870         return modest_msg_view_get_message (msg_view);
871 }
872
873 const gchar*
874 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
875 {
876         ModestMsgViewWindowPrivate *priv;
877
878         g_return_val_if_fail (self, NULL);
879         
880         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
881
882         return (const gchar*) priv->msg_uid;
883 }
884
885 static void 
886 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
887                                             gpointer data)
888 {
889         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
890         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
891         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
892         gboolean is_active;
893         GtkAction *action;
894
895         is_active = gtk_toggle_action_get_active (toggle);
896
897         if (is_active) {
898                 gtk_widget_show (priv->find_toolbar);
899                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
900         } else {
901                 gtk_widget_hide (priv->find_toolbar);
902         }
903
904         /* update the toggle buttons status */
905         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
906         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
907         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
908         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
909         
910 }
911
912 static void
913 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
914                                            ModestMsgViewWindow *obj)
915 {
916         GtkToggleAction *toggle;
917         ModestWindowPrivate *parent_priv;
918         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
919         
920         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
921         gtk_toggle_action_set_active (toggle, FALSE);
922 }
923
924 static void
925 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
926                                            ModestMsgViewWindow *obj)
927 {
928         gchar *current_search;
929         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
930
931         if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
932                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
933                 return;
934         }
935
936         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
937
938         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
939                 g_free (current_search);
940                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
941                 return;
942         }
943
944         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
945                 gboolean result;
946                 g_free (priv->last_search);
947                 priv->last_search = g_strdup (current_search);
948                 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
949                                                  priv->last_search);
950                 if (!result) {
951                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
952                         g_free (priv->last_search);
953                         priv->last_search = NULL;
954                 } 
955         } else {
956                 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
957                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
958                         g_free (priv->last_search);
959                         priv->last_search = NULL;
960                 }
961         }
962         
963         g_free (current_search);
964                 
965 }
966
967 static void
968 modest_msg_view_window_set_zoom (ModestWindow *window,
969                                  gdouble zoom)
970 {
971         ModestMsgViewWindowPrivate *priv;
972      
973         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
974
975         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
976         modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
977 }
978
979 static gdouble
980 modest_msg_view_window_get_zoom (ModestWindow *window)
981 {
982         ModestMsgViewWindowPrivate *priv;
983      
984         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
985
986         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
987         return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
988 }
989
990 static gboolean
991 modest_msg_view_window_zoom_plus (ModestWindow *window)
992 {
993         ModestWindowPrivate *parent_priv;
994         GtkRadioAction *zoom_radio_action;
995         GSList *group, *node;
996
997         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
998         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
999                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1000
1001         group = gtk_radio_action_get_group (zoom_radio_action);
1002
1003         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1004                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1005                 return FALSE;
1006         }
1007
1008         for (node = group; node != NULL; node = g_slist_next (node)) {
1009                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1010                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1011                         return TRUE;
1012                 }
1013         }
1014         return FALSE;
1015 }
1016
1017 static gboolean
1018 modest_msg_view_window_zoom_minus (ModestWindow *window)
1019 {
1020         ModestWindowPrivate *parent_priv;
1021         GtkRadioAction *zoom_radio_action;
1022         GSList *group, *node;
1023
1024         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1025         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1026                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1027
1028         group = gtk_radio_action_get_group (zoom_radio_action);
1029
1030         for (node = group; node != NULL; node = g_slist_next (node)) {
1031                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1032                         if (node->next != NULL) {
1033                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1034                                 return TRUE;
1035                         } else {
1036                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1037                                 return FALSE;
1038                         }
1039                         break;
1040                 }
1041         }
1042         return FALSE;
1043 }
1044
1045 static gboolean
1046 modest_msg_view_window_key_release_event (GtkWidget *window,
1047                                           GdkEventKey *event,
1048                                           gpointer userdata)
1049 {
1050         if (event->type == GDK_KEY_RELEASE) {
1051                 switch (event->keyval) {
1052                 case GDK_Up:
1053                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1054                         return TRUE;
1055                         break;
1056                 case GDK_Down:
1057                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1058                         return TRUE;
1059                         break;
1060                 default:
1061                         return FALSE;
1062                         break;
1063                 };
1064         } else {
1065                 return FALSE;
1066         }
1067 }
1068
1069 static void
1070 modest_msg_view_window_scroll_up (ModestWindow *window)
1071 {
1072         ModestMsgViewWindowPrivate *priv;
1073         gboolean return_value;
1074
1075         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1076         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1077 }
1078
1079 static void
1080 modest_msg_view_window_scroll_down (ModestWindow *window)
1081 {
1082         ModestMsgViewWindowPrivate *priv;
1083         gboolean return_value;
1084
1085         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1086         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1087 }
1088
1089 gboolean
1090 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1091 {
1092         GtkTreePath *path;
1093         ModestMsgViewWindowPrivate *priv;
1094         GtkTreeIter tmp_iter;
1095         gboolean is_last_selected;
1096
1097         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1098         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1099
1100         /*if no model (so no rows at all), then virtually we are the last*/
1101         if (!priv->header_model)
1102                 return TRUE;
1103
1104         path = gtk_tree_row_reference_get_path (priv->row_reference);
1105         if (path == NULL)
1106                 return TRUE;
1107
1108         is_last_selected = TRUE;
1109         while (is_last_selected) {
1110                 TnyHeader *header;
1111                 gtk_tree_path_next (path);
1112                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1113                         break;
1114                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1115                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1116                                 &header, -1);
1117                 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1118                         is_last_selected = FALSE;
1119         }
1120         gtk_tree_path_free (path);
1121         return is_last_selected;
1122 }
1123
1124 gboolean
1125 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1126 {
1127         ModestMsgViewWindowPrivate *priv;
1128
1129         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1130         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1131
1132         return priv->header_model != NULL;
1133 }
1134
1135 gboolean
1136 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1137 {
1138         GtkTreePath *path;
1139         ModestMsgViewWindowPrivate *priv;
1140         gboolean is_first_selected;
1141         GtkTreeIter tmp_iter;
1142 /*      gchar * path_string;*/
1143
1144         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1145         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1146
1147         /*if no model (so no rows at all), then virtually we are the first*/
1148         if (!priv->header_model)
1149                 return TRUE;
1150
1151         path = gtk_tree_row_reference_get_path (priv->row_reference);
1152         if (!path)
1153                 return TRUE;
1154
1155 /*      path_string = gtk_tree_path_to_string (path);
1156         is_first_selected = strcmp (path_string, "0");
1157
1158         g_free (path_string);
1159         gtk_tree_path_free (path);
1160
1161         return is_first_selected;*/
1162
1163         is_first_selected = TRUE;
1164         while (is_first_selected) {
1165                 TnyHeader *header;
1166                 if(!gtk_tree_path_prev (path))
1167                         break;
1168                 /* Here the 'if' is needless for logic, but let make sure
1169                  * iter is valid for gtk_tree_model_get. */
1170                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1171                         break;
1172                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1173                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1174                                 &header, -1);
1175                 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1176                         is_first_selected = FALSE;
1177         }
1178         gtk_tree_path_free (path);
1179         return is_first_selected;
1180 }
1181
1182 /**
1183  * Reads the message whose summary item is @header. It takes care of
1184  * several things, among others:
1185  *
1186  * If the message was not previously downloaded then ask the user
1187  * before downloading. If there is no connection launch the connection
1188  * dialog. Update toolbar dimming rules.
1189  *
1190  * Returns: TRUE if the mail operation was started, otherwise if the
1191  * user do not want to download the message, or if the user do not
1192  * want to connect, then the operation is not issued
1193  **/
1194 static gboolean
1195 message_reader (ModestMsgViewWindow *window,
1196                 ModestMsgViewWindowPrivate *priv,
1197                 TnyHeader *header,
1198                 GtkTreePath *path)
1199 {
1200         ModestMailOperation *mail_op = NULL;
1201         ModestMailOperationTypeOperation op_type;
1202         gboolean already_showing = FALSE;
1203         ModestWindow *msg_window = NULL;
1204         ModestWindowMgr *mgr;
1205
1206         g_return_val_if_fail (path != NULL, FALSE);
1207
1208         mgr = modest_runtime_get_window_mgr ();
1209         already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1210         if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1211                 gboolean retval;
1212                 if (msg_window)
1213                         gtk_window_present (GTK_WINDOW (msg_window));
1214                 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1215                 return TRUE;
1216         }
1217
1218         /* Msg download completed */
1219         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1220                 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1221         } else {
1222                 TnyFolder *folder;
1223                 GtkResponseType response;
1224
1225                 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1226
1227                 /* Ask the user if he wants to download the message */
1228                 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1229                                                                     _("mcen_nc_get_msg"));
1230                 if (response == GTK_RESPONSE_CANCEL)
1231                         return FALSE;
1232                 
1233                 /* Offer the connection dialog if necessary */
1234                 /* FIXME: should this stuff go directly to the mail
1235                    operation instead of spread it all over the
1236                    code? */
1237                 folder = tny_header_get_folder (header);
1238                 if (folder) {
1239                         if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1240                                 g_object_unref (folder);
1241                                 return FALSE;
1242                         }
1243                         g_object_unref (folder);
1244                 }
1245         }
1246
1247         /* New mail operation */
1248         mail_op = modest_mail_operation_new_with_error_handling (op_type, 
1249                                                                  G_OBJECT(window),
1250                                                                  modest_ui_actions_get_msgs_full_error_handler, 
1251                                                                  NULL);
1252                                 
1253         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1254         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1255         g_object_unref (mail_op);
1256
1257         /* Update toolbar dimming rules */
1258         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1259
1260         return TRUE;
1261 }
1262
1263 gboolean        
1264 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1265 {
1266         ModestMsgViewWindowPrivate *priv;
1267         GtkTreePath *path= NULL;
1268         GtkTreeIter tmp_iter;
1269         TnyHeader *header;
1270         gboolean retval = TRUE;
1271
1272         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1273         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1274
1275         /* Update the next row reference if it's not valid. This could
1276            happen if for example the header which it was pointing to,
1277            was deleted. The best place to do it is in the row-deleted
1278            handler but the tinymail model do not work like the glib
1279            tree models and reports the deletion when the row is still
1280            there */
1281         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1282                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1283                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1284                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1285                 }
1286         }
1287         if (priv->next_row_reference)
1288                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1289         if (path == NULL)
1290                 return FALSE;
1291
1292         gtk_tree_model_get_iter (priv->header_model,
1293                                  &tmp_iter,
1294                                  path);
1295
1296         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1297                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1298                             &header, -1);
1299         
1300         /* Read the message & show it */
1301         if (!message_reader (window, priv, header, path)) {
1302                 retval = FALSE;
1303                 gtk_tree_path_free (path);
1304         }
1305
1306         /* Free */
1307         g_object_unref (header);
1308
1309         return retval;          
1310 }
1311
1312 gboolean 
1313 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1314 {
1315         ModestMsgViewWindowPrivate *priv = NULL;
1316         TnyHeader *header = NULL;
1317         GtkTreeIter iter;
1318         GtkTreePath *path;
1319
1320         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1321         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1322
1323         /* Check that the model is not empty */
1324         if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1325                 return FALSE;
1326
1327         /* Get the header */
1328         gtk_tree_model_get (priv->header_model, 
1329                             &iter, 
1330                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1331                             &header, -1);
1332         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1333         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1334                 g_object_unref (header);
1335                 return modest_msg_view_window_select_next_message (self);
1336         }
1337         
1338         path = gtk_tree_model_get_path (priv->header_model, &iter);
1339         
1340         /* Read the message & show it */
1341         message_reader (self, priv, header, path);
1342         
1343         /* Free */
1344         g_object_unref (header);
1345
1346         return TRUE;
1347 }
1348  
1349 gboolean        
1350 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1351 {
1352         ModestMsgViewWindowPrivate *priv = NULL;
1353         GtkTreePath *path;
1354
1355         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1356         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1357
1358         /* Return inmediatly if there is no header model */
1359         if (!priv->header_model)
1360                 return FALSE;
1361
1362         path = gtk_tree_row_reference_get_path (priv->row_reference);
1363         while (gtk_tree_path_prev (path)) {
1364                 TnyHeader *header;
1365                 GtkTreeIter iter;
1366
1367                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1368                 gtk_tree_model_get (priv->header_model, &iter, 
1369                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1370                                     &header, -1);
1371                 if (!header)
1372                         break;
1373                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1374                         g_object_unref (header);
1375                         continue;
1376                 }
1377
1378                 /* Read the message & show it */
1379                 if (!message_reader (window, priv, header, path)) {
1380                         g_object_unref (header);
1381                         break;
1382                 }
1383
1384                 g_object_unref (header);
1385
1386                 return TRUE;
1387         }
1388
1389         gtk_tree_path_free (path);
1390         return FALSE;
1391 }
1392
1393 static void
1394 view_msg_cb (ModestMailOperation *mail_op, 
1395              TnyHeader *header, 
1396              TnyMsg *msg, 
1397              gpointer user_data)
1398 {
1399         ModestMsgViewWindow *self = NULL;
1400         ModestMsgViewWindowPrivate *priv = NULL;
1401         GtkTreePath *path;
1402
1403         /* If there was any error */
1404         path = (GtkTreePath *) user_data;
1405         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1406                 gtk_tree_path_free (path);                      
1407                 return;
1408         }
1409
1410         /* Get the window */ 
1411         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1412         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1413         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1414
1415         /* Update the row reference */
1416         gtk_tree_row_reference_free (priv->row_reference);
1417         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1418         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1419         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1420         gtk_tree_path_free (path);
1421
1422         /* Mark header as read */
1423         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1424                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1425
1426         /* Set new message */
1427         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1428         modest_msg_view_window_update_priority (self);
1429         update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1430         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1431
1432         /* Set the new message uid of the window  */
1433         if (priv->msg_uid) {
1434                 g_free (priv->msg_uid);
1435                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1436         }
1437
1438         /* Notify the observers */
1439         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1440                        0, priv->header_model, priv->row_reference);
1441
1442         /* Free new references */
1443         g_object_unref (self);
1444 }
1445
1446 TnyFolderType
1447 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1448 {
1449         ModestMsgViewWindowPrivate *priv;
1450         TnyMsg *msg;
1451         TnyFolderType folder_type;
1452
1453         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1454
1455         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1456
1457         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1458         if (msg) {
1459                 TnyFolder *folder;
1460
1461                 folder = tny_msg_get_folder (msg);
1462                 
1463                 if (folder) {
1464                         folder_type = tny_folder_get_folder_type (folder);
1465                         
1466                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1467                                 const gchar *fname = tny_folder_get_name (folder);
1468                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1469                         }
1470
1471                         g_object_unref (folder);
1472                 }
1473                 g_object_unref (msg);
1474         }
1475
1476         return folder_type;
1477 }
1478
1479
1480 static void
1481 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1482 {
1483         ModestMsgViewWindowPrivate *priv;
1484         TnyHeaderFlags flags = 0;
1485
1486         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1487
1488         if (priv->header_model) {
1489                 TnyHeader *header;
1490                 GtkTreeIter iter;
1491                 GtkTreePath *path = NULL;
1492
1493                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1494                 g_return_if_fail (path != NULL);
1495                 gtk_tree_model_get_iter (priv->header_model, 
1496                                          &iter, 
1497                                          gtk_tree_row_reference_get_path (priv->row_reference));
1498
1499                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1500                                     &header, -1);
1501                 flags = tny_header_get_flags (header);
1502                 gtk_tree_path_free (path);
1503         }
1504
1505         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1506
1507 }
1508
1509 static gboolean
1510 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1511 {
1512         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1513                 ModestWindowPrivate *parent_priv;
1514                 ModestWindowMgr *mgr;
1515                 gboolean is_fullscreen;
1516                 GtkAction *fs_toggle_action;
1517                 gboolean active;
1518
1519                 mgr = modest_runtime_get_window_mgr ();
1520                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1521
1522                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1523                 
1524                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1525                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1526                 if (is_fullscreen != active) {
1527                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1528                 }
1529         }
1530
1531         return FALSE;
1532
1533 }
1534
1535 void
1536 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1537 {
1538                 ModestWindowPrivate *parent_priv;
1539                 GtkAction *fs_toggle_action;
1540                 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1541                 
1542                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1543                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1544                                               !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1545 }
1546
1547 static void
1548 set_homogeneous (GtkWidget *widget,
1549                  gpointer data)
1550 {
1551         if (GTK_IS_TOOL_ITEM (widget)) {
1552                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1553                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1554         }
1555 }
1556
1557 static void
1558 modest_msg_view_window_show_toolbar (ModestWindow *self,
1559                                      gboolean show_toolbar)
1560 {
1561         ModestMsgViewWindowPrivate *priv = NULL;
1562         ModestWindowPrivate *parent_priv;
1563         GtkWidget *reply_button = NULL, *menu = NULL;
1564         GtkWidget *placeholder = NULL;
1565         gint insert_index;
1566         
1567         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1568         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1569
1570         /* Set optimized view status */
1571         priv->optimized_view = !show_toolbar;
1572
1573         if (!parent_priv->toolbar) {
1574                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1575                                                                   "/ToolBar");
1576
1577                 /* Set homogeneous toolbar */
1578                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
1579                                        set_homogeneous, NULL);
1580
1581                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1582                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1583                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1584                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1585                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1586                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1587                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1588                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1589
1590                 /* Add ProgressBar (Transfer toolbar) */ 
1591                 priv->progress_bar = modest_progress_bar_widget_new ();
1592                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1593                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1594                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1595                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1596                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1597                 
1598                 /* Connect cancel 'clicked' signal to abort progress mode */
1599                 g_signal_connect(priv->cancel_toolitem, "clicked",
1600                                  G_CALLBACK(cancel_progressbar),
1601                                  self);
1602                 
1603                 /* Add it to the observers list */
1604                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1605
1606                 /* Add to window */
1607                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
1608                                            GTK_TOOLBAR (parent_priv->toolbar));
1609
1610
1611                 /* Set reply button tap and hold menu */        
1612                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1613                                                           "/ToolBar/ToolbarMessageReply");
1614                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1615                                                   "/ToolbarReplyCSM");
1616                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1617         }
1618
1619         if (show_toolbar) {
1620                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
1621                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1622                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1623                 
1624                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1625                 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);                   
1626                 
1627         } else {
1628                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1629                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1630         }
1631 }
1632
1633 static void 
1634 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1635                                                GdkEvent *event,
1636                                                ModestMsgViewWindow *window)
1637 {
1638         ModestWindowPrivate *parent_priv;
1639 /*      GtkAction *action; */
1640         gboolean is_address;
1641         gchar *selection;
1642         GtkWidget *focused;
1643
1644         if (!GTK_WIDGET_VISIBLE (window))
1645                 return;
1646
1647         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1648         selection = gtk_clipboard_wait_for_text (clipboard);
1649
1650         is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1651         
1652 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1653 /*      gtk_action_set_sensitive (action, is_address); */
1654
1655         focused = gtk_window_get_focus (GTK_WINDOW (window));
1656
1657 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1658 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1659
1660 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1661 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1662
1663         g_free (selection);
1664 /*      modest_msg_view_window_update_dimmed (window); */
1665         
1666 }
1667
1668 gboolean 
1669 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1670 {
1671         ModestMsgViewWindowPrivate *priv;
1672         
1673         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
1674         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1675
1676         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1677 }
1678
1679 static void
1680 cancel_progressbar (GtkToolButton *toolbutton,
1681                     ModestMsgViewWindow *self)
1682 {
1683         GSList *tmp;
1684         ModestMsgViewWindowPrivate *priv;
1685         
1686         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1687
1688         /* Get operation observers and cancel its current operation */
1689         tmp = priv->progress_widgets;
1690         while (tmp) {
1691                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1692                 tmp=g_slist_next(tmp);
1693         }
1694 }
1695 static gboolean
1696 observers_empty (ModestMsgViewWindow *self)
1697 {
1698         GSList *tmp = NULL;
1699         ModestMsgViewWindowPrivate *priv;
1700         gboolean is_empty = TRUE;
1701         guint pending_ops = 0;
1702  
1703         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1704         tmp = priv->progress_widgets;
1705
1706         /* Check all observers */
1707         while (tmp && is_empty)  {
1708                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
1709                 is_empty = pending_ops == 0;
1710                 
1711                 tmp = g_slist_next(tmp);
1712         }
1713         
1714         return is_empty;
1715 }
1716
1717 static void
1718 on_account_removed (TnyAccountStore *account_store, 
1719                     TnyAccount *account,
1720                     gpointer user_data)
1721 {
1722         /* Do nothing if it's a transport account, because we only
1723            show the messages of a store account */
1724         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
1725                 const gchar *parent_acc = NULL;
1726                 const gchar *our_acc = NULL;
1727
1728                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
1729                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1730
1731                 /* Close this window if I'm showing a message of the removed account */
1732                 if (strcmp (parent_acc, our_acc) == 0)
1733                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
1734         }
1735 }
1736
1737 static void
1738 on_queue_changed (ModestMailOperationQueue *queue,
1739                   ModestMailOperation *mail_op,
1740                   ModestMailOperationQueueNotification type,
1741                   ModestMsgViewWindow *self)
1742 {
1743         GSList *tmp;
1744         ModestMsgViewWindowPrivate *priv;
1745         ModestMailOperationTypeOperation op_type;
1746         ModestToolBarModes mode;
1747         
1748         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1749         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1750
1751         /* If this operations was created by another window, do nothing */
1752         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
1753             return;
1754
1755         /* Get toolbar mode from operation id*/
1756         op_type = modest_mail_operation_get_type_operation (mail_op);
1757         switch (op_type) {
1758 /*      case MODEST_MAIL_OPERATION_TYPE_SEND: */
1759         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
1760         case MODEST_MAIL_OPERATION_TYPE_OPEN:
1761                 mode = TOOLBAR_MODE_TRANSFER;
1762                 break;
1763         default:
1764                 mode = TOOLBAR_MODE_NORMAL;
1765                 
1766         }
1767                 
1768         /* Add operation observers and change toolbar if neccessary*/
1769         tmp = priv->progress_widgets;
1770         switch (type) {
1771         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
1772                 if (mode == TOOLBAR_MODE_TRANSFER) {
1773                         /* Enable transfer toolbar mode */
1774                         set_toolbar_transfer_mode(self);
1775                         while (tmp) {
1776                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1777                                                                       mail_op);
1778                                 tmp = g_slist_next (tmp);
1779                         }
1780                         
1781                 }
1782                 break;
1783         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
1784                 if (mode == TOOLBAR_MODE_TRANSFER) {
1785                         while (tmp) {
1786                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1787                                                                  mail_op);
1788                                 tmp = g_slist_next (tmp);
1789                                 
1790                         }
1791
1792                         /* If no more operations are being observed, NORMAL mode is enabled again */
1793                         if (observers_empty (self)) {
1794                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
1795                         }
1796                 }
1797                 break;
1798         }
1799 }
1800
1801 GList *
1802 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
1803 {
1804         ModestMsgViewWindowPrivate *priv;
1805         GList *selected_attachments = NULL;
1806         
1807         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
1808         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
1809
1810         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1811         
1812         return selected_attachments;
1813 }
1814
1815 void
1816 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
1817 {
1818         ModestMsgViewWindowPrivate *priv;
1819         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1820         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
1821
1822         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1823
1824         if (mime_part == NULL) {
1825                 gboolean error = FALSE;
1826                 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1827                 if (selected_attachments == NULL) {
1828                         error = TRUE;
1829                 } else if (g_list_length (selected_attachments) > 1) {
1830                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
1831                         error = TRUE;
1832                 } else {
1833                         mime_part = (TnyMimePart *) selected_attachments->data;
1834                         g_object_ref (mime_part);
1835                 }
1836                 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
1837                 g_list_free (selected_attachments);
1838
1839                 if (error)
1840                         return;
1841         } else {
1842                 g_object_ref (mime_part);
1843         }
1844
1845         if (tny_mime_part_is_purged (mime_part)) {
1846                 g_object_unref (mime_part);
1847                 return;
1848         }
1849
1850         if (!TNY_IS_MSG (mime_part)) {
1851                 gchar *filepath = NULL;
1852                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
1853                 gchar *extension = NULL;
1854                 TnyFsStream *temp_stream = NULL;
1855
1856                 if (att_filename) {
1857                         extension = g_strrstr (att_filename, ".");
1858                         if (extension != NULL)
1859                                 extension++;
1860                 }
1861
1862                 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
1863
1864                 if (temp_stream) {
1865                         const gchar *content_type;
1866                         content_type = tny_mime_part_get_content_type (mime_part);
1867                         tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
1868                         
1869                         modest_platform_activate_file (filepath, content_type);
1870                         g_object_unref (temp_stream);
1871                         g_free (filepath);
1872                         /* TODO: delete temporary file */
1873                 }
1874         } else {
1875                 /* message attachment */
1876                 TnyHeader *header = NULL;
1877                 ModestWindowMgr *mgr;
1878                 ModestWindow *msg_win = NULL;
1879                 gboolean found;
1880                 
1881                 header = tny_msg_get_header (TNY_MSG (mime_part));
1882                 mgr = modest_runtime_get_window_mgr ();         
1883                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
1884
1885                 if (found) {
1886                         if (msg_win)                            /* there is already a window for this uid; top it */
1887                                 gtk_window_present (GTK_WINDOW(msg_win));
1888                         else 
1889                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
1890                                  * thus, we don't do anything */
1891                                 g_warning ("window for is already being created");
1892                 } else { 
1893                         /* it's not found, so create a new window for it */
1894                         modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
1895                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
1896                         if (!account)
1897                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
1898                         msg_win = modest_msg_view_window_new (TNY_MSG (mime_part), account, NULL);
1899                         modest_window_set_zoom (MODEST_WINDOW (msg_win), 
1900                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
1901                         modest_window_mgr_register_window (mgr, msg_win);
1902                         gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
1903                         gtk_widget_show_all (GTK_WIDGET (msg_win));
1904                 }
1905         }
1906         g_object_unref (mime_part);
1907 }
1908
1909 typedef struct
1910 {
1911         gchar *filename;
1912         TnyMimePart *part;
1913 } SaveMimePartPair;
1914
1915 typedef struct
1916 {
1917         GList *pairs;
1918         GtkWidget *banner;
1919         gboolean result;
1920 } SaveMimePartInfo;
1921
1922 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
1923 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
1924 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
1925 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
1926
1927 static void 
1928 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
1929 {
1930         
1931         GList *node;
1932         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
1933                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
1934                 g_free (pair->filename);
1935                 g_object_unref (pair->part);
1936                 g_slice_free (SaveMimePartPair, pair);
1937         }
1938         g_list_free (info->pairs);
1939         info->pairs = NULL;
1940         if (with_struct) {
1941                 gtk_widget_destroy (info->banner);
1942                 g_object_unref (info->banner);
1943                 g_slice_free (SaveMimePartInfo, info);
1944         }
1945 }
1946
1947 static gboolean
1948 idle_save_mime_part_show_result (SaveMimePartInfo *info)
1949 {
1950         if (info->pairs != NULL) {
1951                 /* This is a GDK lock because we are an idle callback and
1952                  * save_mime_parts_to_file_with_checks can contain Gtk+ code */
1953
1954                 gdk_threads_enter (); /* CHECKED */
1955                 save_mime_parts_to_file_with_checks (info);
1956                 gdk_threads_leave (); /* CHECKED */
1957         } else {
1958                 gboolean result;
1959                 result = info->result;
1960
1961                 /* This is a GDK lock because we are an idle callback and
1962                  * hildon_banner_show_information is or does Gtk+ code */
1963
1964                 gdk_threads_enter (); /* CHECKED */
1965                 save_mime_part_info_free (info, TRUE);
1966                 if (result) {
1967                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
1968                 } else {
1969                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
1970                 }
1971                 gdk_threads_leave (); /* CHECKED */
1972         }
1973
1974         return FALSE;
1975 }
1976
1977 static gpointer
1978 save_mime_part_to_file (SaveMimePartInfo *info)
1979 {
1980         GnomeVFSResult result;
1981         GnomeVFSHandle *handle;
1982         TnyStream *stream;
1983         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
1984
1985         result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
1986         if (result == GNOME_VFS_OK) {
1987                 stream = tny_vfs_stream_new (handle);
1988                 tny_mime_part_decode_to_stream (pair->part, stream);
1989                 g_object_unref (G_OBJECT (stream));
1990                 g_object_unref (pair->part);
1991                 g_slice_free (SaveMimePartPair, pair);
1992                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
1993                 info->result = TRUE;
1994         } else {
1995                 save_mime_part_info_free (info, FALSE);
1996                 info->result = FALSE;
1997         }
1998
1999         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2000         return NULL;
2001 }
2002
2003 static void
2004 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2005 {
2006         SaveMimePartPair *pair;
2007         gboolean is_ok = TRUE;
2008
2009         pair = info->pairs->data;
2010         if (modest_maemo_utils_file_exists (pair->filename)) {
2011                 GtkWidget *confirm_overwrite_dialog;
2012                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2013                                                                          _("emev_nc_replace_files"));
2014                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2015                         is_ok = FALSE;
2016                 }
2017                 gtk_widget_destroy (confirm_overwrite_dialog);
2018         }
2019
2020         if (!is_ok) {
2021                 save_mime_part_info_free (info, TRUE);
2022         } else {
2023                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2024         }
2025
2026 }
2027
2028
2029 void
2030 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2031 {
2032         gboolean clean_list = FALSE;
2033         ModestMsgViewWindowPrivate *priv;
2034         GList *files_to_save = NULL;
2035         GtkWidget *save_dialog = NULL;
2036         gchar *folder = NULL;
2037         gboolean canceled = FALSE;
2038         const gchar *filename = NULL;
2039         gchar *save_multiple_str = NULL;
2040
2041         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2042         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2043
2044         if (mime_parts == NULL) {
2045                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2046                 if (mime_parts == NULL)
2047                         return;
2048                 clean_list = TRUE;
2049         }
2050
2051         /* prepare dialog */
2052         if (mime_parts->next == NULL) {
2053                 /* only one attachment selected */
2054                 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2055                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2056                         filename = tny_mime_part_get_filename (mime_part);
2057                 } else {
2058                         g_warning ("Tried to save a non-file attachment");
2059                         canceled = TRUE;
2060                 }
2061         } else {
2062                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2063                                                      g_list_length (mime_parts));
2064         }
2065         
2066         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2067                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2068
2069         /* set folder */
2070         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2071         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2072         g_free (folder);
2073
2074         /* set filename */
2075         if (filename != NULL)
2076                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2077                                                    filename);
2078
2079         /* if multiple, set multiple string */
2080         if (save_multiple_str) {
2081                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2082         }
2083                 
2084         /* show dialog */
2085         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2086                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2087
2088                 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2089                         hildon_banner_show_information 
2090                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2091                 } else {
2092                         GList *node = NULL;
2093
2094                         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2095                                 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2096                                 
2097                                 if (tny_mime_part_is_attachment (mime_part)) {
2098                                         SaveMimePartPair *pair;
2099
2100                                         if ((mime_parts->next != NULL) &&
2101                                             (tny_mime_part_get_filename (mime_part) == NULL))
2102                                                 continue;
2103                                         
2104                                         pair = g_slice_new0 (SaveMimePartPair);
2105                                         if (mime_parts->next == NULL) {
2106                                                 pair->filename = g_strdup (chooser_uri);
2107                                         } else {
2108                                                 pair->filename = 
2109                                                         g_build_filename (chooser_uri,
2110                                                                           tny_mime_part_get_filename (mime_part), NULL);
2111                                         }
2112                                         pair->part = g_object_ref (mime_part);
2113                                         files_to_save = g_list_prepend (files_to_save, pair);
2114                                 }
2115                         }
2116                 }
2117                 g_free (chooser_uri);
2118         }
2119
2120         gtk_widget_destroy (save_dialog);
2121
2122         if (clean_list) {
2123                 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2124                 g_list_free (mime_parts);
2125         }
2126
2127         if (files_to_save != NULL) {
2128                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2129                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2130                                                                   _CS("sfil_ib_saving"));
2131                 info->pairs = files_to_save;
2132                 info->banner = banner;
2133                 info->result = TRUE;
2134                 g_object_ref (banner);
2135                 save_mime_parts_to_file_with_checks (info);
2136         }
2137 }
2138
2139 static gboolean
2140 show_remove_attachment_information (gpointer userdata)
2141 {
2142         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2143         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2144
2145         if (priv->remove_attachment_banner != NULL) {
2146                 gtk_widget_destroy (priv->remove_attachment_banner);
2147                 g_object_unref (priv->remove_attachment_banner);
2148         }
2149
2150         priv->remove_attachment_banner = g_object_ref (
2151                 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2152
2153         return FALSE;
2154 }
2155
2156 void
2157 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2158 {
2159         ModestMsgViewWindowPrivate *priv;
2160         GList *mime_parts = NULL, *node;
2161         gchar *confirmation_message;
2162         gint response;
2163         gint n_attachments;
2164         TnyMsg *msg;
2165 /*      TnyFolder *folder; */
2166
2167         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2168         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2169
2170         if (get_all)
2171                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2172         else
2173                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2174                 
2175         /* Remove already purged messages from mime parts list */
2176         node = mime_parts;
2177         while (node != NULL) {
2178                 TnyMimePart *part = TNY_MIME_PART (node->data);
2179                 if (tny_mime_part_is_purged (part)) {
2180                         GList *deleted_node = node;
2181                         node = g_list_next (node);
2182                         g_object_unref (part);
2183                         mime_parts = g_list_delete_link (mime_parts, deleted_node);
2184                 } else {
2185                         node = g_list_next (node);
2186                 }
2187         }
2188
2189         if (mime_parts == NULL)
2190                 return;
2191
2192         n_attachments = g_list_length (mime_parts);
2193         if (n_attachments == 1) {
2194                 const gchar *filename;
2195
2196                 if (TNY_IS_MSG (mime_parts->data)) {
2197                         TnyHeader *header;
2198                         header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2199                         filename = tny_header_get_subject (header);
2200                         g_object_unref (header);
2201                         if (filename == NULL)
2202                                 filename = _("mail_va_no_subject");
2203                 } else {
2204                         filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2205                 }
2206                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2207         } else {
2208                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2209                                                                  "mcen_nc_purge_files_text", 
2210                                                                  n_attachments), n_attachments);
2211         }
2212         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2213                                                             confirmation_message);
2214         g_free (confirmation_message);
2215
2216         if (response != GTK_RESPONSE_OK)
2217                 return;
2218
2219         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2220 /*      folder = tny_msg_get_folder (msg); */
2221 /*      tny_msg_uncache_attachments (msg); */
2222 /*      tny_folder_refresh (folder, NULL); */
2223 /*      g_object_unref (folder); */
2224         
2225         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2226                 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2227 /*              modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2228         }
2229
2230         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2231         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2232         tny_msg_rewrite_cache (msg);
2233         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2234
2235         g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2236         g_list_free (mime_parts);
2237
2238         if (priv->purge_timeout > 0) {
2239                 g_source_remove (priv->purge_timeout);
2240                 priv->purge_timeout = 0;
2241         }
2242
2243         if (priv->remove_attachment_banner) {
2244                 gtk_widget_destroy (priv->remove_attachment_banner);
2245                 g_object_unref (priv->remove_attachment_banner);
2246                 priv->remove_attachment_banner = NULL;
2247         }
2248
2249
2250 }
2251
2252
2253 static void
2254 update_window_title (ModestMsgViewWindow *window)
2255 {
2256         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2257         TnyMsg *msg = NULL;
2258         TnyHeader *header = NULL;
2259         const gchar *subject = NULL;
2260
2261         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2262         if (msg != NULL) {
2263                 header = tny_msg_get_header (msg);
2264                 subject = tny_header_get_subject (header);
2265                 g_object_unref (msg);
2266         }
2267
2268         if ((subject == NULL)||(subject[0] == '\0'))
2269                 subject = _("mail_va_no_subject");
2270
2271         gtk_window_set_title (GTK_WINDOW (window), subject);
2272 }