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