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