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