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