Dim "Reply to" commands when a message is in the outbox
[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 || 
1190             priv->row_reference == NULL ||
1191             !gtk_tree_row_reference_valid (priv->row_reference)) {
1192                 msg = modest_msg_view_window_get_message (self);
1193                 if (msg) {
1194                         header = tny_msg_get_header (msg);
1195                         g_object_unref (msg);
1196                 }
1197                 return header;
1198         }
1199
1200         /* Get iter of the currently selected message in the header view: */
1201         path = gtk_tree_row_reference_get_path (priv->row_reference);
1202         g_return_val_if_fail (path != NULL, NULL);
1203         gtk_tree_model_get_iter (priv->header_model, 
1204                                  &iter, 
1205                                  path);
1206
1207         /* Get current message header */
1208         gtk_tree_model_get (priv->header_model, &iter, 
1209                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1210                             &header, -1);
1211
1212         gtk_tree_path_free (path);
1213         return header;
1214 }
1215
1216 TnyMsg*
1217 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1218 {
1219         ModestMsgViewWindowPrivate *priv;
1220         
1221         g_return_val_if_fail (self, NULL);
1222         
1223         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1224         
1225         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1226 }
1227
1228 const gchar*
1229 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1230 {
1231         ModestMsgViewWindowPrivate *priv;
1232
1233         g_return_val_if_fail (self, NULL);
1234         
1235         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1236
1237         return (const gchar*) priv->msg_uid;
1238 }
1239
1240 static void 
1241 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1242                                             gpointer data)
1243 {
1244         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1245         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1246         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1247         gboolean is_active;
1248         GtkAction *action;
1249
1250         is_active = gtk_toggle_action_get_active (toggle);
1251
1252         if (is_active) {
1253                 gtk_widget_show (priv->find_toolbar);
1254                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1255         } else {
1256                 gtk_widget_hide (priv->find_toolbar);
1257         }
1258
1259         /* update the toggle buttons status */
1260         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1261         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1262         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1263         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1264         
1265 }
1266
1267 static void
1268 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1269                                            ModestMsgViewWindow *obj)
1270 {
1271         GtkToggleAction *toggle;
1272         ModestWindowPrivate *parent_priv;
1273         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1274         
1275         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1276         gtk_toggle_action_set_active (toggle, FALSE);
1277 }
1278
1279 static void
1280 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1281                                            ModestMsgViewWindow *obj)
1282 {
1283         gchar *current_search;
1284         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1285
1286         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1287                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1288                 return;
1289         }
1290
1291         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1292
1293         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1294                 g_free (current_search);
1295                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1296                 return;
1297         }
1298
1299         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1300                 gboolean result;
1301                 g_free (priv->last_search);
1302                 priv->last_search = g_strdup (current_search);
1303                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1304                                                      priv->last_search);
1305                 if (!result) {
1306                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1307                         g_free (priv->last_search);
1308                         priv->last_search = NULL;
1309                 } else {
1310                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1311                 }
1312         } else {
1313                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1314                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1315                         g_free (priv->last_search);
1316                         priv->last_search = NULL;
1317                 } else {
1318                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1319                 }
1320         }
1321         
1322         g_free (current_search);
1323                 
1324 }
1325
1326 static void
1327 modest_msg_view_window_set_zoom (ModestWindow *window,
1328                                  gdouble zoom)
1329 {
1330         ModestMsgViewWindowPrivate *priv;
1331         ModestWindowPrivate *parent_priv;
1332         GtkAction *action = NULL;
1333         gint int_zoom = (gint) rint (zoom*100.0+0.1);
1334      
1335         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1336
1337         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1338         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1339         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1340
1341         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
1342                                             "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1343
1344         gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1345 }
1346
1347 static gdouble
1348 modest_msg_view_window_get_zoom (ModestWindow *window)
1349 {
1350         ModestMsgViewWindowPrivate *priv;
1351      
1352         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1353
1354         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1355         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1356 }
1357
1358 static gboolean
1359 modest_msg_view_window_zoom_plus (ModestWindow *window)
1360 {
1361         ModestWindowPrivate *parent_priv;
1362         GtkRadioAction *zoom_radio_action;
1363         GSList *group, *node;
1364
1365         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1366         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1367                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1368
1369         group = gtk_radio_action_get_group (zoom_radio_action);
1370
1371         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1372                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1373                 return FALSE;
1374         }
1375
1376         for (node = group; node != NULL; node = g_slist_next (node)) {
1377                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1378                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1379                         return TRUE;
1380                 }
1381         }
1382         return FALSE;
1383 }
1384
1385 static gboolean
1386 modest_msg_view_window_zoom_minus (ModestWindow *window)
1387 {
1388         ModestWindowPrivate *parent_priv;
1389         GtkRadioAction *zoom_radio_action;
1390         GSList *group, *node;
1391
1392         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1393         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1394                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1395
1396         group = gtk_radio_action_get_group (zoom_radio_action);
1397
1398         for (node = group; node != NULL; node = g_slist_next (node)) {
1399                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1400                         if (node->next != NULL) {
1401                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1402                                 return TRUE;
1403                         } else {
1404                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1405                                 return FALSE;
1406                         }
1407                         break;
1408                 }
1409         }
1410         return FALSE;
1411 }
1412
1413 static gboolean
1414 modest_msg_view_window_key_event (GtkWidget *window,
1415                                   GdkEventKey *event,
1416                                   gpointer userdata)
1417 {
1418         GtkWidget *focus;
1419
1420         focus = gtk_window_get_focus (GTK_WINDOW (window));
1421
1422         /* for the find toolbar case */
1423         if (focus && GTK_IS_ENTRY (focus)) {
1424                 if (event->keyval == GDK_BackSpace) {
1425                         GdkEvent *copy;
1426                         copy = gdk_event_copy ((GdkEvent *) event);
1427                         gtk_widget_event (focus, copy);
1428                         gdk_event_free (copy);
1429                         return TRUE;
1430                 } else 
1431                         return FALSE;
1432         }
1433         if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1434             event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1435             event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1436             event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1437             event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1438             event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1439                 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1440                 gboolean return_value;
1441
1442                 if (event->type == GDK_KEY_RELEASE) {
1443                         GtkScrollType scroll_type;
1444                         
1445                         switch (event->keyval) {
1446                         case GDK_Up: 
1447                         case GDK_KP_Up:
1448                                 scroll_type = GTK_SCROLL_STEP_UP; break;
1449                         case GDK_Down: 
1450                         case GDK_KP_Down:
1451                                 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1452                         case GDK_Page_Up:
1453                         case GDK_KP_Page_Up:
1454                                 scroll_type = GTK_SCROLL_PAGE_UP; break;
1455                         case GDK_Page_Down:
1456                         case GDK_KP_Page_Down:
1457                                 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1458                         case GDK_Home:
1459                         case GDK_KP_Home:
1460                                 scroll_type = GTK_SCROLL_START; break;
1461                         case GDK_End:
1462                         case GDK_KP_End:
1463                                 scroll_type = GTK_SCROLL_END; break;
1464                         default: scroll_type = GTK_SCROLL_NONE;
1465                         }
1466                         
1467                         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", 
1468                                                scroll_type, FALSE, &return_value);
1469                         return TRUE;
1470                 } else {
1471                         return FALSE;
1472                 }
1473         } else {
1474                 return FALSE;
1475         }
1476 }
1477
1478 gboolean
1479 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1480 {
1481         GtkTreePath *path;
1482         ModestMsgViewWindowPrivate *priv;
1483         GtkTreeIter tmp_iter;
1484         gboolean is_last_selected;
1485
1486         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1487         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1488
1489         /*if no model (so no rows at all), then virtually we are the last*/
1490         if (!priv->header_model || !priv->row_reference)
1491                 return TRUE;
1492
1493         path = gtk_tree_row_reference_get_path (priv->row_reference);
1494         if (path == NULL)
1495                 return TRUE;
1496
1497         is_last_selected = TRUE;
1498         while (is_last_selected) {
1499                 TnyHeader *header;
1500                 gtk_tree_path_next (path);
1501                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1502                         break;
1503                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1504                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1505                                 &header, -1);
1506                 if (header) {
1507                         if (msg_is_visible (header, priv->is_outbox))
1508                                 is_last_selected = FALSE;
1509                         g_object_unref(G_OBJECT(header));
1510                 }
1511         }
1512         gtk_tree_path_free (path);
1513         return is_last_selected;
1514 }
1515
1516 gboolean
1517 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1518 {
1519         ModestMsgViewWindowPrivate *priv;
1520
1521         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1522         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1523
1524         return priv->header_model != NULL;
1525 }
1526
1527 gboolean
1528 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1529 {
1530         ModestMsgViewWindowPrivate *priv;
1531
1532         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1533         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1534
1535         return priv->is_search_result;
1536 }
1537
1538 static gboolean
1539 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1540 {
1541         return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1542                 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1543         
1544 }
1545
1546 gboolean
1547 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1548 {
1549         GtkTreePath *path;
1550         ModestMsgViewWindowPrivate *priv;
1551         gboolean is_first_selected;
1552         GtkTreeIter tmp_iter;
1553 /*      gchar * path_string;*/
1554
1555         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1556         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1557
1558         /*if no model (so no rows at all), then virtually we are the first*/
1559         if (!priv->header_model || !priv->row_reference)
1560                 return TRUE;
1561
1562         path = gtk_tree_row_reference_get_path (priv->row_reference);
1563         if (!path)
1564                 return TRUE;
1565
1566 /*      path_string = gtk_tree_path_to_string (path);
1567         is_first_selected = strcmp (path_string, "0");
1568
1569         g_free (path_string);
1570         gtk_tree_path_free (path);
1571
1572         return is_first_selected;*/
1573
1574         is_first_selected = TRUE;
1575         while (is_first_selected) {
1576                 TnyHeader *header;
1577                 if(!gtk_tree_path_prev (path))
1578                         break;
1579                 /* Here the 'if' is needless for logic, but let make sure
1580                  * iter is valid for gtk_tree_model_get. */
1581                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1582                         break;
1583                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1584                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1585                                 &header, -1);
1586                 if (header) {
1587                         if (msg_is_visible (header, priv->is_outbox))
1588                                 is_first_selected = FALSE;
1589                         g_object_unref(G_OBJECT(header));
1590                 }
1591         }
1592         gtk_tree_path_free (path);
1593         return is_first_selected;
1594 }
1595
1596 /**
1597  * Reads the message whose summary item is @header. It takes care of
1598  * several things, among others:
1599  *
1600  * If the message was not previously downloaded then ask the user
1601  * before downloading. If there is no connection launch the connection
1602  * dialog. Update toolbar dimming rules.
1603  *
1604  * Returns: TRUE if the mail operation was started, otherwise if the
1605  * user do not want to download the message, or if the user do not
1606  * want to connect, then the operation is not issued
1607  **/
1608 static gboolean
1609 message_reader (ModestMsgViewWindow *window,
1610                 ModestMsgViewWindowPrivate *priv,
1611                 TnyHeader *header,
1612                 GtkTreeRowReference *row_reference)
1613 {
1614         ModestMailOperation *mail_op = NULL;
1615         gboolean already_showing = FALSE;
1616         ModestWindow *msg_window = NULL;
1617         ModestWindowMgr *mgr;
1618
1619         g_return_val_if_fail (row_reference != NULL, FALSE);
1620
1621         mgr = modest_runtime_get_window_mgr ();
1622         already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1623         if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1624                 gboolean retval;
1625                 if (msg_window)
1626                         gtk_window_present (GTK_WINDOW (msg_window));
1627                 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1628                 return TRUE;
1629         }
1630
1631         /* Msg download completed */
1632         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1633                 /* Ask the user if he wants to download the message if
1634                    we're not online */
1635                 if (!tny_device_is_online (modest_runtime_get_device())) {
1636                         TnyFolder *folder = NULL;
1637                         GtkResponseType response;
1638
1639                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1640                                                                             _("mcen_nc_get_msg"));
1641                         if (response == GTK_RESPONSE_CANCEL)
1642                                 return FALSE;
1643                 
1644                         /* Offer the connection dialog if necessary */
1645                         folder = tny_header_get_folder (header);
1646                         if (folder) {
1647                                 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, 
1648                                                                                               TNY_FOLDER_STORE (folder))) {
1649                                         g_object_unref (folder);
1650                                         return FALSE;
1651                                 }
1652                                 g_object_unref (folder);
1653                         }
1654                 }
1655         }
1656
1657         /* New mail operation */
1658         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1659                                                                  modest_ui_actions_get_msgs_full_error_handler, 
1660                                                                  NULL, NULL);
1661                                 
1662         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1663         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1664         g_object_unref (mail_op);
1665
1666         /* Update dimming rules */
1667         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1668         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1669
1670         return TRUE;
1671 }
1672
1673 gboolean        
1674 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1675 {
1676         ModestMsgViewWindowPrivate *priv;
1677         GtkTreePath *path= NULL;
1678         GtkTreeIter tmp_iter;
1679         TnyHeader *header;
1680         gboolean retval = TRUE;
1681         GtkTreeRowReference *row_reference = NULL;
1682
1683         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1684         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1685
1686         if (!priv->row_reference)
1687                 return FALSE;
1688
1689         /* Update the next row reference if it's not valid. This could
1690            happen if for example the header which it was pointing to,
1691            was deleted. The best place to do it is in the row-deleted
1692            handler but the tinymail model do not work like the glib
1693            tree models and reports the deletion when the row is still
1694            there */
1695         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1696                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1697                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1698                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1699                 }
1700         }
1701         if (priv->next_row_reference)
1702                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1703         if (path == NULL)
1704                 return FALSE;
1705
1706         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1707
1708         gtk_tree_model_get_iter (priv->header_model,
1709                                  &tmp_iter,
1710                                  path);
1711         gtk_tree_path_free (path);
1712
1713         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1714                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1715                             &header, -1);
1716         
1717         /* Read the message & show it */
1718         if (!message_reader (window, priv, header, row_reference)) {
1719                 retval = FALSE;
1720                 gtk_tree_row_reference_free (row_reference);
1721         }
1722
1723         /* Free */
1724         g_object_unref (header);
1725
1726         return retval;          
1727 }
1728
1729 gboolean        
1730 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1731 {
1732         ModestMsgViewWindowPrivate *priv = NULL;
1733         GtkTreePath *path;
1734         gboolean finished = FALSE;
1735         gboolean retval = FALSE;
1736
1737         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1738         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1739
1740         /* Return inmediatly if there is no header model */
1741         if (!priv->header_model || !priv->row_reference)
1742                 return FALSE;
1743
1744         path = gtk_tree_row_reference_get_path (priv->row_reference);
1745         while (!finished && gtk_tree_path_prev (path)) {
1746                 TnyHeader *header;
1747                 GtkTreeIter iter;
1748
1749                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1750                 gtk_tree_model_get (priv->header_model, &iter, 
1751                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1752                                     &header, -1);
1753                 finished = TRUE;
1754                 if (header) {
1755                         if (msg_is_visible (header, priv->is_outbox)) {
1756                                 GtkTreeRowReference *row_reference;
1757                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1758                                 /* Read the message & show it */
1759                                 retval = message_reader (window, priv, header, row_reference);
1760                                 if (!retval)
1761                                         gtk_tree_row_reference_free (row_reference);
1762                         } else {
1763                                 finished = FALSE;
1764                         }
1765                         g_object_unref (header);
1766                 }
1767         }
1768
1769         gtk_tree_path_free (path);
1770         return retval;
1771 }
1772
1773 static void
1774 view_msg_cb (ModestMailOperation *mail_op, 
1775              TnyHeader *header, 
1776              gboolean canceled,
1777              TnyMsg *msg, 
1778              GError *error,
1779              gpointer user_data)
1780 {
1781         ModestMsgViewWindow *self = NULL;
1782         ModestMsgViewWindowPrivate *priv = NULL;
1783         GtkTreeRowReference *row_reference = NULL;
1784
1785         if (canceled) {
1786                 g_object_unref (self);
1787                 return;
1788         }
1789         
1790         /* If there was any error */
1791         row_reference = (GtkTreeRowReference *) user_data;
1792         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1793                 gtk_tree_row_reference_free (row_reference);                    
1794                 return;
1795         }
1796
1797         /* Get the window */ 
1798         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1799         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1800         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1801
1802         /* Update the row reference */
1803         if (priv->row_reference != NULL) {
1804                 gtk_tree_row_reference_free (priv->row_reference);
1805                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1806                 if (priv->next_row_reference != NULL) {
1807                         gtk_tree_row_reference_free (priv->next_row_reference);
1808                 }
1809                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1810                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1811                 gtk_tree_row_reference_free (row_reference);
1812         }
1813
1814         /* Mark header as read */
1815         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1816                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1817
1818         /* Set new message */
1819         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1820                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1821                 modest_msg_view_window_update_priority (self);
1822                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1823                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1824         }
1825
1826         /* Set the new message uid of the window  */
1827         if (priv->msg_uid) {
1828                 g_free (priv->msg_uid);
1829                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1830         }
1831
1832         /* Notify the observers */
1833         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1834                        0, priv->header_model, priv->row_reference);
1835
1836         /* Free new references */
1837         g_object_unref (self);
1838 }
1839
1840 TnyFolderType
1841 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1842 {
1843         ModestMsgViewWindowPrivate *priv;
1844         TnyMsg *msg;
1845         TnyFolderType folder_type;
1846
1847         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1848
1849         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1850
1851         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1852         if (msg) {
1853                 TnyFolder *folder;
1854
1855                 folder = tny_msg_get_folder (msg);
1856                 if (folder) {
1857                         folder_type = modest_tny_folder_guess_folder_type (folder);
1858                         g_object_unref (folder);
1859                 }
1860                 g_object_unref (msg);
1861         }
1862
1863         return folder_type;
1864 }
1865
1866
1867 static void
1868 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1869 {
1870         ModestMsgViewWindowPrivate *priv;
1871         TnyHeader *header = NULL;
1872         TnyHeaderFlags flags = 0;
1873
1874         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1875
1876         if (priv->header_model && priv->row_reference) {
1877                 GtkTreeIter iter;
1878                 GtkTreePath *path = NULL;
1879
1880                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1881                 g_return_if_fail (path != NULL);
1882                 gtk_tree_model_get_iter (priv->header_model, 
1883                                          &iter, 
1884                                          gtk_tree_row_reference_get_path (priv->row_reference));
1885
1886                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1887                                     &header, -1);
1888                 gtk_tree_path_free (path);
1889         } else {
1890                 TnyMsg *msg;
1891                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1892                 if (msg) {
1893                         header = tny_msg_get_header (msg);
1894                         g_object_unref (msg);
1895                 }
1896         }
1897
1898         if (header) {
1899                 flags = tny_header_get_flags (header);
1900                 g_object_unref(G_OBJECT(header));
1901         }
1902
1903         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1904
1905 }
1906
1907 static void
1908 toolbar_resize (ModestMsgViewWindow *self)
1909 {
1910         ModestMsgViewWindowPrivate *priv = NULL;
1911         ModestWindowPrivate *parent_priv = NULL;
1912         GtkWidget *widget;
1913         gint static_button_size;
1914         ModestWindowMgr *mgr;
1915
1916         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1917         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1918         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1919
1920         mgr = modest_runtime_get_window_mgr ();
1921         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1922
1923         if (parent_priv->toolbar) {
1924                 /* left size buttons */
1925                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1926                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1927                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1928                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1929                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
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/ToolbarDeleteMessage");
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/FindInMessage");
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                 
1942                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1943                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1944                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1945                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1946                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1947                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1948                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1949                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1950         }
1951                 
1952 }
1953
1954 static gboolean
1955 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1956 {
1957         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1958                 ModestWindowPrivate *parent_priv;
1959                 ModestWindowMgr *mgr;
1960                 gboolean is_fullscreen;
1961                 GtkAction *fs_toggle_action;
1962                 gboolean active;
1963
1964                 mgr = modest_runtime_get_window_mgr ();
1965                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1966
1967                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1968                 
1969                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1970                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1971                 if (is_fullscreen != active) {
1972                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1973                 }
1974                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
1975         }
1976
1977         return FALSE;
1978
1979 }
1980
1981 static void
1982 modest_msg_view_window_show_toolbar (ModestWindow *self,
1983                                      gboolean show_toolbar)
1984 {
1985         ModestMsgViewWindowPrivate *priv = NULL;
1986         ModestWindowPrivate *parent_priv;
1987         GtkWidget *reply_button = NULL, *menu = NULL;
1988         GtkWidget *placeholder = NULL;
1989         gint insert_index;
1990         const gchar *action_name;
1991         GtkAction *action;
1992         
1993         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1994         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1995
1996         /* Set optimized view status */
1997         priv->optimized_view = !show_toolbar;
1998
1999         if (!parent_priv->toolbar) {
2000                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2001                                                                   "/ToolBar");
2002                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2003
2004                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2005                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2006                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2007                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2008                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2009
2010                 /* Add ProgressBar (Transfer toolbar) */ 
2011                 priv->progress_bar = modest_progress_bar_new ();
2012                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2013                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2014                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2015                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2016                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2017                 
2018                 /* Connect cancel 'clicked' signal to abort progress mode */
2019                 g_signal_connect(priv->cancel_toolitem, "clicked",
2020                                  G_CALLBACK(cancel_progressbar),
2021                                  self);
2022                 
2023                 /* Add it to the observers list */
2024                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2025
2026                 /* Add to window */
2027                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2028                                            GTK_TOOLBAR (parent_priv->toolbar));
2029
2030                 /* Set reply button tap and hold menu */        
2031                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2032                                                           "/ToolBar/ToolbarMessageReply");
2033                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2034                                                   "/ToolbarReplyCSM");
2035                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2036         }
2037
2038         if (show_toolbar) {
2039                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2040                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2041                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2042
2043                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2044                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2045                         set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2046                 else
2047                         set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2048
2049         } else {
2050                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2051                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2052         }
2053
2054         /* Update also the actions (to update the toggles in the
2055            menus), we have to do it manually because some other window
2056            of the same time could have changed it (remember that the
2057            toolbar fullscreen mode is shared by all the windows of the
2058            same type */
2059         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2060                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2061         else
2062                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2063
2064         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2065         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2066                                                             show_toolbar);
2067 }
2068
2069 static void 
2070 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2071                                                GdkEvent *event,
2072                                                ModestMsgViewWindow *window)
2073 {
2074         if (!GTK_WIDGET_VISIBLE (window))
2075                 return;
2076
2077         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2078 }
2079
2080 gboolean 
2081 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2082 {
2083         ModestMsgViewWindowPrivate *priv;
2084         
2085         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2086         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2087
2088         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2089 }
2090
2091 static void
2092 cancel_progressbar (GtkToolButton *toolbutton,
2093                     ModestMsgViewWindow *self)
2094 {
2095         GSList *tmp;
2096         ModestMsgViewWindowPrivate *priv;
2097         
2098         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2099
2100         /* Get operation observers and cancel its current operation */
2101         tmp = priv->progress_widgets;
2102         while (tmp) {
2103                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2104                 tmp=g_slist_next(tmp);
2105         }
2106 }
2107 static gboolean
2108 observers_empty (ModestMsgViewWindow *self)
2109 {
2110         GSList *tmp = NULL;
2111         ModestMsgViewWindowPrivate *priv;
2112         gboolean is_empty = TRUE;
2113         guint pending_ops = 0;
2114  
2115         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2116         tmp = priv->progress_widgets;
2117
2118         /* Check all observers */
2119         while (tmp && is_empty)  {
2120                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2121                 is_empty = pending_ops == 0;
2122                 
2123                 tmp = g_slist_next(tmp);
2124         }
2125         
2126         return is_empty;
2127 }
2128
2129 static void
2130 on_account_removed (TnyAccountStore *account_store, 
2131                     TnyAccount *account,
2132                     gpointer user_data)
2133 {
2134         /* Do nothing if it's a transport account, because we only
2135            show the messages of a store account */
2136         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2137                 const gchar *parent_acc = NULL;
2138                 const gchar *our_acc = NULL;
2139
2140                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2141                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2142
2143                 /* Close this window if I'm showing a message of the removed account */
2144                 if (strcmp (parent_acc, our_acc) == 0)
2145                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2146         }
2147 }
2148
2149 static void 
2150 on_mail_operation_started (ModestMailOperation *mail_op,
2151                            gpointer user_data)
2152 {
2153         ModestMsgViewWindow *self;
2154         ModestMailOperationTypeOperation op_type;
2155         GSList *tmp;
2156         ModestMsgViewWindowPrivate *priv;
2157         GObject *source = NULL;
2158
2159         self = MODEST_MSG_VIEW_WINDOW (user_data);
2160         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2161         op_type = modest_mail_operation_get_type_operation (mail_op);
2162         tmp = priv->progress_widgets;
2163         source = modest_mail_operation_get_source(mail_op);
2164         if (G_OBJECT (self) == source) {
2165                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2166                         set_toolbar_transfer_mode(self);
2167                         while (tmp) {
2168                                 modest_progress_object_add_operation (
2169                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2170                                                 mail_op);
2171                                 tmp = g_slist_next (tmp);
2172                         }
2173                 }
2174         }
2175         g_object_unref (source);
2176 }
2177
2178 static void 
2179 on_mail_operation_finished (ModestMailOperation *mail_op,
2180                             gpointer user_data)
2181 {
2182         ModestMsgViewWindow *self;
2183         ModestMailOperationTypeOperation op_type;
2184         GSList *tmp;
2185         ModestMsgViewWindowPrivate *priv;
2186         
2187         self = MODEST_MSG_VIEW_WINDOW (user_data);
2188         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2189         op_type = modest_mail_operation_get_type_operation (mail_op);
2190         tmp = priv->progress_widgets;
2191         
2192         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2193                 while (tmp) {
2194                         modest_progress_object_remove_operation (
2195                                         MODEST_PROGRESS_OBJECT (tmp->data),
2196                                         mail_op);
2197                         tmp = g_slist_next (tmp);
2198                 }
2199
2200                 /* If no more operations are being observed, NORMAL mode is enabled again */
2201                 if (observers_empty (self)) {
2202                         set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2203                 }
2204         }
2205 }
2206
2207 static void
2208 on_queue_changed (ModestMailOperationQueue *queue,
2209                   ModestMailOperation *mail_op,
2210                   ModestMailOperationQueueNotification type,
2211                   ModestMsgViewWindow *self)
2212 {       
2213         ModestMsgViewWindowPrivate *priv;
2214
2215         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2216
2217         /* If this operations was created by another window, do nothing */
2218         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2219             return;
2220
2221         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2222                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2223                                                                G_OBJECT (mail_op),
2224                                                                "operation-started",
2225                                                                G_CALLBACK (on_mail_operation_started),
2226                                                                self);
2227                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2228                                                                G_OBJECT (mail_op),
2229                                                                "operation-finished",
2230                                                                G_CALLBACK (on_mail_operation_finished),
2231                                                                self);
2232         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2233                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2234                                                                   G_OBJECT (mail_op),
2235                                                                   "operation-started");
2236                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2237                                                                   G_OBJECT (mail_op),
2238                                                                   "operation-finished");
2239         }
2240 }
2241
2242 TnyList *
2243 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2244 {
2245         ModestMsgViewWindowPrivate *priv;
2246         TnyList *selected_attachments = NULL;
2247         
2248         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2249         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2250
2251         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2252         
2253         return selected_attachments;
2254 }
2255
2256 void
2257 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2258 {
2259         ModestMsgViewWindowPrivate *priv;
2260         const gchar *msg_uid;
2261         gchar *attachment_uid = NULL;
2262         gint attachment_index = 0;
2263         TnyList *attachments;
2264
2265         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2266         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2267         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2268
2269         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2270         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2271         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2272         g_object_unref (attachments);
2273         
2274         if (msg_uid && attachment_index >= 0) {
2275                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2276         }
2277
2278         if (mime_part == NULL) {
2279                 gboolean error = FALSE;
2280                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2281                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2282                         error = TRUE;
2283                 } else if (tny_list_get_length (selected_attachments) > 1) {
2284                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2285                         error = TRUE;
2286                 } else {
2287                         TnyIterator *iter;
2288                         iter = tny_list_create_iterator (selected_attachments);
2289                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2290                         g_object_unref (iter);
2291                 }
2292                 g_object_unref (selected_attachments);
2293
2294                 if (error)
2295                         return;
2296         } else {
2297                 g_object_ref (mime_part);
2298         }
2299
2300         if (tny_mime_part_is_purged (mime_part)) {
2301                 g_object_unref (mime_part);
2302                 return;
2303         }
2304
2305         if (!TNY_IS_MSG (mime_part)) {
2306                 gchar *filepath = NULL;
2307                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2308                 const gchar *content_type;
2309                 gboolean show_error_banner = FALSE;
2310                 GError *err;
2311                 TnyFsStream *temp_stream = NULL;
2312                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2313                                                                &filepath);
2314                 
2315                 if (temp_stream != NULL) {
2316                         content_type = tny_mime_part_get_content_type (mime_part);
2317                         if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2318                                 /* make the file read-only */
2319                                 if (g_chmod(filepath, 0444) != 0)
2320                                         g_warning ("%s: failed to set file '%s' to read-only: %s",
2321                                                         __FUNCTION__, filepath, strerror(errno));
2322
2323                                 modest_platform_activate_file (filepath, content_type);
2324                         } else {
2325                                 /* error while saving attachment, maybe cerm_device_memory_full */
2326                                 show_error_banner = TRUE;
2327                                 if (err != NULL) {
2328                                         g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2329                                         g_error_free (err);
2330                                 }
2331                         }
2332                         g_object_unref (temp_stream);
2333                         g_free (filepath);
2334                         /* NOTE: files in the temporary area will be automatically
2335                          * cleaned after some time if they are no longer in use */
2336                 } else {
2337                         if (filepath != NULL) {
2338                                 /* the file may already exist but it isn't writable,
2339                                  * let's try to open it anyway */
2340                                 content_type = tny_mime_part_get_content_type (mime_part);
2341                                 modest_platform_activate_file (filepath, content_type);
2342                                 g_free (filepath);
2343                         } else {
2344                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2345                                 show_error_banner = TRUE;
2346                         }
2347                 }
2348                 if (show_error_banner)
2349                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2350         } else {
2351                 /* message attachment */
2352                 TnyHeader *header = NULL;
2353                 ModestWindowMgr *mgr;
2354                 ModestWindow *msg_win = NULL;
2355                 gboolean found;
2356                 
2357                 header = tny_msg_get_header (TNY_MSG (mime_part));
2358                 mgr = modest_runtime_get_window_mgr ();         
2359                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2360
2361                 if (found) {
2362                         if (msg_win)                            /* there is already a window for this uid; top it */
2363                                 gtk_window_present (GTK_WINDOW(msg_win));
2364                         else 
2365                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2366                                  * thus, we don't do anything */
2367                                 g_warning ("window for is already being created");
2368                 } else { 
2369                         /* it's not found, so create a new window for it */
2370                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2371                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2372                         if (!account)
2373                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2374                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2375                         modest_window_set_zoom (MODEST_WINDOW (msg_win), 
2376                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2377                         modest_window_mgr_register_window (mgr, msg_win);
2378                         gtk_widget_show_all (GTK_WIDGET (msg_win));
2379                 }
2380         }
2381         g_object_unref (mime_part);
2382 }
2383
2384 typedef struct
2385 {
2386         gchar *filename;
2387         TnyMimePart *part;
2388 } SaveMimePartPair;
2389
2390 typedef struct
2391 {
2392         GList *pairs;
2393         GtkWidget *banner;
2394         gboolean result;
2395 } SaveMimePartInfo;
2396
2397 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2398 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2399 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2400 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2401
2402 static void 
2403 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2404 {
2405         
2406         GList *node;
2407         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2408                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2409                 g_free (pair->filename);
2410                 g_object_unref (pair->part);
2411                 g_slice_free (SaveMimePartPair, pair);
2412         }
2413         g_list_free (info->pairs);
2414         info->pairs = NULL;
2415         if (with_struct) {
2416                 gtk_widget_destroy (info->banner);
2417                 g_slice_free (SaveMimePartInfo, info);
2418         }
2419 }
2420
2421 static gboolean
2422 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2423 {
2424         if (info->pairs != NULL) {
2425                 save_mime_part_to_file (info);
2426         } else {
2427                 gboolean result;
2428                 result = info->result;
2429
2430                 /* This is a GDK lock because we are an idle callback and
2431                  * hildon_banner_show_information is or does Gtk+ code */
2432
2433                 gdk_threads_enter (); /* CHECKED */
2434                 save_mime_part_info_free (info, TRUE);
2435                 if (result) {
2436                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2437                 } else {
2438                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2439                 }
2440                 gdk_threads_leave (); /* CHECKED */
2441         }
2442
2443         return FALSE;
2444 }
2445
2446 static gpointer
2447 save_mime_part_to_file (SaveMimePartInfo *info)
2448 {
2449         GnomeVFSResult result;
2450         GnomeVFSHandle *handle;
2451         TnyStream *stream;
2452         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2453         gboolean decode_result = TRUE;
2454
2455         result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2456         if (result == GNOME_VFS_OK) {
2457                 stream = tny_vfs_stream_new (handle);
2458                 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2459                         decode_result = FALSE;
2460                 }
2461                 g_object_unref (G_OBJECT (stream));
2462                 g_object_unref (pair->part);
2463                 g_slice_free (SaveMimePartPair, pair);
2464                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2465                 info->result = decode_result;
2466         } else {
2467                 save_mime_part_info_free (info, FALSE);
2468                 info->result = FALSE;
2469         }
2470
2471         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2472         return NULL;
2473 }
2474
2475 static void
2476 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2477 {
2478         gboolean is_ok = TRUE;
2479         gint replaced_files = 0;
2480         const GList *files = info->pairs;
2481         const GList *iter;
2482
2483         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2484                 SaveMimePartPair *pair = iter->data;
2485                 if (modest_utils_file_exists (pair->filename)) {
2486                         replaced_files++;
2487                 }
2488         }
2489         if (replaced_files) {
2490                 GtkWidget *confirm_overwrite_dialog;
2491                 const gchar *message = (replaced_files == 1) ?
2492                         _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2493                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2494                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2495                         is_ok = FALSE;
2496                 }
2497                 gtk_widget_destroy (confirm_overwrite_dialog);
2498         }
2499
2500         if (!is_ok) {
2501                 save_mime_part_info_free (info, TRUE);
2502         } else {
2503                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2504                                                                   _CS("sfil_ib_saving"));
2505                 info->banner = banner;
2506                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2507         }
2508
2509 }
2510
2511
2512 void
2513 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2514 {
2515         ModestMsgViewWindowPrivate *priv;
2516         GList *files_to_save = NULL;
2517         GtkWidget *save_dialog = NULL;
2518         gchar *folder = NULL;
2519         gboolean canceled = FALSE;
2520         const gchar *filename = NULL;
2521         gchar *save_multiple_str = NULL;
2522
2523         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2524         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2525
2526         if (mime_parts == NULL) {
2527                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2528                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2529                         return;
2530         } else {
2531                 g_object_ref (mime_parts);
2532         }
2533
2534         /* prepare dialog */
2535         if (tny_list_get_length (mime_parts) == 1) {
2536                 TnyIterator *iter;
2537                 /* only one attachment selected */
2538                 iter = tny_list_create_iterator (mime_parts);
2539                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2540                 g_object_unref (iter);
2541                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2542                         filename = tny_mime_part_get_filename (mime_part);
2543                 } else {
2544                         g_warning ("Tried to save a non-file attachment");
2545                         canceled = TRUE;
2546                 }
2547                 g_object_unref (mime_part);
2548         } else {
2549                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2550                                                      tny_list_get_length (mime_parts));
2551         }
2552         
2553         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2554                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2555
2556         /* set folder */
2557         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2558         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2559         g_free (folder);
2560
2561         /* set filename */
2562         if (filename != NULL)
2563                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2564                                                    filename);
2565
2566         /* if multiple, set multiple string */
2567         if (save_multiple_str) {
2568                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2569         }
2570                 
2571         /* show dialog */
2572         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2573                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2574
2575                 if (!modest_utils_folder_writable (chooser_uri)) {
2576                         hildon_banner_show_information 
2577                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2578                 } else {
2579                         TnyIterator *iter;
2580
2581                         iter = tny_list_create_iterator (mime_parts);
2582                         while (!tny_iterator_is_done (iter)) {
2583                                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2584
2585                                 if ((tny_mime_part_is_attachment (mime_part)) && 
2586                                     (tny_mime_part_get_filename (mime_part) != NULL)) {
2587                                         SaveMimePartPair *pair;
2588                                         
2589                                         pair = g_slice_new0 (SaveMimePartPair);
2590                                         if (save_multiple_str) {
2591                                                 pair->filename = 
2592                                                         g_build_filename (chooser_uri,
2593                                                                           tny_mime_part_get_filename (mime_part), NULL);
2594                                         } else {
2595                                                 pair->filename = g_strdup (chooser_uri);
2596                                         }
2597                                         pair->part = mime_part;
2598                                         files_to_save = g_list_prepend (files_to_save, pair);
2599                                 }
2600                                 tny_iterator_next (iter);
2601                         }
2602                         g_object_unref (iter);
2603                 }
2604                 g_free (chooser_uri);
2605         }
2606
2607         gtk_widget_destroy (save_dialog);
2608
2609         g_object_unref (mime_parts);
2610
2611         if (files_to_save != NULL) {
2612                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2613                 info->pairs = files_to_save;
2614                 info->result = TRUE;
2615                 save_mime_parts_to_file_with_checks (info);
2616         }
2617 }
2618
2619 static gboolean
2620 show_remove_attachment_information (gpointer userdata)
2621 {
2622         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2623         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2624
2625         /* We're outside the main lock */
2626         gdk_threads_enter ();
2627
2628         if (priv->remove_attachment_banner != NULL) {
2629                 gtk_widget_destroy (priv->remove_attachment_banner);
2630                 g_object_unref (priv->remove_attachment_banner);
2631         }
2632
2633         priv->remove_attachment_banner = g_object_ref (
2634                 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2635
2636         gdk_threads_leave ();
2637
2638         return FALSE;
2639 }
2640
2641 void
2642 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2643 {
2644         ModestMsgViewWindowPrivate *priv;
2645         TnyList *mime_parts = NULL;
2646         gchar *confirmation_message;
2647         gint response;
2648         gint n_attachments;
2649         TnyMsg *msg;
2650         TnyIterator *iter;
2651 /*      TnyFolder *folder; */
2652
2653         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2654         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2655
2656         if (get_all)
2657                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2658         else
2659                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2660                 
2661         /* Remove already purged messages from mime parts list */
2662         iter = tny_list_create_iterator (mime_parts);
2663         while (!tny_iterator_is_done (iter)) {
2664                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2665                 tny_iterator_next (iter);
2666                 if (tny_mime_part_is_purged (part)) {
2667                         tny_list_remove (mime_parts, (GObject *) part);
2668                 }
2669                 g_object_unref (part);
2670         }
2671         g_object_unref (iter);
2672
2673         if (tny_list_get_length (mime_parts) == 0) {
2674                 g_object_unref (mime_parts);
2675                 return;
2676         }
2677
2678         n_attachments = tny_list_get_length (mime_parts);
2679         if (n_attachments == 1) {
2680                 const gchar *filename;
2681                 TnyMimePart *part;
2682
2683                 iter = tny_list_create_iterator (mime_parts);
2684                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2685                 g_object_unref (iter);
2686                 if (TNY_IS_MSG (part)) {
2687                         TnyHeader *header;
2688                         header = tny_msg_get_header (TNY_MSG (part));
2689                         filename = tny_header_get_subject (header);
2690                         g_object_unref (header);
2691                         if (filename == NULL)
2692                                 filename = _("mail_va_no_subject");
2693                 } else {
2694                         filename = tny_mime_part_get_filename (TNY_MIME_PART (part));
2695                 }
2696                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2697                 g_object_unref (part);
2698         } else {
2699                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2700                                                                  "mcen_nc_purge_files_text", 
2701                                                                  n_attachments), n_attachments);
2702         }
2703         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2704                                                             confirmation_message);
2705         g_free (confirmation_message);
2706
2707         if (response != GTK_RESPONSE_OK) {
2708                 g_object_unref (mime_parts);
2709                 return;
2710         }
2711
2712         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2713 /*      folder = tny_msg_get_folder (msg); */
2714 /*      tny_msg_uncache_attachments (msg); */
2715 /*      tny_folder_refresh (folder, NULL); */
2716 /*      g_object_unref (folder); */
2717         
2718         iter = tny_list_create_iterator (mime_parts);
2719         while (!tny_iterator_is_done (iter)) {
2720                 TnyMimePart *part;
2721
2722                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2723                 tny_mime_part_set_purged (TNY_MIME_PART (part));
2724 /*              modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2725                 g_object_unref (part);
2726                 tny_iterator_next (iter);
2727         }
2728         g_object_unref (iter);
2729
2730         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2731         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2732         tny_msg_rewrite_cache (msg);
2733         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2734         g_object_unref (msg);
2735
2736         g_object_unref (mime_parts);
2737
2738         if (priv->purge_timeout > 0) {
2739                 g_source_remove (priv->purge_timeout);
2740                 priv->purge_timeout = 0;
2741         }
2742
2743         if (priv->remove_attachment_banner) {
2744                 gtk_widget_destroy (priv->remove_attachment_banner);
2745                 g_object_unref (priv->remove_attachment_banner);
2746                 priv->remove_attachment_banner = NULL;
2747         }
2748
2749
2750 }
2751
2752
2753 static void
2754 update_window_title (ModestMsgViewWindow *window)
2755 {
2756         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2757         TnyMsg *msg = NULL;
2758         TnyHeader *header = NULL;
2759         const gchar *subject = NULL;
2760         
2761         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2762
2763         if (msg != NULL) {
2764                 header = tny_msg_get_header (msg);
2765                 subject = tny_header_get_subject (header);
2766                 g_object_unref (msg);
2767         }
2768
2769         if ((subject == NULL)||(subject[0] == '\0'))
2770                 subject = _("mail_va_no_subject");
2771
2772         gtk_window_set_title (GTK_WINDOW (window), subject);
2773 }
2774
2775
2776 static void on_move_focus (GtkWidget *widget,
2777                            GtkDirectionType direction,
2778                            gpointer userdata)
2779 {
2780         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2781 }
2782