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