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