a8fa99e0b359a1fa0eff6db4a5ff98dbfe2e9df2
[modest] / src / hildon2 / 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 <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
64 #include <math.h>
65 #include <errno.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
72
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
75
76         GtkWidget   *msg_view;
77         GtkWidget   *main_scroll;
78         GtkWidget   *find_toolbar;
79         gchar       *last_search;
80
81         /* Progress observers */
82         GSList           *progress_widgets;
83
84         /* Tollbar items */
85         GtkWidget   *prev_toolitem;
86         GtkWidget   *next_toolitem;
87         gboolean    progress_hint;
88         gint        fetching_images;
89
90         /* Optimized view enabled */
91         gboolean optimized_view;
92
93         /* Whether this was created via the *_new_for_search_result() function. */
94         gboolean is_search_result;
95
96         /* Whether the message is in outbox */
97         gboolean is_outbox;
98
99         /* A reference to the @model of the header view 
100          * to allow selecting previous/next messages,
101          * if the message is currently selected in the header view.
102          */
103         const gchar *header_folder_id;
104         GtkTreeModel *header_model;
105         GtkTreeRowReference *row_reference;
106         GtkTreeRowReference *next_row_reference;
107
108         gulong clipboard_change_handler;
109         gulong queue_change_handler;
110         gulong account_removed_handler;
111         gulong row_changed_handler;
112         gulong row_deleted_handler;
113         gulong row_inserted_handler;
114         gulong rows_reordered_handler;
115
116         guint purge_timeout;
117         GtkWidget *remove_attachment_banner;
118
119         gchar *msg_uid;
120
121         GSList *sighandlers;
122 };
123
124 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
125 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
126 static void  modest_header_view_observer_init(
127                 ModestHeaderViewObserverIface *iface_class);
128 static void  modest_msg_view_window_finalize     (GObject *obj);
129 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
130                                                          gpointer data);
131 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132                                                         ModestMsgViewWindow *obj);
133 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134                                                         ModestMsgViewWindow *obj);
135
136 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
137
138 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
139 static void modest_msg_view_window_set_zoom (ModestWindow *window,
140                                              gdouble zoom);
141 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
142 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
143 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
144                                                   GdkEventKey *event,
145                                                   gpointer userdata);
146 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
147
148 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
149                                                    gboolean show_toolbar);
150
151 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
152                                                            GdkEvent *event,
153                                                            ModestMsgViewWindow *window);
154
155 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
156                                                    GtkTreePath *arg1,
157                                                    GtkTreeIter *arg2,
158                                                    ModestMsgViewWindow *window);
159
160 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
161                                                    GtkTreePath *arg1,
162                                                    ModestMsgViewWindow *window);
163
164 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
165                                                     GtkTreePath *tree_path,
166                                                     GtkTreeIter *tree_iter,
167                                                     ModestMsgViewWindow *window);
168
169 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
170                                                      GtkTreePath *arg1,
171                                                      GtkTreeIter *arg2,
172                                                      gpointer arg3,
173                                                      ModestMsgViewWindow *window);
174
175 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
176                                                           GtkTreeModel *model,
177                                                           const gchar *tny_folder_id);
178
179 static void on_queue_changed    (ModestMailOperationQueue *queue,
180                                  ModestMailOperation *mail_op,
181                                  ModestMailOperationQueueNotification type,
182                                  ModestMsgViewWindow *self);
183
184 static void on_account_removed  (TnyAccountStore *account_store, 
185                                  TnyAccount *account,
186                                  gpointer user_data);
187
188 static void on_move_focus (GtkWidget *widget,
189                            GtkDirectionType direction,
190                            gpointer userdata);
191
192 static void view_msg_cb         (ModestMailOperation *mail_op, 
193                                  TnyHeader *header, 
194                                  gboolean canceled,
195                                  TnyMsg *msg, 
196                                  GError *error,
197                                  gpointer user_data);
198
199 static void set_progress_hint    (ModestMsgViewWindow *self, 
200                                   gboolean enabled);
201
202 static void update_window_title (ModestMsgViewWindow *window);
203
204 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
205 static void init_window (ModestMsgViewWindow *obj);
206
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
208
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
210
211 static gboolean on_fetch_image (ModestMsgView *msgview,
212                                 const gchar *uri,
213                                 TnyStream *stream,
214                                 ModestMsgViewWindow *window);
215
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217                                                      GtkScrollType scroll_type,
218                                                      gboolean horizontal,
219                                                      gpointer userdata);
220 static gboolean message_reader (ModestMsgViewWindow *window,
221                                 ModestMsgViewWindowPrivate *priv,
222                                 TnyHeader *header,
223                                 GtkTreeRowReference *row_reference);
224
225 static void setup_menu (ModestMsgViewWindow *self);
226 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
227                                                    GdkEvent *event,
228                                                    gpointer userdata);
229
230
231 /* list my signals */
232 enum {
233         MSG_CHANGED_SIGNAL,
234         SCROLL_CHILD_SIGNAL,
235         LAST_SIGNAL
236 };
237
238 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
239         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
240 };
241
242
243 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
244                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
245                                                     ModestMsgViewWindowPrivate))
246 /* globals */
247 static GtkWindowClass *parent_class = NULL;
248
249 /* uncomment the following if you have defined any signals */
250 static guint signals[LAST_SIGNAL] = {0};
251
252 GType
253 modest_msg_view_window_get_type (void)
254 {
255         static GType my_type = 0;
256         if (!my_type) {
257                 static const GTypeInfo my_info = {
258                         sizeof(ModestMsgViewWindowClass),
259                         NULL,           /* base init */
260                         NULL,           /* base finalize */
261                         (GClassInitFunc) modest_msg_view_window_class_init,
262                         NULL,           /* class finalize */
263                         NULL,           /* class data */
264                         sizeof(ModestMsgViewWindow),
265                         1,              /* n_preallocs */
266                         (GInstanceInitFunc) modest_msg_view_window_init,
267                         NULL
268                 };
269                 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
270                                                   "ModestMsgViewWindow",
271                                                   &my_info, 0);
272
273                 static const GInterfaceInfo modest_header_view_observer_info = 
274                 {
275                         (GInterfaceInitFunc) modest_header_view_observer_init,
276                         NULL,         /* interface_finalize */
277                         NULL          /* interface_data */
278                 };
279
280                 g_type_add_interface_static (my_type,
281                                 MODEST_TYPE_HEADER_VIEW_OBSERVER,
282                                 &modest_header_view_observer_info);
283         }
284         return my_type;
285 }
286
287 static void
288 save_state (ModestWindow *self)
289 {
290         modest_widget_memory_save (modest_runtime_get_conf (),
291                                    G_OBJECT(self), 
292                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
293 }
294
295 static 
296 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
297                                               GtkScrollType scroll_type,
298                                               gboolean horizontal,
299                                               gpointer userdata)
300 {
301         ModestMsgViewWindowPrivate *priv;
302         gboolean return_value;
303
304         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
305         g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
306         return return_value;
307 }
308
309 static void
310 add_scroll_binding (GtkBindingSet *binding_set,
311                     guint keyval,
312                     GtkScrollType scroll)
313 {
314         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
315         
316         gtk_binding_entry_add_signal (binding_set, keyval, 0,
317                                       "scroll_child", 2,
318                                       GTK_TYPE_SCROLL_TYPE, scroll,
319                                       G_TYPE_BOOLEAN, FALSE);
320         gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
321                                       "scroll_child", 2,
322                                       GTK_TYPE_SCROLL_TYPE, scroll,
323                                       G_TYPE_BOOLEAN, FALSE);
324 }
325
326 static void
327 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
328 {
329         GObjectClass *gobject_class;
330         HildonWindowClass *hildon_window_class;
331         ModestWindowClass *modest_window_class;
332         GtkBindingSet *binding_set;
333
334         gobject_class = (GObjectClass*) klass;
335         hildon_window_class = (HildonWindowClass *) klass;
336         modest_window_class = (ModestWindowClass *) klass;
337
338         parent_class            = g_type_class_peek_parent (klass);
339         gobject_class->finalize = modest_msg_view_window_finalize;
340
341         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
342         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
343         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
344         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
345         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
346         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
347
348         modest_window_class->save_state_func = save_state;
349
350         klass->scroll_child = modest_msg_view_window_scroll_child;
351
352         signals[MSG_CHANGED_SIGNAL] =
353                 g_signal_new ("msg-changed",
354                               G_TYPE_FROM_CLASS (gobject_class),
355                               G_SIGNAL_RUN_FIRST,
356                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
357                               NULL, NULL,
358                               modest_marshal_VOID__POINTER_POINTER,
359                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
360
361         signals[SCROLL_CHILD_SIGNAL] =
362                 g_signal_new ("scroll-child",
363                               G_TYPE_FROM_CLASS (gobject_class),
364                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
365                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
366                               NULL, NULL,
367                               modest_marshal_BOOLEAN__ENUM_BOOLEAN,
368                               G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
369
370         binding_set = gtk_binding_set_by_class (klass);
371         add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
372         add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
373         add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
374         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
375         add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
376         add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
377
378         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
379
380 }
381
382 static void modest_header_view_observer_init(
383                 ModestHeaderViewObserverIface *iface_class)
384 {
385         iface_class->update_func = modest_msg_view_window_update_model_replaced;
386 }
387
388 static void
389 modest_msg_view_window_init (ModestMsgViewWindow *obj)
390 {
391         ModestMsgViewWindowPrivate *priv;
392         ModestWindowPrivate *parent_priv = NULL;
393         GtkActionGroup *action_group = NULL;
394         GError *error = NULL;
395         GdkPixbuf *window_icon;
396
397         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
398         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
399         parent_priv->ui_manager = gtk_ui_manager_new();
400
401         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
402         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
403
404         /* Add common actions */
405         gtk_action_group_add_actions (action_group,
406                                       modest_action_entries,
407                                       G_N_ELEMENTS (modest_action_entries),
408                                       obj);
409         gtk_action_group_add_toggle_actions (action_group,
410                                              msg_view_toggle_action_entries,
411                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
412                                              obj);
413
414         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
415         g_object_unref (action_group);
416
417         /* Load the UI definition */
418         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
419                                          &error);
420         if (error) {
421                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
422                 g_error_free (error);
423                 error = NULL;
424         }
425         /* ****** */
426
427         /* Add accelerators */
428         gtk_window_add_accel_group (GTK_WINDOW (obj), 
429                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
430         
431         priv->is_search_result = FALSE;
432         priv->is_outbox = FALSE;
433
434         priv->msg_view      = NULL;
435         priv->header_model  = NULL;
436         priv->header_folder_id  = NULL;
437         priv->clipboard_change_handler = 0;
438         priv->queue_change_handler = 0;
439         priv->account_removed_handler = 0;
440         priv->row_changed_handler = 0;
441         priv->row_deleted_handler = 0;
442         priv->row_inserted_handler = 0;
443         priv->rows_reordered_handler = 0;
444         priv->progress_hint = FALSE;
445         priv->fetching_images = 0;
446
447         priv->optimized_view  = FALSE;
448         priv->purge_timeout = 0;
449         priv->remove_attachment_banner = NULL;
450         priv->msg_uid = NULL;
451         
452         priv->sighandlers = NULL;
453         
454         /* Init window */
455         init_window (MODEST_MSG_VIEW_WINDOW(obj));
456         
457         /* Set window icon */
458         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG); 
459         if (window_icon) {
460                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
461                 g_object_unref (window_icon);
462         }       
463         
464         hildon_program_add_window (hildon_program_get_instance(),
465                                    HILDON_WINDOW(obj));
466
467 }
468
469
470 static gboolean
471 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
472 {
473         ModestMsgViewWindowPrivate *priv = NULL;
474         
475         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
476
477         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
478
479         set_progress_hint (self, TRUE);
480         
481         return FALSE;
482 }
483
484 static void
485 update_progress_hint (ModestMsgViewWindow *self)
486 {
487         ModestMsgViewWindowPrivate *priv;
488         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
489
490         if (GTK_WIDGET_VISIBLE (self)) {
491                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), 
492                                                           (priv->progress_hint || (priv->fetching_images > 0))?1:0);
493         }
494 }
495
496 static void 
497 set_progress_hint (ModestMsgViewWindow *self, 
498                    gboolean enabled)
499 {
500         ModestWindowPrivate *parent_priv;
501         ModestMsgViewWindowPrivate *priv;
502
503         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
504
505         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
506         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
507                         
508         /* Sets current progress hint */
509         priv->progress_hint = enabled;
510
511         update_progress_hint (self);
512
513 }
514
515
516 static void
517 init_window (ModestMsgViewWindow *obj)
518 {
519         GtkWidget *main_vbox;
520         ModestMsgViewWindowPrivate *priv;
521
522         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
523
524         priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
525         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
526         main_vbox = gtk_vbox_new  (FALSE, 6);
527         priv->main_scroll = hildon_pannable_area_new ();
528         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
529         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
530         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
531
532         priv->find_toolbar = hildon_find_toolbar_new (NULL);
533         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
534         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
535
536         /* NULL-ize fields if the window is destroyed */
537         g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
538
539         gtk_widget_show_all (GTK_WIDGET(main_vbox));
540 }
541
542 static void
543 modest_msg_view_window_disconnect_signals (ModestWindow *self)
544 {
545         ModestMsgViewWindowPrivate *priv;
546         GtkWidget *header_view = NULL;
547         GtkWindow *parent_window = NULL;
548         
549         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
550
551         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
552             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
553                                            priv->clipboard_change_handler)) 
554                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
555                                              priv->clipboard_change_handler);
556
557         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
558                                            priv->queue_change_handler))
559                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
560                                              priv->queue_change_handler);
561
562         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
563                                            priv->account_removed_handler))
564                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
565                                              priv->account_removed_handler);
566
567         if (priv->header_model) {
568                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
569                                                   priv->row_changed_handler))
570                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
571                                                     priv->row_changed_handler);
572                 
573                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
574                                                   priv->row_deleted_handler))
575                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
576                                              priv->row_deleted_handler);
577                 
578                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
579                                                   priv->row_inserted_handler))
580                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
581                                                     priv->row_inserted_handler);
582                 
583                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
584                                                   priv->rows_reordered_handler))
585                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
586                                                     priv->rows_reordered_handler);
587         }
588
589         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
590         priv->sighandlers = NULL;
591
592         parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
593         if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
594                 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
595                 if (header_view) {
596                         modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
597                                                            MODEST_HEADER_VIEW_OBSERVER(self));
598                 }
599         }
600 }       
601
602 static void
603 modest_msg_view_window_finalize (GObject *obj)
604 {
605         ModestMsgViewWindowPrivate *priv;
606
607         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
608
609         /* Sanity check: shouldn't be needed, the window mgr should
610            call this function before */
611         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
612
613         if (priv->header_model != NULL) {
614                 g_object_unref (priv->header_model);
615                 priv->header_model = NULL;
616         }
617
618         if (priv->remove_attachment_banner) {
619                 gtk_widget_destroy (priv->remove_attachment_banner);
620                 g_object_unref (priv->remove_attachment_banner);
621                 priv->remove_attachment_banner = NULL;
622         }
623
624         if (priv->purge_timeout > 0) {
625                 g_source_remove (priv->purge_timeout);
626                 priv->purge_timeout = 0;
627         }
628
629         if (priv->row_reference) {
630                 gtk_tree_row_reference_free (priv->row_reference);
631                 priv->row_reference = NULL;
632         }
633
634         if (priv->next_row_reference) {
635                 gtk_tree_row_reference_free (priv->next_row_reference);
636                 priv->next_row_reference = NULL;
637         }
638
639         if (priv->msg_uid) {
640                 g_free (priv->msg_uid);
641                 priv->msg_uid = NULL;
642         }
643
644         G_OBJECT_CLASS(parent_class)->finalize (obj);
645 }
646
647 static gboolean
648 select_next_valid_row (GtkTreeModel *model,
649                        GtkTreeRowReference **row_reference,
650                        gboolean cycle,
651                        gboolean is_outbox)
652 {
653         GtkTreeIter tmp_iter;
654         GtkTreePath *path;
655         GtkTreePath *next = NULL;
656         gboolean retval = FALSE, finished;
657
658         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
659
660         path = gtk_tree_row_reference_get_path (*row_reference);
661         gtk_tree_model_get_iter (model, &tmp_iter, path);
662         gtk_tree_row_reference_free (*row_reference);
663         *row_reference = NULL;
664
665         finished = FALSE;
666         do {
667                 TnyHeader *header = NULL;
668
669                 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
670                         gtk_tree_model_get (model, &tmp_iter, 
671                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
672                                             &header, -1);
673
674                         if (header) {
675                                 if (msg_is_visible (header, is_outbox)) {
676                                         next = gtk_tree_model_get_path (model, &tmp_iter);
677                                         *row_reference = gtk_tree_row_reference_new (model, next);
678                                         gtk_tree_path_free (next);
679                                         retval = TRUE;
680                                         finished = TRUE;
681                                 }
682                                 g_object_unref (header);
683                                 header = NULL;
684                         }
685                 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
686                         next = gtk_tree_model_get_path (model, &tmp_iter);
687                         
688                         /* Ensure that we are not selecting the same */
689                         if (gtk_tree_path_compare (path, next) != 0) {
690                                 gtk_tree_model_get (model, &tmp_iter, 
691                                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
692                                                     &header, -1);                               
693                                 if (header) {
694                                         if (msg_is_visible (header, is_outbox)) {
695                                                 *row_reference = gtk_tree_row_reference_new (model, next);
696                                                 retval = TRUE;
697                                                 finished = TRUE;
698                                         }
699                                         g_object_unref (header);
700                                         header = NULL;
701                                 }
702                         } else {
703                                 /* If we ended up in the same message
704                                    then there is no valid next
705                                    message */
706                                 finished = TRUE;
707                         }
708                         gtk_tree_path_free (next);
709                 } else {
710                         /* If there are no more messages and we don't
711                            want to start again in the first one then
712                            there is no valid next message */
713                         finished = TRUE;
714                 }
715         } while (!finished);
716
717         /* Free */
718         gtk_tree_path_free (path);
719
720         return retval;
721 }
722
723 /* TODO: This should be in _init(), with the parameters as properties. */
724 static void
725 modest_msg_view_window_construct (ModestMsgViewWindow *self, 
726                                   const gchar *modest_account_name,
727                                   const gchar *mailbox,
728                                   const gchar *msg_uid)
729 {
730         GObject *obj = NULL;
731         ModestMsgViewWindowPrivate *priv = NULL;
732         ModestWindowPrivate *parent_priv = NULL;
733         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
734         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
735
736         obj = G_OBJECT (self);
737         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
739
740         priv->msg_uid = g_strdup (msg_uid);
741
742         /* Menubar */
743         parent_priv->menubar = NULL;
744
745         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
746         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
747
748         setup_menu (self);
749         /* Add common dimming rules */
750         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
751                                               modest_msg_view_toolbar_dimming_entries,
752                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
753                                               MODEST_WINDOW (self));
754         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
755                                               modest_msg_view_clipboard_dimming_entries,
756                                               G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
757                                               MODEST_WINDOW (self));
758
759         /* Insert dimming rules group for this window */
760         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
761         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
762         g_object_unref (toolbar_rules_group);
763         g_object_unref (clipboard_rules_group);
764
765         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
766
767         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);
768         g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
769                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
770         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
771                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
772         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
773                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
774         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
775                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
776         g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
777                           G_CALLBACK (modest_ui_actions_on_details), obj);
778         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
779                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
780         g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
781                           G_CALLBACK (on_fetch_image), obj);
782
783         g_signal_connect (G_OBJECT (obj), "key-release-event",
784                           G_CALLBACK (modest_msg_view_window_key_event),
785                           NULL);
786
787         g_signal_connect (G_OBJECT (obj), "key-press-event",
788                           G_CALLBACK (modest_msg_view_window_key_event),
789                           NULL);
790
791         g_signal_connect (G_OBJECT (obj), "move-focus",
792                           G_CALLBACK (on_move_focus), obj);
793
794         g_signal_connect (G_OBJECT (obj), "map-event",
795                           G_CALLBACK (_modest_msg_view_window_map_event),
796                           G_OBJECT (obj));
797
798         /* Mail Operation Queue */
799         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
800                                                        "queue-changed",
801                                                        G_CALLBACK (on_queue_changed),
802                                                        obj);
803
804         /* Account manager */
805         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
806                                                           "account_removed",
807                                                           G_CALLBACK(on_account_removed),
808                                                           obj);
809
810         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
811         modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
812
813         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
814         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
815         priv->last_search = NULL;
816
817         modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
818
819         /* Init the clipboard actions dim status */
820         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
821
822         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
823
824
825 }
826
827 /* FIXME: parameter checks */
828 ModestWindow *
829 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
830                                               const gchar *modest_account_name,
831                                               const gchar *mailbox,
832                                               const gchar *msg_uid,
833                                               GtkTreeModel *model, 
834                                               GtkTreeRowReference *row_reference)
835 {
836         ModestMsgViewWindow *window = NULL;
837         ModestMsgViewWindowPrivate *priv = NULL;
838         TnyFolder *header_folder = NULL;
839         ModestHeaderView *header_view = NULL;
840         ModestWindowMgr *mgr = NULL;
841
842         MODEST_DEBUG_BLOCK (
843                modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
844         );
845
846         mgr = modest_runtime_get_window_mgr ();
847         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
848         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
849
850         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
851
852         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
853
854         /* Remember the message list's TreeModel so we can detect changes
855          * and change the list selection when necessary: */
856         header_folder = modest_header_view_get_folder (header_view);
857         if (header_folder) {
858                 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
859                                    TNY_FOLDER_TYPE_OUTBOX);
860                 priv->header_folder_id = tny_folder_get_id (header_folder);
861                 g_object_unref(header_folder);
862         }
863
864         /* Setup row references and connect signals */
865         priv->header_model = g_object_ref (model);
866
867         if (row_reference) {
868                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
869                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
870                 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
871         } else {
872                 priv->row_reference = NULL;
873                 priv->next_row_reference = NULL;
874         }
875
876         /* Connect signals */
877         priv->row_changed_handler = 
878                 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
879                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
880                                   window);
881         priv->row_deleted_handler = 
882                 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
883                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
884                                   window);
885         priv->row_inserted_handler = 
886                 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
887                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
888                                   window);
889         priv->rows_reordered_handler = 
890                 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
891                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
892                                  window);
893
894         if (header_view != NULL){
895                 modest_header_view_add_observer(header_view,
896                                 MODEST_HEADER_VIEW_OBSERVER(window));
897         }
898
899         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
900         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
901
902         /* gtk_widget_show_all (GTK_WIDGET (window)); */
903         modest_msg_view_window_update_priority (window);
904         /* Check dimming rules */
905         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
906         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
907         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
908
909         return MODEST_WINDOW(window);
910 }
911
912 ModestWindow *
913 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
914                                              const gchar *modest_account_name,
915                                              const gchar *mailbox,
916                                              const gchar *msg_uid,
917                                              GtkTreeRowReference *row_reference)
918 {
919         ModestMsgViewWindow *window = NULL;
920         ModestMsgViewWindowPrivate *priv = NULL;
921         TnyFolder *header_folder = NULL;
922         ModestWindowMgr *mgr = NULL;
923         GtkTreePath *path;
924         GtkTreeIter iter;
925
926         mgr = modest_runtime_get_window_mgr ();
927         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
928         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
929
930         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
931
932         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
933
934         /* Remember the message list's TreeModel so we can detect changes 
935          * and change the list selection when necessary: */
936
937         if (header_view != NULL){
938                 header_folder = modest_header_view_get_folder(header_view);
939                 /* This could happen if the header folder was
940                    unseleted before opening this msg window (for
941                    example if the user selects an account in the
942                    folder view of the main window */
943                 if (header_folder) {
944                         priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == 
945                                            TNY_FOLDER_TYPE_OUTBOX);
946                         priv->header_folder_id = tny_folder_get_id(header_folder);
947                         g_object_unref(header_folder);
948                 }
949         }
950
951         /* Setup row references and connect signals */
952         priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
953         g_object_ref (priv->header_model);
954
955         if (row_reference) {
956                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
957                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
958                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
959         } else {
960                 priv->row_reference = NULL;
961                 priv->next_row_reference = NULL;
962         }
963
964         /* Connect signals */
965         priv->row_changed_handler = 
966                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
967                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
968                                   window);
969         priv->row_deleted_handler = 
970                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
971                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
972                                   window);
973         priv->row_inserted_handler = 
974                 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
975                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
976                                   window);
977         priv->rows_reordered_handler = 
978                 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
979                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
980                                  window);
981
982         if (header_view != NULL){
983                 modest_header_view_add_observer(header_view,
984                                                 MODEST_HEADER_VIEW_OBSERVER(window));
985         }
986
987         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
988
989         path = gtk_tree_row_reference_get_path (row_reference);
990         if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
991                 TnyHeader *header;
992                 gtk_tree_model_get (priv->header_model, &iter, 
993                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
994                                     &header, -1);
995                 message_reader (window, priv, header, row_reference);
996         }
997         gtk_tree_path_free (path);
998
999         /* Check dimming rules */
1000         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1001         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1002         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1003
1004         return MODEST_WINDOW(window);
1005 }
1006
1007 ModestWindow *
1008 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
1009                                               const gchar *modest_account_name,
1010                                               const gchar *mailbox,
1011                                               const gchar *msg_uid)
1012 {
1013         ModestMsgViewWindow *window = NULL;
1014         ModestMsgViewWindowPrivate *priv = NULL;
1015         ModestWindowMgr *mgr = NULL;
1016
1017         mgr = modest_runtime_get_window_mgr ();
1018         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1019         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1020         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1021
1022         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1023
1024         /* Remember that this is a search result, 
1025          * so we can disable some UI appropriately: */
1026         priv->is_search_result = TRUE;
1027
1028         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1029         
1030         update_window_title (window);
1031         /* gtk_widget_show_all (GTK_WIDGET (window));*/
1032         modest_msg_view_window_update_priority (window);
1033
1034         /* Check dimming rules */
1035         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1036         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1037         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1038
1039         return MODEST_WINDOW(window);
1040 }
1041
1042 ModestWindow *
1043 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
1044                                            const gchar *modest_account_name,
1045                                            const gchar *mailbox,
1046                                            const gchar *msg_uid)
1047 {
1048         GObject *obj = NULL;
1049         ModestMsgViewWindowPrivate *priv;       
1050         ModestWindowMgr *mgr = NULL;
1051
1052         g_return_val_if_fail (msg, NULL);
1053         mgr = modest_runtime_get_window_mgr ();
1054         obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1055         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1056         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
1057                                           modest_account_name, mailbox, msg_uid);
1058
1059         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1060         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1061
1062         /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1063
1064         /* Check dimming rules */
1065         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1066         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1067         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1068
1069         return MODEST_WINDOW(obj);
1070 }
1071
1072 static void
1073 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1074                                        GtkTreePath *arg1,
1075                                        GtkTreeIter *arg2,
1076                                        ModestMsgViewWindow *window)
1077 {
1078         check_dimming_rules_after_change (window);
1079 }
1080
1081 static void 
1082 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1083                                       GtkTreePath *arg1,
1084                                       ModestMsgViewWindow *window)
1085 {
1086         check_dimming_rules_after_change (window);
1087 }
1088         /* The window could have dissapeared */
1089
1090 static void
1091 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1092 {
1093         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1094         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1095 }
1096
1097
1098 /* On insertions we check if the folder still has the message we are
1099  * showing or do not. If do not, we do nothing. Which means we are still
1100  * not attached to any header folder and thus next/prev buttons are
1101  * still dimmed. Once the message that is shown by msg-view is found, the
1102  * new model of header-view will be attached and the references will be set.
1103  * On each further insertions dimming rules will be checked. However
1104  * this requires extra CPU time at least works.
1105  * (An message might be deleted from TnyFolder and thus will not be
1106  * inserted into the model again for example if it is removed by the
1107  * imap server and the header view is refreshed.)
1108  */
1109 static void 
1110 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1111                                         GtkTreePath *tree_path,
1112                                         GtkTreeIter *tree_iter,
1113                                         ModestMsgViewWindow *window)
1114 {
1115         ModestMsgViewWindowPrivate *priv = NULL; 
1116         TnyHeader *header = NULL;
1117
1118         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1119         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1120
1121         g_assert (model == priv->header_model);
1122         
1123         /* Check if the newly inserted message is the same we are actually
1124          * showing. IF not, we should remain detached from the header model
1125          * and thus prev and next toolbar buttons should remain dimmed. */
1126         gtk_tree_model_get (model, tree_iter, 
1127                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1128                             &header, -1);
1129
1130         if (TNY_IS_HEADER (header)) {
1131                 gchar *uid = NULL;
1132
1133                 uid = modest_tny_folder_get_header_unique_id (header);
1134                 if (!g_str_equal(priv->msg_uid, uid)) {
1135                         check_dimming_rules_after_change (window);
1136                         g_free(uid);
1137                         g_object_unref (G_OBJECT(header));
1138                         return;
1139                 }
1140                 g_free(uid);
1141                 g_object_unref(G_OBJECT(header));
1142         }
1143
1144         if (priv->row_reference) {
1145                 gtk_tree_row_reference_free (priv->row_reference); 
1146         }
1147
1148         /* Setup row_reference for the actual msg. */
1149         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1150         if (priv->row_reference == NULL) {
1151                 g_warning("No reference for msg header item.");
1152                 return;
1153         }
1154
1155         /* Now set up next_row_reference. */
1156         if (priv->next_row_reference) {
1157                 gtk_tree_row_reference_free (priv->next_row_reference); 
1158         }
1159
1160         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1161         select_next_valid_row (priv->header_model,
1162                                &(priv->next_row_reference), FALSE, priv->is_outbox);
1163
1164         /* Connect the remaining callbacks to become able to detect
1165          * changes in header-view. */
1166         priv->row_changed_handler = 
1167                 g_signal_connect (priv->header_model, "row-changed",
1168                                   G_CALLBACK (modest_msg_view_window_on_row_changed),
1169                                   window);
1170         priv->row_deleted_handler = 
1171                 g_signal_connect (priv->header_model, "row-deleted",
1172                                   G_CALLBACK (modest_msg_view_window_on_row_deleted),
1173                                   window);
1174         priv->rows_reordered_handler = 
1175                 g_signal_connect (priv->header_model, "rows-reordered",
1176                                   G_CALLBACK (modest_msg_view_window_on_row_reordered),
1177                                   window);
1178
1179         check_dimming_rules_after_change (window);      
1180 }
1181
1182 static void 
1183 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1184                                          GtkTreePath *arg1,
1185                                          GtkTreeIter *arg2,
1186                                          gpointer arg3,
1187                                          ModestMsgViewWindow *window)
1188 {
1189         ModestMsgViewWindowPrivate *priv = NULL; 
1190         gboolean already_changed = FALSE;
1191
1192         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1193
1194         /* If the current row was reordered select the proper next
1195            valid row. The same if the next row reference changes */
1196         if (priv->row_reference && 
1197             gtk_tree_row_reference_valid (priv->row_reference)) {
1198                 GtkTreePath *path;
1199                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200                 if (gtk_tree_path_compare (path, arg1) == 0) {
1201                         if (priv->next_row_reference) {
1202                                 gtk_tree_row_reference_free (priv->next_row_reference);
1203                         }
1204                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1206                         already_changed = TRUE;
1207                 }
1208                 gtk_tree_path_free (path);
1209         }
1210         if (!already_changed &&
1211             priv->next_row_reference &&
1212             gtk_tree_row_reference_valid (priv->next_row_reference)) {
1213                 GtkTreePath *path;
1214                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1215                 if (gtk_tree_path_compare (path, arg1) == 0) {
1216                         if (priv->next_row_reference) {
1217                                 gtk_tree_row_reference_free (priv->next_row_reference);
1218                         }
1219                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1221                 }
1222                 gtk_tree_path_free (path);
1223         }
1224         check_dimming_rules_after_change (window);
1225 }
1226
1227 /* The modest_msg_view_window_update_model_replaced implements update
1228  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1229  * actually belongs to the header-view is the same as the TnyFolder of
1230  * the message of msg-view or not. If they are different, there is
1231  * nothing to do. If they are the same, then the model has replaced and
1232  * the reference in msg-view shall be replaced from the old model to
1233  * the new model. In this case the view will be detached from it's
1234  * header folder. From this point the next/prev buttons are dimmed.
1235  */
1236 static void 
1237 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1238                                               GtkTreeModel *model,
1239                                               const gchar *tny_folder_id)
1240 {
1241         ModestMsgViewWindowPrivate *priv = NULL; 
1242         ModestMsgViewWindow *window = NULL;
1243
1244         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1245         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1246
1247         window = MODEST_MSG_VIEW_WINDOW(observer);
1248         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1249
1250         /* If there is an other folder in the header-view then we do
1251          * not care about it's model (msg list). Else if the
1252          * header-view shows the folder the msg shown by us is in, we
1253          * shall replace our model reference and make some check. */
1254         if(model == NULL || tny_folder_id == NULL || 
1255            (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1256                 return;
1257
1258         /* Model is changed(replaced), so we should forget the old
1259          * one. Because there might be other references and there
1260          * might be some change on the model even if we unreferenced
1261          * it, we need to disconnect our signals here. */
1262         if (priv->header_model) {
1263                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1264                                                   priv->row_changed_handler))
1265                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1266                                                     priv->row_changed_handler);
1267                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1268                                                   priv->row_deleted_handler))
1269                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1270                                                     priv->row_deleted_handler);
1271                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1272                                                   priv->row_inserted_handler))
1273                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1274                                                     priv->row_inserted_handler);
1275                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1276                                                   priv->rows_reordered_handler))
1277                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1278                                                     priv->rows_reordered_handler);
1279
1280                 /* Frees */
1281                 if (priv->row_reference)
1282                         gtk_tree_row_reference_free (priv->row_reference);
1283                 if (priv->next_row_reference)
1284                         gtk_tree_row_reference_free (priv->next_row_reference);
1285                 g_object_unref(priv->header_model);
1286
1287                 /* Initialize */
1288                 priv->row_changed_handler = 0;
1289                 priv->row_deleted_handler = 0;
1290                 priv->row_inserted_handler = 0;
1291                 priv->rows_reordered_handler = 0;
1292                 priv->next_row_reference = NULL;
1293                 priv->row_reference = NULL;
1294                 priv->header_model = NULL;
1295         }
1296
1297         priv->header_model = g_object_ref (model);
1298
1299         /* Also we must connect to the new model for row insertions.
1300          * Only for insertions now. We will need other ones only after
1301          * the msg is show by msg-view is added to the new model. */
1302         priv->row_inserted_handler =
1303                 g_signal_connect (priv->header_model, "row-inserted",
1304                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1305                                   window);
1306
1307         modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1308         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1309 }
1310
1311 gboolean 
1312 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1313 {
1314         ModestMsgViewWindowPrivate *priv= NULL; 
1315
1316         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1317         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1318
1319         return priv->progress_hint;
1320 }
1321
1322 TnyHeader*
1323 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1324 {
1325         ModestMsgViewWindowPrivate *priv= NULL; 
1326         TnyMsg *msg = NULL;
1327         TnyHeader *header = NULL;
1328         GtkTreePath *path = NULL;
1329         GtkTreeIter iter;
1330
1331         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1332         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1333
1334         /* If the message was not obtained from a treemodel,
1335          * for instance if it was opened directly by the search UI:
1336          */
1337         if (priv->header_model == NULL || 
1338             priv->row_reference == NULL ||
1339             !gtk_tree_row_reference_valid (priv->row_reference)) {
1340                 msg = modest_msg_view_window_get_message (self);
1341                 if (msg) {
1342                         header = tny_msg_get_header (msg);
1343                         g_object_unref (msg);
1344                 }
1345                 return header;
1346         }
1347
1348         /* Get iter of the currently selected message in the header view: */
1349         path = gtk_tree_row_reference_get_path (priv->row_reference);
1350         g_return_val_if_fail (path != NULL, NULL);
1351         gtk_tree_model_get_iter (priv->header_model, 
1352                                  &iter, 
1353                                  path);
1354
1355         /* Get current message header */
1356         gtk_tree_model_get (priv->header_model, &iter, 
1357                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1358                             &header, -1);
1359
1360         gtk_tree_path_free (path);
1361         return header;
1362 }
1363
1364 TnyMsg*
1365 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1366 {
1367         ModestMsgViewWindowPrivate *priv;
1368         
1369         g_return_val_if_fail (self, NULL);
1370         
1371         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1372         
1373         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1374 }
1375
1376 const gchar*
1377 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1378 {
1379         ModestMsgViewWindowPrivate *priv;
1380
1381         g_return_val_if_fail (self, NULL);
1382         
1383         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1384
1385         return (const gchar*) priv->msg_uid;
1386 }
1387
1388 static void 
1389 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1390                                             gpointer data)
1391 {
1392         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1393         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1395         gboolean is_active;
1396         GtkAction *action;
1397
1398         is_active = gtk_toggle_action_get_active (toggle);
1399
1400         if (is_active) {
1401                 gtk_widget_show (priv->find_toolbar);
1402                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1403         } else {
1404                 gtk_widget_hide (priv->find_toolbar);
1405                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1406         }
1407
1408         /* update the toggle buttons status */
1409         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1410         if (action)
1411                 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1412
1413 }
1414
1415 static void
1416 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1417                                            ModestMsgViewWindow *obj)
1418 {
1419         GtkToggleAction *toggle;
1420         ModestWindowPrivate *parent_priv;
1421         ModestMsgViewWindowPrivate *priv;
1422
1423         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1425
1426         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1427         gtk_toggle_action_set_active (toggle, FALSE);
1428         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1429 }
1430
1431 static void
1432 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1433                                            ModestMsgViewWindow *obj)
1434 {
1435         gchar *current_search;
1436         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1437
1438         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1439                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1440                 return;
1441         }
1442
1443         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1444
1445         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1446                 g_free (current_search);
1447                 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1448                 return;
1449         }
1450
1451         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1452                 gboolean result;
1453                 g_free (priv->last_search);
1454                 priv->last_search = g_strdup (current_search);
1455                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1456                                                      priv->last_search);
1457                 if (!result) {
1458                         hildon_banner_show_information (NULL, NULL, 
1459                                                         _HL("ckct_ib_find_no_matches"));
1460                         g_free (priv->last_search);
1461                         priv->last_search = NULL;
1462                 } else {
1463                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1464                 }
1465         } else {
1466                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1467                         hildon_banner_show_information (NULL, NULL, 
1468                                                         _HL("ckct_ib_find_search_complete"));
1469                         g_free (priv->last_search);
1470                         priv->last_search = NULL;
1471                 } else {
1472                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1473                 }
1474         }
1475         
1476         g_free (current_search);
1477                 
1478 }
1479
1480 static void
1481 modest_msg_view_window_set_zoom (ModestWindow *window,
1482                                  gdouble zoom)
1483 {
1484         ModestMsgViewWindowPrivate *priv;
1485         ModestWindowPrivate *parent_priv;
1486      
1487         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1488
1489         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1490         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1491         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1492
1493 }
1494
1495 static gdouble
1496 modest_msg_view_window_get_zoom (ModestWindow *window)
1497 {
1498         ModestMsgViewWindowPrivate *priv;
1499      
1500         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1501
1502         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1503         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1504 }
1505
1506 static gboolean
1507 modest_msg_view_window_zoom_plus (ModestWindow *window)
1508 {
1509         gdouble zoom_level;
1510         ModestMsgViewWindowPrivate *priv;
1511         gint int_zoom;
1512         gchar *banner_text;
1513      
1514         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1515         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1516   
1517         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1518
1519         if (zoom_level >= 2.0) {
1520                 hildon_banner_show_information (NULL, NULL, 
1521                                                 _CS("ckct_ib_max_zoom_level_reached"));
1522                 return FALSE;
1523         } else if (zoom_level >= 1.5) {
1524                 zoom_level = 2.0;
1525         } else if (zoom_level >= 1.2) {
1526                 zoom_level = 1.5;
1527         } else if (zoom_level >= 1.0) {
1528                 zoom_level = 1.2;
1529         } else if (zoom_level >= 0.8) {
1530                 zoom_level = 1.0;
1531         } else if (zoom_level >= 0.5) {
1532                 zoom_level = 0.8;
1533         } else {
1534                 zoom_level = 0.5;
1535         }
1536
1537         /* set zoom level */
1538         int_zoom = (gint) rint (zoom_level*100.0+0.1);
1539         banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1540         modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1541         g_free (banner_text);
1542         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1543
1544         return TRUE;
1545 }
1546
1547 static gboolean
1548 modest_msg_view_window_zoom_minus (ModestWindow *window)
1549 {
1550         gdouble zoom_level;
1551         ModestMsgViewWindowPrivate *priv;
1552         gint int_zoom;
1553         gchar *banner_text;
1554      
1555         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1556         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1557   
1558         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1559
1560         if (zoom_level <= 0.5) {
1561                 hildon_banner_show_information (NULL, NULL, 
1562                                                 _CS("ckct_ib_min_zoom_level_reached"));
1563                 return FALSE;
1564         } else if (zoom_level <= 0.8) {
1565                 zoom_level = 0.5;
1566         } else if (zoom_level <= 1.0) {
1567                 zoom_level = 0.8;
1568         } else if (zoom_level <= 1.2) {
1569                 zoom_level = 1.0;
1570         } else if (zoom_level <= 1.5) {
1571                 zoom_level = 1.2;
1572         } else if (zoom_level <= 2.0) {
1573                 zoom_level = 1.5;
1574         } else {
1575                 zoom_level = 2.0;
1576         }
1577
1578         /* set zoom level */
1579         int_zoom = (gint) rint (zoom_level*100.0+0.1);
1580         banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1581         modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1582         g_free (banner_text);
1583         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1584
1585         return TRUE;
1586         
1587 }
1588
1589 static gboolean
1590 modest_msg_view_window_key_event (GtkWidget *window,
1591                                   GdkEventKey *event,
1592                                   gpointer userdata)
1593 {
1594         GtkWidget *focus;
1595
1596         focus = gtk_window_get_focus (GTK_WINDOW (window));
1597
1598         /* for the find toolbar case */
1599         if (focus && GTK_IS_ENTRY (focus)) {
1600                 if (event->keyval == GDK_BackSpace) {
1601                         GdkEvent *copy;
1602                         copy = gdk_event_copy ((GdkEvent *) event);
1603                         gtk_widget_event (focus, copy);
1604                         gdk_event_free (copy);
1605                         return TRUE;
1606                 } else 
1607                         return FALSE;
1608         }
1609         if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1610             event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1611             event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1612             event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1613             event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1614             event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1615                 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1616                 /* gboolean return_value; */
1617
1618                 if (event->type == GDK_KEY_PRESS) {
1619                         GtkScrollType scroll_type;
1620
1621                         switch (event->keyval) {
1622                         case GDK_Up: 
1623                         case GDK_KP_Up:
1624                                 scroll_type = GTK_SCROLL_STEP_UP; break;
1625                         case GDK_Down: 
1626                         case GDK_KP_Down:
1627                                 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1628                         case GDK_Page_Up:
1629                         case GDK_KP_Page_Up:
1630                                 scroll_type = GTK_SCROLL_PAGE_UP; break;
1631                         case GDK_Page_Down:
1632                         case GDK_KP_Page_Down:
1633                                 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1634                         case GDK_Home:
1635                         case GDK_KP_Home:
1636                                 scroll_type = GTK_SCROLL_START; break;
1637                         case GDK_End:
1638                         case GDK_KP_End:
1639                                 scroll_type = GTK_SCROLL_END; break;
1640                         default: scroll_type = GTK_SCROLL_NONE;
1641                         }
1642
1643                         /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",  */
1644                         /*                     scroll_type, FALSE, &return_value); */
1645                         return FALSE;
1646                 } else {
1647                         return FALSE;
1648                 }
1649         } else {
1650                 return FALSE;
1651         }
1652 }
1653
1654 gboolean
1655 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1656 {
1657         GtkTreePath *path;
1658         ModestMsgViewWindowPrivate *priv;
1659         GtkTreeIter tmp_iter;
1660         gboolean is_last_selected;
1661
1662         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1663         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1664
1665         /*if no model (so no rows at all), then virtually we are the last*/
1666         if (!priv->header_model || !priv->row_reference)
1667                 return TRUE;
1668
1669         if (!gtk_tree_row_reference_valid (priv->row_reference))
1670                 return TRUE;
1671
1672         path = gtk_tree_row_reference_get_path (priv->row_reference);
1673         if (path == NULL)
1674                 return TRUE;
1675
1676         is_last_selected = TRUE;
1677         while (is_last_selected) {
1678                 TnyHeader *header;
1679                 gtk_tree_path_next (path);
1680                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1681                         break;
1682                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1683                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1684                                 &header, -1);
1685                 if (header) {
1686                         if (msg_is_visible (header, priv->is_outbox))
1687                                 is_last_selected = FALSE;
1688                         g_object_unref(G_OBJECT(header));
1689                 }
1690         }
1691         gtk_tree_path_free (path);
1692         return is_last_selected;
1693 }
1694
1695 gboolean
1696 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1697 {
1698         ModestMsgViewWindowPrivate *priv;
1699
1700         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1701         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1702
1703         return priv->header_model != NULL;
1704 }
1705
1706 gboolean
1707 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1708 {
1709         ModestMsgViewWindowPrivate *priv;
1710
1711         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1712         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1713
1714         return priv->is_search_result;
1715 }
1716
1717 static gboolean
1718 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1719 {
1720         if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1721                 return FALSE;
1722         if (!check_outbox) {
1723                 return TRUE;
1724         } else {
1725                 ModestTnySendQueueStatus status;
1726                 status = modest_tny_all_send_queues_get_msg_status (header);
1727                 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1728                         (status != MODEST_TNY_SEND_QUEUE_SENDING));
1729         }
1730 }
1731
1732 gboolean
1733 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1734 {
1735         GtkTreePath *path;
1736         ModestMsgViewWindowPrivate *priv;
1737         gboolean is_first_selected;
1738         GtkTreeIter tmp_iter;
1739
1740         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1741         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1742
1743         /*if no model (so no rows at all), then virtually we are the first*/
1744         if (!priv->header_model || !priv->row_reference)
1745                 return TRUE;
1746
1747         if (!gtk_tree_row_reference_valid (priv->row_reference))
1748                 return TRUE;
1749
1750         path = gtk_tree_row_reference_get_path (priv->row_reference);
1751         if (!path)
1752                 return TRUE;
1753
1754         is_first_selected = TRUE;
1755         while (is_first_selected) {
1756                 TnyHeader *header;
1757                 if(!gtk_tree_path_prev (path))
1758                         break;
1759                 /* Here the 'if' is needless for logic, but let make sure
1760                  * iter is valid for gtk_tree_model_get. */
1761                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1762                         break;
1763                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1764                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1765                                 &header, -1);
1766                 if (header) {
1767                         if (msg_is_visible (header, priv->is_outbox))
1768                                 is_first_selected = FALSE;
1769                         g_object_unref(G_OBJECT(header));
1770                 }
1771         }
1772         gtk_tree_path_free (path);
1773         return is_first_selected;
1774 }
1775
1776 typedef struct {
1777         TnyHeader *header;
1778         GtkTreeRowReference *row_reference;
1779 } MsgReaderInfo;
1780
1781 static void
1782 message_reader_performer (gboolean canceled, 
1783                           GError *err,
1784                           GtkWindow *parent_window, 
1785                           TnyAccount *account, 
1786                           gpointer user_data)
1787 {
1788         ModestMailOperation *mail_op = NULL;
1789         MsgReaderInfo *info;
1790
1791         info = (MsgReaderInfo *) user_data;
1792         if (canceled || err) {
1793                 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1794                 goto frees;
1795         }
1796
1797         /* Register the header - it'll be unregistered in the callback */
1798         modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1799
1800         /* New mail operation */
1801         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1802                                                                  modest_ui_actions_disk_operations_error_handler, 
1803                                                                  NULL, NULL);
1804
1805         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1806         modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1807         g_object_unref (mail_op);
1808
1809         /* Update dimming rules */
1810         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1811         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1812
1813  frees:
1814         /* Frees. The row_reference will be freed by the view_msg_cb callback */
1815         g_object_unref (info->header);
1816         g_slice_free (MsgReaderInfo, info);
1817 }
1818
1819
1820 /**
1821  * Reads the message whose summary item is @header. It takes care of
1822  * several things, among others:
1823  *
1824  * If the message was not previously downloaded then ask the user
1825  * before downloading. If there is no connection launch the connection
1826  * dialog. Update toolbar dimming rules.
1827  *
1828  * Returns: TRUE if the mail operation was started, otherwise if the
1829  * user do not want to download the message, or if the user do not
1830  * want to connect, then the operation is not issued
1831  **/
1832 static gboolean
1833 message_reader (ModestMsgViewWindow *window,
1834                 ModestMsgViewWindowPrivate *priv,
1835                 TnyHeader *header,
1836                 GtkTreeRowReference *row_reference)
1837 {
1838         ModestWindowMgr *mgr;
1839         TnyAccount *account;
1840         TnyFolder *folder;
1841         MsgReaderInfo *info;
1842
1843         g_return_val_if_fail (row_reference != NULL, FALSE);
1844
1845         mgr = modest_runtime_get_window_mgr ();
1846         /* Msg download completed */
1847         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1848
1849                 /* We set the header from model while we're loading */
1850                 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1851                 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1852
1853                 /* Ask the user if he wants to download the message if
1854                    we're not online */
1855                 if (!tny_device_is_online (modest_runtime_get_device())) {
1856                         GtkResponseType response;
1857
1858                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1859                                                                             _("mcen_nc_get_msg"));
1860                         if (response == GTK_RESPONSE_CANCEL) {
1861                                 update_window_title (window);
1862                                 return FALSE;
1863                         }
1864
1865                         folder = tny_header_get_folder (header);
1866                         info = g_slice_new (MsgReaderInfo);
1867                         info->header = g_object_ref (header);
1868                         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1869
1870                         /* Offer the connection dialog if necessary */
1871                         modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
1872                                                                        TRUE,
1873                                                                        TNY_FOLDER_STORE (folder),
1874                                                                        message_reader_performer, 
1875                                                                        info);
1876                         g_object_unref (folder);
1877                         return TRUE;
1878                 }
1879         }
1880         
1881         folder = tny_header_get_folder (header);
1882         account = tny_folder_get_account (folder);
1883         info = g_slice_new (MsgReaderInfo);
1884         info->header = g_object_ref (header);
1885         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1886         
1887         message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1888         g_object_unref (account);
1889         g_object_unref (folder);
1890
1891         return TRUE;
1892 }
1893
1894 gboolean        
1895 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1896 {
1897         ModestMsgViewWindowPrivate *priv;
1898         GtkTreePath *path= NULL;
1899         GtkTreeIter tmp_iter;
1900         TnyHeader *header;
1901         gboolean retval = TRUE;
1902         GtkTreeRowReference *row_reference = NULL;
1903
1904         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1905         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1906
1907         if (!priv->row_reference)
1908                 return FALSE;
1909
1910         /* Update the next row reference if it's not valid. This could
1911            happen if for example the header which it was pointing to,
1912            was deleted. The best place to do it is in the row-deleted
1913            handler but the tinymail model do not work like the glib
1914            tree models and reports the deletion when the row is still
1915            there */
1916         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1917                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1918                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1919                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1920                 }
1921         }
1922         if (priv->next_row_reference)
1923                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1924         if (path == NULL)
1925                 return FALSE;
1926
1927         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1928
1929         gtk_tree_model_get_iter (priv->header_model,
1930                                  &tmp_iter,
1931                                  path);
1932         gtk_tree_path_free (path);
1933
1934         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1935                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1936                             &header, -1);
1937         
1938         /* Read the message & show it */
1939         if (!message_reader (window, priv, header, row_reference)) {
1940                 retval = FALSE;
1941         }
1942         gtk_tree_row_reference_free (row_reference);
1943
1944         /* Free */
1945         g_object_unref (header);
1946
1947         return retval;
1948 }
1949
1950 gboolean        
1951 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1952 {
1953         ModestMsgViewWindowPrivate *priv = NULL;
1954         GtkTreePath *path;
1955         gboolean finished = FALSE;
1956         gboolean retval = FALSE;
1957
1958         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1959         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1960
1961         /* Return inmediatly if there is no header model */
1962         if (!priv->header_model || !priv->row_reference)
1963                 return FALSE;
1964
1965         path = gtk_tree_row_reference_get_path (priv->row_reference);
1966         while (!finished && gtk_tree_path_prev (path)) {
1967                 TnyHeader *header;
1968                 GtkTreeIter iter;
1969
1970                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1971                 gtk_tree_model_get (priv->header_model, &iter, 
1972                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1973                                     &header, -1);
1974                 finished = TRUE;
1975                 if (header) {
1976                         if (msg_is_visible (header, priv->is_outbox)) {
1977                                 GtkTreeRowReference *row_reference;
1978                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1979                                 /* Read the message & show it */
1980                                 retval = message_reader (window, priv, header, row_reference);
1981                                 gtk_tree_row_reference_free (row_reference);
1982                         } else {
1983                                 finished = FALSE;
1984                         }
1985                         g_object_unref (header);
1986                 }
1987         }
1988
1989         gtk_tree_path_free (path);
1990         return retval;
1991 }
1992
1993 static void
1994 view_msg_cb (ModestMailOperation *mail_op, 
1995              TnyHeader *header, 
1996              gboolean canceled,
1997              TnyMsg *msg, 
1998              GError *error,
1999              gpointer user_data)
2000 {
2001         ModestMsgViewWindow *self = NULL;
2002         ModestMsgViewWindowPrivate *priv = NULL;
2003         GtkTreeRowReference *row_reference = NULL;
2004
2005         /* Unregister the header (it was registered before creating the mail operation) */
2006         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2007
2008         row_reference = (GtkTreeRowReference *) user_data;
2009         if (canceled) {
2010                 gtk_tree_row_reference_free (row_reference);
2011                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2012                 if (self) {
2013                         /* Restore window title */
2014                         update_window_title (self);
2015                         g_object_unref (self);
2016                 }
2017                 return;
2018         }
2019
2020         /* If there was any error */
2021         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2022                 gtk_tree_row_reference_free (row_reference);
2023                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2024                 if (self) {
2025                         /* Restore window title */
2026                         update_window_title (self);
2027                         g_object_unref (self);
2028                 }
2029                 return;
2030         }
2031
2032         /* Get the window */ 
2033         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2034         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2035         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2036
2037         /* Update the row reference */
2038         if (priv->row_reference != NULL) {
2039                 gtk_tree_row_reference_free (priv->row_reference);
2040                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2041                 if (priv->next_row_reference != NULL) {
2042                         gtk_tree_row_reference_free (priv->next_row_reference);
2043                 }
2044                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2045                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2046         }
2047
2048         /* Mark header as read */
2049         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2050                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2051
2052         /* Set new message */
2053         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2054                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2055                 modest_msg_view_window_update_priority (self);
2056                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2057                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2058         }
2059
2060         /* Set the new message uid of the window  */
2061         if (priv->msg_uid) {
2062                 g_free (priv->msg_uid);
2063                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2064         }
2065
2066         /* Notify the observers */
2067         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
2068                        0, priv->header_model, priv->row_reference);
2069
2070         /* Frees */
2071         g_object_unref (self);
2072         gtk_tree_row_reference_free (row_reference);            
2073 }
2074
2075 TnyFolderType
2076 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2077 {
2078         ModestMsgViewWindowPrivate *priv;
2079         TnyMsg *msg;
2080         TnyFolderType folder_type;
2081
2082         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2083
2084         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2085
2086         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2087         if (msg) {
2088                 TnyFolder *folder;
2089
2090                 folder = tny_msg_get_folder (msg);
2091                 if (folder) {
2092                         folder_type = modest_tny_folder_guess_folder_type (folder);
2093                         g_object_unref (folder);
2094                 }
2095                 g_object_unref (msg);
2096         }
2097
2098         return folder_type;
2099 }
2100
2101
2102 static void
2103 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2104 {
2105         ModestMsgViewWindowPrivate *priv;
2106         TnyHeader *header = NULL;
2107         TnyHeaderFlags flags = 0;
2108
2109         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2110
2111         if (priv->header_model && priv->row_reference) {
2112                 GtkTreeIter iter;
2113                 GtkTreePath *path = NULL;
2114
2115                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2116                 g_return_if_fail (path != NULL);
2117                 gtk_tree_model_get_iter (priv->header_model, 
2118                                          &iter, 
2119                                          gtk_tree_row_reference_get_path (priv->row_reference));
2120
2121                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2122                                     &header, -1);
2123                 gtk_tree_path_free (path);
2124         } else {
2125                 TnyMsg *msg;
2126                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2127                 if (msg) {
2128                         header = tny_msg_get_header (msg);
2129                         g_object_unref (msg);
2130                 }
2131         }
2132
2133         if (header) {
2134                 flags = tny_header_get_flags (header);
2135                 g_object_unref(G_OBJECT(header));
2136         }
2137
2138         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2139
2140 }
2141
2142 static void
2143 toolbar_resize (ModestMsgViewWindow *self)
2144 {
2145         ModestMsgViewWindowPrivate *priv = NULL;
2146         ModestWindowPrivate *parent_priv = NULL;
2147         GtkWidget *widget;
2148         gint static_button_size;
2149         ModestWindowMgr *mgr;
2150
2151         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2152         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2153         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2154
2155         mgr = modest_runtime_get_window_mgr ();
2156         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2157
2158         if (parent_priv->toolbar) {
2159                 /* left size buttons */
2160                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2161                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2162                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2163                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2164                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2165                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2166                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2167                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2168                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2169                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2170                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2171                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2172                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2173                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2174                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2175                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2176
2177                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2178                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2180                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2181         }
2182 }
2183
2184 static void
2185 modest_msg_view_window_show_toolbar (ModestWindow *self,
2186                                      gboolean show_toolbar)
2187 {
2188         ModestMsgViewWindowPrivate *priv = NULL;
2189         ModestWindowPrivate *parent_priv;
2190
2191         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2192         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2193
2194         /* Set optimized view status */
2195         priv->optimized_view = !show_toolbar;
2196
2197         if (!parent_priv->toolbar) {
2198                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2199                                                                   "/ToolBar");
2200                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2201                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2202
2203                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2204                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2205                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2206
2207                 /* Add to window */
2208                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2209                                            GTK_TOOLBAR (parent_priv->toolbar));
2210
2211         }
2212
2213         if (show_toolbar) {
2214                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2215                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2216                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2217
2218                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2219                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2220                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2221                 else
2222                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2223
2224         } else {
2225                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2226                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2227         }
2228 }
2229
2230 static void 
2231 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2232                                                GdkEvent *event,
2233                                                ModestMsgViewWindow *window)
2234 {
2235         if (!GTK_WIDGET_VISIBLE (window))
2236                 return;
2237
2238         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2239 }
2240
2241 gboolean 
2242 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2243 {
2244         ModestMsgViewWindowPrivate *priv;
2245         
2246         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2247         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2248
2249         return priv->progress_hint;
2250 }
2251
2252 static gboolean
2253 observers_empty (ModestMsgViewWindow *self)
2254 {
2255         GSList *tmp = NULL;
2256         ModestMsgViewWindowPrivate *priv;
2257         gboolean is_empty = TRUE;
2258         guint pending_ops = 0;
2259  
2260         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2261         tmp = priv->progress_widgets;
2262
2263         /* Check all observers */
2264         while (tmp && is_empty)  {
2265                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2266                 is_empty = pending_ops == 0;
2267                 
2268                 tmp = g_slist_next(tmp);
2269         }
2270         
2271         return is_empty;
2272 }
2273
2274 static void
2275 on_account_removed (TnyAccountStore *account_store, 
2276                     TnyAccount *account,
2277                     gpointer user_data)
2278 {
2279         /* Do nothing if it's a transport account, because we only
2280            show the messages of a store account */
2281         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2282                 const gchar *parent_acc = NULL;
2283                 const gchar *our_acc = NULL;
2284
2285                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2286                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2287
2288                 /* Close this window if I'm showing a message of the removed account */
2289                 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2290                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2291         }
2292 }
2293
2294 static void 
2295 on_mail_operation_started (ModestMailOperation *mail_op,
2296                            gpointer user_data)
2297 {
2298         ModestMsgViewWindow *self;
2299         ModestMailOperationTypeOperation op_type;
2300         GSList *tmp;
2301         ModestMsgViewWindowPrivate *priv;
2302         GObject *source = NULL;
2303
2304         self = MODEST_MSG_VIEW_WINDOW (user_data);
2305         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2306         op_type = modest_mail_operation_get_type_operation (mail_op);
2307         tmp = priv->progress_widgets;
2308         source = modest_mail_operation_get_source(mail_op);
2309         if (G_OBJECT (self) == source) {
2310                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2311                         set_toolbar_transfer_mode(self);
2312                         while (tmp) {
2313                                 modest_progress_object_add_operation (
2314                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2315                                                 mail_op);
2316                                 tmp = g_slist_next (tmp);
2317                         }
2318                 }
2319         }
2320         g_object_unref (source);
2321 }
2322
2323 static void 
2324 on_mail_operation_finished (ModestMailOperation *mail_op,
2325                             gpointer user_data)
2326 {
2327         ModestMsgViewWindow *self;
2328         ModestMailOperationTypeOperation op_type;
2329         GSList *tmp;
2330         ModestMsgViewWindowPrivate *priv;
2331         
2332         self = MODEST_MSG_VIEW_WINDOW (user_data);
2333         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2334         op_type = modest_mail_operation_get_type_operation (mail_op);
2335         tmp = priv->progress_widgets;
2336         
2337         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2338                 while (tmp) {
2339                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2340                                                                  mail_op);
2341                         tmp = g_slist_next (tmp);
2342                 }
2343
2344                 /* If no more operations are being observed, NORMAL mode is enabled again */
2345                 if (observers_empty (self)) {
2346                         set_progress_hint (self, FALSE);
2347                 }
2348         }
2349
2350         /* Update dimming rules. We have to do this right here
2351            and not in view_msg_cb because at that point the
2352            transfer mode is still enabled so the dimming rule
2353            won't let the user delete the message that has been
2354            readed for example */
2355         check_dimming_rules_after_change (self);
2356
2357 }
2358
2359 static void
2360 on_queue_changed (ModestMailOperationQueue *queue,
2361                   ModestMailOperation *mail_op,
2362                   ModestMailOperationQueueNotification type,
2363                   ModestMsgViewWindow *self)
2364 {       
2365         ModestMsgViewWindowPrivate *priv;
2366
2367         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2368
2369         /* If this operations was created by another window, do nothing */
2370         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2371             return;
2372
2373         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2374                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2375                                                                G_OBJECT (mail_op),
2376                                                                "operation-started",
2377                                                                G_CALLBACK (on_mail_operation_started),
2378                                                                self);
2379                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2380                                                                G_OBJECT (mail_op),
2381                                                                "operation-finished",
2382                                                                G_CALLBACK (on_mail_operation_finished),
2383                                                                self);
2384         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2385                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2386                                                                   G_OBJECT (mail_op),
2387                                                                   "operation-started");
2388                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2389                                                                   G_OBJECT (mail_op),
2390                                                                   "operation-finished");
2391         }
2392 }
2393
2394 TnyList *
2395 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2396 {
2397         ModestMsgViewWindowPrivate *priv;
2398         TnyList *selected_attachments = NULL;
2399         
2400         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2401         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2402
2403         /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2404         selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2405         
2406         return selected_attachments;
2407 }
2408
2409 typedef struct {
2410         ModestMsgViewWindow *self;
2411         gchar *file_path;
2412 } DecodeAsyncHelper;
2413
2414 static void
2415 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2416                                    gboolean cancelled, 
2417                                    TnyStream *stream, 
2418                                    GError *err, 
2419                                    gpointer user_data)
2420 {
2421         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2422
2423         /* It could happen that the window was closed */
2424         if (GTK_WIDGET_VISIBLE (helper->self))
2425                 set_progress_hint (helper->self, FALSE);
2426
2427         if (cancelled || err) {
2428                 if (err) {
2429                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2430                         modest_platform_information_banner (NULL, NULL, msg);
2431                         g_free (msg);
2432                 }
2433                 goto free;
2434         }
2435
2436         /* make the file read-only */
2437         g_chmod(helper->file_path, 0444);
2438
2439         /* Activate the file */
2440         modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2441
2442  free:
2443         /* Frees */
2444         g_object_unref (helper->self);
2445         g_free (helper->file_path);
2446         g_slice_free (DecodeAsyncHelper, helper);
2447 }
2448
2449 void
2450 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2451                                         TnyMimePart *mime_part)
2452 {
2453         ModestMsgViewWindowPrivate *priv;
2454         const gchar *msg_uid;
2455         gchar *attachment_uid = NULL;
2456         gint attachment_index = 0;
2457         TnyList *attachments;
2458
2459         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2460         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2461         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2462
2463         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2464         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2465         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2466         g_object_unref (attachments);
2467
2468         if (msg_uid && attachment_index >= 0) {
2469                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2470         }
2471
2472         if (mime_part == NULL) {
2473                 gboolean error = FALSE;
2474                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2475                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2476                         error = TRUE;
2477                 } else if (tny_list_get_length (selected_attachments) > 1) {
2478                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2479                         error = TRUE;
2480                 } else {
2481                         TnyIterator *iter;
2482                         iter = tny_list_create_iterator (selected_attachments);
2483                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2484                         g_object_unref (iter);
2485                 }
2486                 if (selected_attachments)
2487                         g_object_unref (selected_attachments);
2488
2489                 if (error)
2490                         goto frees;
2491         } else {
2492                 g_object_ref (mime_part);
2493         }
2494
2495         if (tny_mime_part_is_purged (mime_part))
2496                 goto frees;
2497
2498         if (!modest_tny_mime_part_is_msg (mime_part)) {
2499                 gchar *filepath = NULL;
2500                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2501                 gboolean show_error_banner = FALSE;
2502                 TnyFsStream *temp_stream = NULL;
2503                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2504                                                                &filepath);
2505
2506                 if (temp_stream != NULL) {
2507                         DecodeAsyncHelper *helper;
2508
2509                         /* Activate progress hint */
2510                         set_progress_hint (window, TRUE);
2511
2512                         helper = g_slice_new0 (DecodeAsyncHelper);
2513                         helper->self = g_object_ref (window);
2514                         helper->file_path = g_strdup (filepath);
2515
2516                         tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2517                                                               on_decode_to_stream_async_handler,
2518                                                               NULL,
2519                                                               helper);
2520                         g_object_unref (temp_stream);
2521                         /* NOTE: files in the temporary area will be automatically
2522                          * cleaned after some time if they are no longer in use */
2523                 } else {
2524                         if (filepath) {
2525                                 const gchar *content_type;
2526                                 /* the file may already exist but it isn't writable,
2527                                  * let's try to open it anyway */
2528                                 content_type = tny_mime_part_get_content_type (mime_part);
2529                                 modest_platform_activate_file (filepath, content_type);
2530                         } else {
2531                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2532                                 show_error_banner = TRUE;
2533                         }
2534                 }
2535                 if (filepath)
2536                         g_free (filepath);
2537                 if (show_error_banner)
2538                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2539         } else {
2540                 /* message attachment */
2541                 TnyHeader *header = NULL;
2542                 ModestWindowMgr *mgr;
2543                 ModestWindow *msg_win = NULL;
2544                 gboolean found;
2545
2546                 header = tny_msg_get_header (TNY_MSG (mime_part));
2547                 mgr = modest_runtime_get_window_mgr ();
2548                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2549
2550                 if (found) {
2551                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
2552                          * thus, we don't do anything */
2553                         g_warning ("window for is already being created");
2554                 } else {
2555                         /* it's not found, so create a new window for it */
2556                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2557                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2558                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2559                         if (!account)
2560                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2561                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, 
2562                                                                              mailbox, attachment_uid);
2563                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2564                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2565                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2566                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2567                         else
2568                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2569                 }
2570         }
2571
2572  frees:
2573         if (attachment_uid)
2574                 g_free (attachment_uid);
2575         if (mime_part)
2576                 g_object_unref (mime_part);
2577 }
2578
2579 typedef struct
2580 {
2581         gchar *filename;
2582         TnyMimePart *part;
2583 } SaveMimePartPair;
2584
2585 typedef struct
2586 {
2587         GList *pairs;
2588         GnomeVFSResult result;
2589 } SaveMimePartInfo;
2590
2591 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2592 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2593 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2594 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2595
2596 static void
2597 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2598 {
2599         GList *node;
2600
2601         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2602                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2603                 g_free (pair->filename);
2604                 g_object_unref (pair->part);
2605                 g_slice_free (SaveMimePartPair, pair);
2606         }
2607         g_list_free (info->pairs);
2608         info->pairs = NULL;
2609         if (with_struct) {
2610                 g_slice_free (SaveMimePartInfo, info);
2611         }
2612 }
2613
2614 static gboolean
2615 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2616 {
2617         if (info->pairs != NULL) {
2618                 save_mime_part_to_file (info);
2619         } else {
2620                 /* This is a GDK lock because we are an idle callback and
2621                  * hildon_banner_show_information is or does Gtk+ code */
2622
2623                 gdk_threads_enter (); /* CHECKED */
2624                 save_mime_part_info_free (info, TRUE);
2625                 if (info->result == GNOME_VFS_OK) {
2626                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2627                 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2628                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2629                         modest_platform_information_banner (NULL, NULL, msg);
2630                         g_free (msg);
2631                 } else {
2632                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2633                 }
2634                 gdk_threads_leave (); /* CHECKED */
2635         }
2636
2637         return FALSE;
2638 }
2639
2640 static gpointer
2641 save_mime_part_to_file (SaveMimePartInfo *info)
2642 {
2643         GnomeVFSHandle *handle;
2644         TnyStream *stream;
2645         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2646
2647         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2648         if (info->result == GNOME_VFS_OK) {
2649                 GError *error = NULL;
2650                 stream = tny_vfs_stream_new (handle);
2651                 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2652                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2653
2654                         if ((error->domain == TNY_ERROR_DOMAIN) && 
2655                             (error->code == TNY_IO_ERROR_WRITE) &&
2656                             (errno == ENOSPC)) {
2657                                 info->result = GNOME_VFS_ERROR_NO_SPACE;
2658                         } else {
2659                                 info->result = GNOME_VFS_ERROR_IO;
2660                         }
2661                 }
2662                 g_object_unref (G_OBJECT (stream));
2663                 g_object_unref (pair->part);
2664                 g_slice_free (SaveMimePartPair, pair);
2665                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2666         } else {
2667                 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2668                 save_mime_part_info_free (info, FALSE);
2669         }
2670
2671         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2672         return NULL;
2673 }
2674
2675 static void
2676 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2677 {
2678         gboolean is_ok = TRUE;
2679         gint replaced_files = 0;
2680         const GList *files = info->pairs;
2681         const GList *iter;
2682
2683         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2684                 SaveMimePartPair *pair = iter->data;
2685                 if (modest_utils_file_exists (pair->filename)) {
2686                         replaced_files++;
2687                 }
2688         }
2689         if (replaced_files) {
2690                 GtkWidget *confirm_overwrite_dialog;
2691
2692                 if (replaced_files == 1) {
2693                         SaveMimePartPair *pair = files->data;
2694                         const gchar *filename = tny_mime_part_get_filename (pair->part);
2695                         gchar *message = g_strdup_printf ("%s\n%s",
2696                                                           _FM("docm_nc_replace_file"),
2697                                                           (filename) ? filename : "");
2698                         confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2699                         g_free (message);
2700                 } else {
2701                         confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2702                                                                                  _FM("docm_nc_replace_multiple"));
2703                 }
2704                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2705                         is_ok = FALSE;
2706
2707                 gtk_widget_destroy (confirm_overwrite_dialog);
2708         }
2709
2710         if (!is_ok) {
2711                 save_mime_part_info_free (info, TRUE);
2712         } else {
2713                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2714         }
2715
2716 }
2717
2718 static void
2719 save_attachments_response (GtkDialog *dialog,
2720                            gint       arg1,
2721                            gpointer   user_data)  
2722 {
2723         TnyList *mime_parts;
2724         gchar *chooser_uri;
2725         GList *files_to_save = NULL;
2726         gchar *current_folder;
2727
2728         mime_parts = TNY_LIST (user_data);
2729
2730         if (arg1 != GTK_RESPONSE_OK)
2731                 goto end;
2732
2733         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2734         current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2735         if (current_folder && current_folder != '\0') {
2736                 GError *err = NULL;
2737                 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, 
2738                                         current_folder,&err);
2739                 if (err != NULL) {
2740                         g_debug ("Error storing latest used folder: %s", err->message);
2741                         g_error_free (err);
2742                 }
2743         }
2744         g_free (current_folder);
2745
2746         if (!modest_utils_folder_writable (chooser_uri)) {
2747                 hildon_banner_show_information 
2748                         (NULL, NULL, _FM("sfil_ib_readonly_location"));
2749         } else {
2750                 TnyIterator *iter;
2751
2752                 iter = tny_list_create_iterator (mime_parts);
2753                 while (!tny_iterator_is_done (iter)) {
2754                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2755
2756                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2757                             !tny_mime_part_is_purged (mime_part) &&
2758                             (tny_mime_part_get_filename (mime_part) != NULL)) {
2759                                 SaveMimePartPair *pair;
2760
2761                                 pair = g_slice_new0 (SaveMimePartPair);
2762
2763                                 if (tny_list_get_length (mime_parts) > 1) {
2764                                         gchar *escaped = 
2765                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2766                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2767                                         g_free (escaped);
2768                                 } else {
2769                                         pair->filename = g_strdup (chooser_uri);
2770                                 }
2771                                 pair->part = mime_part;
2772                                 files_to_save = g_list_prepend (files_to_save, pair);
2773                         }
2774                         tny_iterator_next (iter);
2775                 }
2776                 g_object_unref (iter);
2777         }
2778         g_free (chooser_uri);
2779
2780         if (files_to_save != NULL) {
2781                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2782                 info->pairs = files_to_save;
2783                 info->result = TRUE;
2784                 save_mime_parts_to_file_with_checks (info);
2785         }
2786
2787  end:
2788         /* Free and close the dialog */
2789         g_object_unref (mime_parts);
2790         gtk_widget_destroy (GTK_WIDGET (dialog));
2791 }
2792
2793 void
2794 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, 
2795                                          TnyList *mime_parts)
2796 {
2797         ModestMsgViewWindowPrivate *priv;
2798         GtkWidget *save_dialog = NULL;
2799         gchar *conf_folder = NULL;
2800         gchar *filename = NULL;
2801         gchar *save_multiple_str = NULL;
2802
2803         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2804         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2805
2806         if (mime_parts == NULL) {
2807                 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2808                  * selection available */
2809                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2810                 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2811                         g_object_unref (mime_parts);
2812                         return;
2813                 }
2814                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2815                         if (mime_parts) {
2816                                 g_object_unref (mime_parts);
2817                                 mime_parts = NULL;
2818                         }
2819                         return;
2820                 }
2821         } else {
2822                 g_object_ref (mime_parts);
2823         }
2824
2825         /* prepare dialog */
2826         if (tny_list_get_length (mime_parts) == 1) {
2827                 TnyIterator *iter;
2828                 /* only one attachment selected */
2829                 iter = tny_list_create_iterator (mime_parts);
2830                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2831                 g_object_unref (iter);
2832                 if (!modest_tny_mime_part_is_msg (mime_part) && 
2833                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2834                     !tny_mime_part_is_purged (mime_part)) {
2835                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
2836                 } else {
2837                         /* TODO: show any error? */
2838                         g_warning ("Tried to save a non-file attachment");
2839                         g_object_unref (mime_parts);
2840                         return;
2841                 }
2842                 g_object_unref (mime_part);
2843         } else {
2844                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2845                                                      tny_list_get_length (mime_parts));
2846         }
2847
2848         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2849                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2850
2851         /* set folder */
2852         conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2853         if (conf_folder && conf_folder[0] != '\0') {
2854                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2855         } else {
2856                 gchar *docs_folder;
2857                 /* Set the default folder to images folder */
2858                 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2859                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2860                 g_free (docs_folder);
2861         }
2862         g_free (conf_folder);
2863
2864         /* set filename */
2865         if (filename) {
2866                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2867                                                    filename);
2868                 g_free (filename);
2869         }
2870
2871         /* if multiple, set multiple string */
2872         if (save_multiple_str) {
2873                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2874                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2875         }
2876
2877         /* We must run this asynchronously, because the hildon dialog
2878            performs a gtk_dialog_run by itself which leads to gdk
2879            deadlocks */
2880         g_signal_connect (save_dialog, "response", 
2881                           G_CALLBACK (save_attachments_response), mime_parts);
2882
2883         gtk_widget_show_all (save_dialog);
2884 }
2885
2886 static gboolean
2887 show_remove_attachment_information (gpointer userdata)
2888 {
2889         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2890         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2891
2892         /* We're outside the main lock */
2893         gdk_threads_enter ();
2894
2895         if (priv->remove_attachment_banner != NULL) {
2896                 gtk_widget_destroy (priv->remove_attachment_banner);
2897                 g_object_unref (priv->remove_attachment_banner);
2898         }
2899
2900         priv->remove_attachment_banner = g_object_ref (
2901                 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2902
2903         gdk_threads_leave ();
2904
2905         return FALSE;
2906 }
2907
2908 void
2909 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2910 {
2911         ModestMsgViewWindowPrivate *priv;
2912         TnyList *mime_parts = NULL, *tmp;
2913         gchar *confirmation_message;
2914         gint response;
2915         gint n_attachments;
2916         TnyMsg *msg;
2917         TnyIterator *iter;
2918
2919         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2920         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2921
2922         /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2923          * because we don't have selection
2924          */
2925         mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2926
2927         /* Remove already purged messages from mime parts list. We use
2928            a copy of the list to remove items in the original one */
2929         tmp = tny_list_copy (mime_parts);
2930         iter = tny_list_create_iterator (tmp);
2931         while (!tny_iterator_is_done (iter)) {
2932                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2933                 if (tny_mime_part_is_purged (part))
2934                         tny_list_remove (mime_parts, (GObject *) part);
2935
2936                 g_object_unref (part);
2937                 tny_iterator_next (iter);
2938         }
2939         g_object_unref (tmp);
2940         g_object_unref (iter);
2941
2942         if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2943             tny_list_get_length (mime_parts) == 0) {
2944                 g_object_unref (mime_parts);
2945                 return;
2946         }
2947
2948         n_attachments = tny_list_get_length (mime_parts);
2949         if (n_attachments == 1) {
2950                 gchar *filename;
2951                 TnyMimePart *part;
2952
2953                 iter = tny_list_create_iterator (mime_parts);
2954                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2955                 g_object_unref (iter);
2956                 if (modest_tny_mime_part_is_msg (part)) {
2957                         TnyHeader *header;
2958                         header = tny_msg_get_header (TNY_MSG (part));
2959                         filename = tny_header_dup_subject (header);
2960                         g_object_unref (header);
2961                         if (filename == NULL)
2962                                 filename = g_strdup (_("mail_va_no_subject"));
2963                 } else {
2964                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2965                 }
2966                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2967                 g_free (filename);
2968                 g_object_unref (part);
2969         } else {
2970                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2971                                                                  "mcen_nc_purge_files_text", 
2972                                                                  n_attachments), n_attachments);
2973         }
2974         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2975                                                             confirmation_message);
2976         g_free (confirmation_message);
2977
2978         if (response != GTK_RESPONSE_OK) {
2979                 g_object_unref (mime_parts);
2980                 return;
2981         }
2982
2983         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2984
2985         iter = tny_list_create_iterator (mime_parts);
2986         while (!tny_iterator_is_done (iter)) {
2987                 TnyMimePart *part;
2988
2989                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2990                 tny_mime_part_set_purged (TNY_MIME_PART (part));
2991                 g_object_unref (part);
2992                 tny_iterator_next (iter);
2993         }
2994         g_object_unref (iter);
2995
2996         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2997         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2998         tny_msg_rewrite_cache (msg);
2999         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3000         g_object_unref (msg);
3001
3002         g_object_unref (mime_parts);
3003
3004         if (priv->purge_timeout > 0) {
3005                 g_source_remove (priv->purge_timeout);
3006                 priv->purge_timeout = 0;
3007         }
3008
3009         if (priv->remove_attachment_banner) {
3010                 gtk_widget_destroy (priv->remove_attachment_banner);
3011                 g_object_unref (priv->remove_attachment_banner);
3012                 priv->remove_attachment_banner = NULL;
3013         }
3014 }
3015
3016
3017 static void
3018 update_window_title (ModestMsgViewWindow *window)
3019 {
3020         ModestMsgViewWindowPrivate *priv;
3021         TnyMsg *msg = NULL;
3022         TnyHeader *header = NULL;
3023         gchar *subject = NULL;
3024
3025         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3026
3027         /* Note that if the window is closed while we're retrieving
3028            the message, this widget could de deleted */
3029         if (!priv->msg_view)
3030                 return;
3031
3032         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3033
3034         if (msg != NULL) {
3035                 header = tny_msg_get_header (msg);
3036                 subject = tny_header_dup_subject (header);
3037                 g_object_unref (header);
3038                 g_object_unref (msg);
3039         }
3040
3041         if ((subject == NULL)||(subject[0] == '\0')) {
3042                 g_free (subject);
3043                 subject = g_strdup (_("mail_va_no_subject"));
3044         }
3045
3046         gtk_window_set_title (GTK_WINDOW (window), subject);
3047 }
3048
3049
3050 static void
3051 on_move_focus (GtkWidget *widget,
3052                GtkDirectionType direction,
3053                gpointer userdata)
3054 {
3055         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3056 }
3057
3058 static TnyStream *
3059 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3060 {
3061         GnomeVFSResult result;
3062         GnomeVFSHandle *handle = NULL;
3063         GnomeVFSFileInfo *info = NULL;
3064         TnyStream *stream;
3065
3066         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3067         if (result != GNOME_VFS_OK) {
3068                 *expected_size = 0;
3069                 return NULL;
3070         }
3071         
3072         info = gnome_vfs_file_info_new ();
3073         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3074         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3075                 /* We put a "safe" default size for going to cache */
3076                 *expected_size = (300*1024);
3077         } else {
3078                 *expected_size = info->size;
3079         }
3080         gnome_vfs_file_info_unref (info);
3081
3082         stream = tny_vfs_stream_new (handle);
3083
3084         return stream;
3085
3086 }
3087
3088 typedef struct {
3089         gchar *uri;
3090         gchar *cache_id;
3091         TnyStream *output_stream;
3092         GtkWidget *msg_view;
3093         GtkWidget *window;
3094 } FetchImageData;
3095
3096 gboolean
3097 on_fetch_image_idle_refresh_view (gpointer userdata)
3098 {
3099
3100         FetchImageData *fidata = (FetchImageData *) userdata;
3101
3102         gdk_threads_enter ();
3103         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3104                 ModestMsgViewWindowPrivate *priv;
3105
3106                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3107                 priv->fetching_images--;
3108                 gtk_widget_queue_draw (fidata->msg_view);
3109                 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3110         }
3111         gdk_threads_leave ();
3112
3113         g_object_unref (fidata->msg_view);
3114         g_object_unref (fidata->window);
3115         g_slice_free (FetchImageData, fidata);
3116         return FALSE;
3117 }
3118
3119 static gpointer
3120 on_fetch_image_thread (gpointer userdata)
3121 {
3122         FetchImageData *fidata = (FetchImageData *) userdata;
3123         TnyStreamCache *cache;
3124         TnyStream *cache_stream;
3125
3126         cache = modest_runtime_get_images_cache ();
3127         cache_stream = 
3128                 tny_stream_cache_get_stream (cache, 
3129                                              fidata->cache_id, 
3130                                              (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, 
3131                                              (gpointer) fidata->uri);
3132         g_free (fidata->cache_id);
3133         g_free (fidata->uri);
3134
3135         if (cache_stream != NULL) {
3136                 char buffer[4096];
3137
3138                 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3139                         gssize nb_read;
3140
3141                         nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3142                         if (G_UNLIKELY (nb_read < 0)) {
3143                                 break;
3144                         } else if (G_LIKELY (nb_read > 0)) {
3145                                 gssize nb_written = 0;
3146
3147                                 while (G_UNLIKELY (nb_written < nb_read)) {
3148                                         gssize len;
3149
3150                                         len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3151                                                                 nb_read - nb_written);
3152                                         if (G_UNLIKELY (len < 0))
3153                                                 break;
3154                                         nb_written += len;
3155                                 }
3156                         }
3157                 }
3158                 tny_stream_close (cache_stream);
3159                 g_object_unref (cache_stream);
3160         }
3161
3162         tny_stream_close (fidata->output_stream);
3163         g_object_unref (fidata->output_stream);
3164
3165         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3166
3167         return NULL;
3168 }
3169
3170 static gboolean
3171 on_fetch_image (ModestMsgView *msgview,
3172                 const gchar *uri,
3173                 TnyStream *stream,
3174                 ModestMsgViewWindow *window)
3175 {
3176         const gchar *current_account;
3177         ModestMsgViewWindowPrivate *priv;
3178         FetchImageData *fidata;
3179
3180         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3181
3182         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3183
3184         fidata = g_slice_new0 (FetchImageData);
3185         fidata->msg_view = g_object_ref (msgview);
3186         fidata->window = g_object_ref (window);
3187         fidata->uri = g_strdup (uri);
3188         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3189         fidata->output_stream = g_object_ref (stream);
3190
3191         priv->fetching_images++;
3192         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3193                 g_object_unref (fidata->output_stream);
3194                 g_free (fidata->cache_id);
3195                 g_free (fidata->uri);
3196                 g_object_unref (fidata->msg_view);
3197                 g_slice_free (FetchImageData, fidata);
3198                 tny_stream_close (stream);
3199                 priv->fetching_images--;
3200                 update_progress_hint (window);
3201                 return FALSE;
3202         }
3203         update_progress_hint (window);
3204
3205         return TRUE;
3206 }
3207
3208 static void 
3209 setup_menu (ModestMsgViewWindow *self)
3210 {
3211         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3212
3213         /* Settings menu buttons */
3214         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3215                                            APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3216                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3217         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3218                                            APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3219                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3220
3221         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3222                                            APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3223                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3224         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3225                                            APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3226                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3227
3228         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3229                                            APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3230                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3231         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3232                                            APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3233                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3234
3235         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3236                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3237                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3238         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3239                                            APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3240                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3241
3242         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3243                                            APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3244                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3245         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3246                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
3247                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3248 }
3249
3250 void
3251 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3252 {
3253         ModestMsgViewWindowPrivate *priv;
3254         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3255         GSList *recipients = NULL;
3256         TnyMsg *msg = NULL;
3257         gboolean contacts_to_add = FALSE;
3258
3259         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3260         if (msg == NULL) {
3261                 TnyHeader *header;
3262
3263                 header = modest_msg_view_window_get_header (self);
3264                 if (header == NULL)
3265                         return;
3266                 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3267                 g_object_unref (header);
3268         } else {
3269                 recipients = modest_tny_msg_get_all_recipients_list (msg);
3270                 g_object_unref (msg);
3271         }
3272
3273         if (recipients != NULL) {
3274                 GtkWidget *picker_dialog;
3275                 GtkWidget *selector;
3276                 GSList *node;
3277                 gchar *selected = NULL;
3278
3279                 selector = hildon_touch_selector_new_text ();
3280                 g_object_ref (selector);
3281
3282                 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3283                         if (!modest_address_book_has_address ((const gchar *) node->data)) {
3284                                 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), 
3285                                                                    (const gchar *) node->data);
3286                                 contacts_to_add = TRUE;
3287                         }
3288                 }
3289
3290                 if (contacts_to_add) {
3291                         gint picker_result;
3292
3293                         picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3294                         gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3295
3296                         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog), 
3297                                                            HILDON_TOUCH_SELECTOR (selector));
3298                         
3299                         picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3300
3301                         if (picker_result == GTK_RESPONSE_OK) {
3302                                 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3303                         }
3304                         gtk_widget_destroy (picker_dialog);
3305
3306                         if (selected)
3307                                 modest_address_book_add_address (selected);
3308                         g_free (selected);
3309
3310                 } else {
3311
3312                         g_object_unref (selector);
3313
3314                 }
3315         }
3316         
3317         if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3318 }
3319
3320 static gboolean 
3321 _modest_msg_view_window_map_event (GtkWidget *widget,
3322                                    GdkEvent *event,
3323                                    gpointer userdata)
3324 {
3325         ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3326
3327         update_progress_hint (self);
3328
3329         return FALSE;
3330 }
3331
3332 void
3333 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3334 {
3335         ModestMsgViewWindowPrivate *priv;
3336         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3337
3338         modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3339 }
3340
3341 gboolean 
3342 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3343 {
3344         ModestMsgViewWindowPrivate *priv;
3345         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3346
3347         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3348
3349         return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3350 }
3351
3352 void 
3353 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3354 {
3355         ModestMsgViewWindowPrivate *priv;
3356         TnyHeader *header;
3357
3358         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3359
3360         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3361         header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));     
3362
3363         if (!message_reader (self, priv, header, priv->row_reference)) {
3364                 g_warning ("Shouldn't happen, trying to reload a message failed");
3365         }
3366
3367         g_object_unref (header);
3368 }