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