eff182eb6926b372221d417df6e8a7c06baf4ab8
[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                 g_object_unref (header);
997         }
998         gtk_tree_path_free (path);
999
1000         /* Check dimming rules */
1001         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1002         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1003         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1004
1005         return MODEST_WINDOW(window);
1006 }
1007
1008 ModestWindow *
1009 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
1010                                               const gchar *modest_account_name,
1011                                               const gchar *mailbox,
1012                                               const gchar *msg_uid)
1013 {
1014         ModestMsgViewWindow *window = NULL;
1015         ModestMsgViewWindowPrivate *priv = NULL;
1016         ModestWindowMgr *mgr = NULL;
1017
1018         mgr = modest_runtime_get_window_mgr ();
1019         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1020         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1021         modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1022
1023         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1024
1025         /* Remember that this is a search result, 
1026          * so we can disable some UI appropriately: */
1027         priv->is_search_result = TRUE;
1028
1029         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1030         
1031         update_window_title (window);
1032         /* gtk_widget_show_all (GTK_WIDGET (window));*/
1033         modest_msg_view_window_update_priority (window);
1034
1035         /* Check dimming rules */
1036         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1037         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1038         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1039
1040         return MODEST_WINDOW(window);
1041 }
1042
1043 ModestWindow *
1044 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
1045                                            const gchar *modest_account_name,
1046                                            const gchar *mailbox,
1047                                            const gchar *msg_uid)
1048 {
1049         GObject *obj = NULL;
1050         ModestMsgViewWindowPrivate *priv;       
1051         ModestWindowMgr *mgr = NULL;
1052
1053         g_return_val_if_fail (msg, NULL);
1054         mgr = modest_runtime_get_window_mgr ();
1055         obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1056         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1057         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
1058                                           modest_account_name, mailbox, msg_uid);
1059
1060         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1061         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1062
1063         /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1064
1065         /* Check dimming rules */
1066         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1067         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1068         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1069
1070         return MODEST_WINDOW(obj);
1071 }
1072
1073 static void
1074 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1075                                        GtkTreePath *arg1,
1076                                        GtkTreeIter *arg2,
1077                                        ModestMsgViewWindow *window)
1078 {
1079         check_dimming_rules_after_change (window);
1080 }
1081
1082 static void 
1083 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1084                                       GtkTreePath *arg1,
1085                                       ModestMsgViewWindow *window)
1086 {
1087         check_dimming_rules_after_change (window);
1088 }
1089         /* The window could have dissapeared */
1090
1091 static void
1092 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1093 {
1094         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1095         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1096 }
1097
1098
1099 /* On insertions we check if the folder still has the message we are
1100  * showing or do not. If do not, we do nothing. Which means we are still
1101  * not attached to any header folder and thus next/prev buttons are
1102  * still dimmed. Once the message that is shown by msg-view is found, the
1103  * new model of header-view will be attached and the references will be set.
1104  * On each further insertions dimming rules will be checked. However
1105  * this requires extra CPU time at least works.
1106  * (An message might be deleted from TnyFolder and thus will not be
1107  * inserted into the model again for example if it is removed by the
1108  * imap server and the header view is refreshed.)
1109  */
1110 static void 
1111 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1112                                         GtkTreePath *tree_path,
1113                                         GtkTreeIter *tree_iter,
1114                                         ModestMsgViewWindow *window)
1115 {
1116         ModestMsgViewWindowPrivate *priv = NULL; 
1117         TnyHeader *header = NULL;
1118
1119         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1120         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1121
1122         g_assert (model == priv->header_model);
1123         
1124         /* Check if the newly inserted message is the same we are actually
1125          * showing. IF not, we should remain detached from the header model
1126          * and thus prev and next toolbar buttons should remain dimmed. */
1127         gtk_tree_model_get (model, tree_iter, 
1128                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1129                             &header, -1);
1130
1131         if (TNY_IS_HEADER (header)) {
1132                 gchar *uid = NULL;
1133
1134                 uid = modest_tny_folder_get_header_unique_id (header);
1135                 if (!g_str_equal(priv->msg_uid, uid)) {
1136                         check_dimming_rules_after_change (window);
1137                         g_free(uid);
1138                         g_object_unref (G_OBJECT(header));
1139                         return;
1140                 }
1141                 g_free(uid);
1142                 g_object_unref(G_OBJECT(header));
1143         }
1144
1145         if (priv->row_reference) {
1146                 gtk_tree_row_reference_free (priv->row_reference); 
1147         }
1148
1149         /* Setup row_reference for the actual msg. */
1150         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1151         if (priv->row_reference == NULL) {
1152                 g_warning("No reference for msg header item.");
1153                 return;
1154         }
1155
1156         /* Now set up next_row_reference. */
1157         if (priv->next_row_reference) {
1158                 gtk_tree_row_reference_free (priv->next_row_reference); 
1159         }
1160
1161         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1162         select_next_valid_row (priv->header_model,
1163                                &(priv->next_row_reference), FALSE, priv->is_outbox);
1164
1165         /* Connect the remaining callbacks to become able to detect
1166          * changes in header-view. */
1167         priv->row_changed_handler = 
1168                 g_signal_connect (priv->header_model, "row-changed",
1169                                   G_CALLBACK (modest_msg_view_window_on_row_changed),
1170                                   window);
1171         priv->row_deleted_handler = 
1172                 g_signal_connect (priv->header_model, "row-deleted",
1173                                   G_CALLBACK (modest_msg_view_window_on_row_deleted),
1174                                   window);
1175         priv->rows_reordered_handler = 
1176                 g_signal_connect (priv->header_model, "rows-reordered",
1177                                   G_CALLBACK (modest_msg_view_window_on_row_reordered),
1178                                   window);
1179
1180         check_dimming_rules_after_change (window);      
1181 }
1182
1183 static void 
1184 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1185                                          GtkTreePath *arg1,
1186                                          GtkTreeIter *arg2,
1187                                          gpointer arg3,
1188                                          ModestMsgViewWindow *window)
1189 {
1190         ModestMsgViewWindowPrivate *priv = NULL; 
1191         gboolean already_changed = FALSE;
1192
1193         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1194
1195         /* If the current row was reordered select the proper next
1196            valid row. The same if the next row reference changes */
1197         if (priv->row_reference && 
1198             gtk_tree_row_reference_valid (priv->row_reference)) {
1199                 GtkTreePath *path;
1200                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1201                 if (gtk_tree_path_compare (path, arg1) == 0) {
1202                         if (priv->next_row_reference) {
1203                                 gtk_tree_row_reference_free (priv->next_row_reference);
1204                         }
1205                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1206                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1207                         already_changed = TRUE;
1208                 }
1209                 gtk_tree_path_free (path);
1210         }
1211         if (!already_changed &&
1212             priv->next_row_reference &&
1213             gtk_tree_row_reference_valid (priv->next_row_reference)) {
1214                 GtkTreePath *path;
1215                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1216                 if (gtk_tree_path_compare (path, arg1) == 0) {
1217                         if (priv->next_row_reference) {
1218                                 gtk_tree_row_reference_free (priv->next_row_reference);
1219                         }
1220                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1221                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1222                 }
1223                 gtk_tree_path_free (path);
1224         }
1225         check_dimming_rules_after_change (window);
1226 }
1227
1228 /* The modest_msg_view_window_update_model_replaced implements update
1229  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1230  * actually belongs to the header-view is the same as the TnyFolder of
1231  * the message of msg-view or not. If they are different, there is
1232  * nothing to do. If they are the same, then the model has replaced and
1233  * the reference in msg-view shall be replaced from the old model to
1234  * the new model. In this case the view will be detached from it's
1235  * header folder. From this point the next/prev buttons are dimmed.
1236  */
1237 static void 
1238 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1239                                               GtkTreeModel *model,
1240                                               const gchar *tny_folder_id)
1241 {
1242         ModestMsgViewWindowPrivate *priv = NULL; 
1243         ModestMsgViewWindow *window = NULL;
1244
1245         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1246         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1247
1248         window = MODEST_MSG_VIEW_WINDOW(observer);
1249         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1250
1251         /* If there is an other folder in the header-view then we do
1252          * not care about it's model (msg list). Else if the
1253          * header-view shows the folder the msg shown by us is in, we
1254          * shall replace our model reference and make some check. */
1255         if(model == NULL || tny_folder_id == NULL || 
1256            (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1257                 return;
1258
1259         /* Model is changed(replaced), so we should forget the old
1260          * one. Because there might be other references and there
1261          * might be some change on the model even if we unreferenced
1262          * it, we need to disconnect our signals here. */
1263         if (priv->header_model) {
1264                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1265                                                   priv->row_changed_handler))
1266                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1267                                                     priv->row_changed_handler);
1268                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1269                                                   priv->row_deleted_handler))
1270                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1271                                                     priv->row_deleted_handler);
1272                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1273                                                   priv->row_inserted_handler))
1274                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1275                                                     priv->row_inserted_handler);
1276                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1277                                                   priv->rows_reordered_handler))
1278                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1279                                                     priv->rows_reordered_handler);
1280
1281                 /* Frees */
1282                 if (priv->row_reference)
1283                         gtk_tree_row_reference_free (priv->row_reference);
1284                 if (priv->next_row_reference)
1285                         gtk_tree_row_reference_free (priv->next_row_reference);
1286                 g_object_unref(priv->header_model);
1287
1288                 /* Initialize */
1289                 priv->row_changed_handler = 0;
1290                 priv->row_deleted_handler = 0;
1291                 priv->row_inserted_handler = 0;
1292                 priv->rows_reordered_handler = 0;
1293                 priv->next_row_reference = NULL;
1294                 priv->row_reference = NULL;
1295                 priv->header_model = NULL;
1296         }
1297
1298         priv->header_model = g_object_ref (model);
1299
1300         /* Also we must connect to the new model for row insertions.
1301          * Only for insertions now. We will need other ones only after
1302          * the msg is show by msg-view is added to the new model. */
1303         priv->row_inserted_handler =
1304                 g_signal_connect (priv->header_model, "row-inserted",
1305                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1306                                   window);
1307
1308         modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1309         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1310 }
1311
1312 gboolean 
1313 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1314 {
1315         ModestMsgViewWindowPrivate *priv= NULL; 
1316
1317         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1318         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1319
1320         return priv->progress_hint;
1321 }
1322
1323 TnyHeader*
1324 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1325 {
1326         ModestMsgViewWindowPrivate *priv= NULL; 
1327         TnyMsg *msg = NULL;
1328         TnyHeader *header = NULL;
1329         GtkTreePath *path = NULL;
1330         GtkTreeIter iter;
1331
1332         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1333         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1334
1335         /* If the message was not obtained from a treemodel,
1336          * for instance if it was opened directly by the search UI:
1337          */
1338         if (priv->header_model == NULL || 
1339             priv->row_reference == NULL ||
1340             !gtk_tree_row_reference_valid (priv->row_reference)) {
1341                 msg = modest_msg_view_window_get_message (self);
1342                 if (msg) {
1343                         header = tny_msg_get_header (msg);
1344                         g_object_unref (msg);
1345                 }
1346                 return header;
1347         }
1348
1349         /* Get iter of the currently selected message in the header view: */
1350         path = gtk_tree_row_reference_get_path (priv->row_reference);
1351         g_return_val_if_fail (path != NULL, NULL);
1352         gtk_tree_model_get_iter (priv->header_model, 
1353                                  &iter, 
1354                                  path);
1355
1356         /* Get current message header */
1357         gtk_tree_model_get (priv->header_model, &iter, 
1358                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1359                             &header, -1);
1360
1361         gtk_tree_path_free (path);
1362         return header;
1363 }
1364
1365 TnyMsg*
1366 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1367 {
1368         ModestMsgViewWindowPrivate *priv;
1369         
1370         g_return_val_if_fail (self, NULL);
1371         
1372         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1373         
1374         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1375 }
1376
1377 const gchar*
1378 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1379 {
1380         ModestMsgViewWindowPrivate *priv;
1381
1382         g_return_val_if_fail (self, NULL);
1383         
1384         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1385
1386         return (const gchar*) priv->msg_uid;
1387 }
1388
1389 static void 
1390 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1391                                             gpointer data)
1392 {
1393         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1394         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1395         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1396         gboolean is_active;
1397         GtkAction *action;
1398
1399         is_active = gtk_toggle_action_get_active (toggle);
1400
1401         if (is_active) {
1402                 gtk_widget_show (priv->find_toolbar);
1403                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1404         } else {
1405                 gtk_widget_hide (priv->find_toolbar);
1406                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1407         }
1408
1409         /* update the toggle buttons status */
1410         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1411         if (action)
1412                 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1413
1414 }
1415
1416 static void
1417 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1418                                            ModestMsgViewWindow *obj)
1419 {
1420         GtkToggleAction *toggle;
1421         ModestWindowPrivate *parent_priv;
1422         ModestMsgViewWindowPrivate *priv;
1423
1424         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1425         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1426
1427         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1428         gtk_toggle_action_set_active (toggle, FALSE);
1429         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1430 }
1431
1432 static void
1433 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1434                                            ModestMsgViewWindow *obj)
1435 {
1436         gchar *current_search;
1437         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1438
1439         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1440                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1441                 return;
1442         }
1443
1444         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1445
1446         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1447                 g_free (current_search);
1448                 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1449                 return;
1450         }
1451
1452         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1453                 gboolean result;
1454                 g_free (priv->last_search);
1455                 priv->last_search = g_strdup (current_search);
1456                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1457                                                      priv->last_search);
1458                 if (!result) {
1459                         hildon_banner_show_information (NULL, NULL, 
1460                                                         _HL("ckct_ib_find_no_matches"));
1461                         g_free (priv->last_search);
1462                         priv->last_search = NULL;
1463                 } else {
1464                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1465                 }
1466         } else {
1467                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1468                         hildon_banner_show_information (NULL, NULL, 
1469                                                         _HL("ckct_ib_find_search_complete"));
1470                         g_free (priv->last_search);
1471                         priv->last_search = NULL;
1472                 } else {
1473                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1474                 }
1475         }
1476         
1477         g_free (current_search);
1478                 
1479 }
1480
1481 static void
1482 modest_msg_view_window_set_zoom (ModestWindow *window,
1483                                  gdouble zoom)
1484 {
1485         ModestMsgViewWindowPrivate *priv;
1486         ModestWindowPrivate *parent_priv;
1487      
1488         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1489
1490         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1491         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1492         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1493
1494 }
1495
1496 static gdouble
1497 modest_msg_view_window_get_zoom (ModestWindow *window)
1498 {
1499         ModestMsgViewWindowPrivate *priv;
1500      
1501         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1502
1503         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1504         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1505 }
1506
1507 static gboolean
1508 modest_msg_view_window_zoom_plus (ModestWindow *window)
1509 {
1510         gdouble zoom_level;
1511         ModestMsgViewWindowPrivate *priv;
1512         gint int_zoom;
1513         gchar *banner_text;
1514      
1515         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1516         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1517   
1518         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1519
1520         if (zoom_level >= 2.0) {
1521                 hildon_banner_show_information (NULL, NULL, 
1522                                                 _CS("ckct_ib_max_zoom_level_reached"));
1523                 return FALSE;
1524         } else if (zoom_level >= 1.5) {
1525                 zoom_level = 2.0;
1526         } else if (zoom_level >= 1.2) {
1527                 zoom_level = 1.5;
1528         } else if (zoom_level >= 1.0) {
1529                 zoom_level = 1.2;
1530         } else if (zoom_level >= 0.8) {
1531                 zoom_level = 1.0;
1532         } else if (zoom_level >= 0.5) {
1533                 zoom_level = 0.8;
1534         } else {
1535                 zoom_level = 0.5;
1536         }
1537
1538         /* set zoom level */
1539         int_zoom = (gint) rint (zoom_level*100.0+0.1);
1540         banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1541         modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1542         g_free (banner_text);
1543         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1544
1545         return TRUE;
1546 }
1547
1548 static gboolean
1549 modest_msg_view_window_zoom_minus (ModestWindow *window)
1550 {
1551         gdouble zoom_level;
1552         ModestMsgViewWindowPrivate *priv;
1553         gint int_zoom;
1554         gchar *banner_text;
1555      
1556         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1557         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1558   
1559         zoom_level =  modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1560
1561         if (zoom_level <= 0.5) {
1562                 hildon_banner_show_information (NULL, NULL, 
1563                                                 _CS("ckct_ib_min_zoom_level_reached"));
1564                 return FALSE;
1565         } else if (zoom_level <= 0.8) {
1566                 zoom_level = 0.5;
1567         } else if (zoom_level <= 1.0) {
1568                 zoom_level = 0.8;
1569         } else if (zoom_level <= 1.2) {
1570                 zoom_level = 1.0;
1571         } else if (zoom_level <= 1.5) {
1572                 zoom_level = 1.2;
1573         } else if (zoom_level <= 2.0) {
1574                 zoom_level = 1.5;
1575         } else {
1576                 zoom_level = 2.0;
1577         }
1578
1579         /* set zoom level */
1580         int_zoom = (gint) rint (zoom_level*100.0+0.1);
1581         banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1582         modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1583         g_free (banner_text);
1584         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1585
1586         return TRUE;
1587         
1588 }
1589
1590 static gboolean
1591 modest_msg_view_window_key_event (GtkWidget *window,
1592                                   GdkEventKey *event,
1593                                   gpointer userdata)
1594 {
1595         GtkWidget *focus;
1596
1597         focus = gtk_window_get_focus (GTK_WINDOW (window));
1598
1599         /* for the find toolbar case */
1600         if (focus && GTK_IS_ENTRY (focus)) {
1601                 if (event->keyval == GDK_BackSpace) {
1602                         GdkEvent *copy;
1603                         copy = gdk_event_copy ((GdkEvent *) event);
1604                         gtk_widget_event (focus, copy);
1605                         gdk_event_free (copy);
1606                         return TRUE;
1607                 } else 
1608                         return FALSE;
1609         }
1610         if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1611             event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1612             event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1613             event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1614             event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1615             event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1616                 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1617                 /* gboolean return_value; */
1618
1619                 if (event->type == GDK_KEY_PRESS) {
1620                         GtkScrollType scroll_type;
1621
1622                         switch (event->keyval) {
1623                         case GDK_Up: 
1624                         case GDK_KP_Up:
1625                                 scroll_type = GTK_SCROLL_STEP_UP; break;
1626                         case GDK_Down: 
1627                         case GDK_KP_Down:
1628                                 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1629                         case GDK_Page_Up:
1630                         case GDK_KP_Page_Up:
1631                                 scroll_type = GTK_SCROLL_PAGE_UP; break;
1632                         case GDK_Page_Down:
1633                         case GDK_KP_Page_Down:
1634                                 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1635                         case GDK_Home:
1636                         case GDK_KP_Home:
1637                                 scroll_type = GTK_SCROLL_START; break;
1638                         case GDK_End:
1639                         case GDK_KP_End:
1640                                 scroll_type = GTK_SCROLL_END; break;
1641                         default: scroll_type = GTK_SCROLL_NONE;
1642                         }
1643
1644                         /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",  */
1645                         /*                     scroll_type, FALSE, &return_value); */
1646                         return FALSE;
1647                 } else {
1648                         return FALSE;
1649                 }
1650         } else {
1651                 return FALSE;
1652         }
1653 }
1654
1655 gboolean
1656 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1657 {
1658         GtkTreePath *path;
1659         ModestMsgViewWindowPrivate *priv;
1660         GtkTreeIter tmp_iter;
1661         gboolean is_last_selected;
1662
1663         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1664         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1665
1666         /*if no model (so no rows at all), then virtually we are the last*/
1667         if (!priv->header_model || !priv->row_reference)
1668                 return TRUE;
1669
1670         if (!gtk_tree_row_reference_valid (priv->row_reference))
1671                 return TRUE;
1672
1673         path = gtk_tree_row_reference_get_path (priv->row_reference);
1674         if (path == NULL)
1675                 return TRUE;
1676
1677         is_last_selected = TRUE;
1678         while (is_last_selected) {
1679                 TnyHeader *header;
1680                 gtk_tree_path_next (path);
1681                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1682                         break;
1683                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1684                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1685                                 &header, -1);
1686                 if (header) {
1687                         if (msg_is_visible (header, priv->is_outbox))
1688                                 is_last_selected = FALSE;
1689                         g_object_unref(G_OBJECT(header));
1690                 }
1691         }
1692         gtk_tree_path_free (path);
1693         return is_last_selected;
1694 }
1695
1696 gboolean
1697 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1698 {
1699         ModestMsgViewWindowPrivate *priv;
1700
1701         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1702         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1703
1704         return priv->header_model != NULL;
1705 }
1706
1707 gboolean
1708 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1709 {
1710         ModestMsgViewWindowPrivate *priv;
1711
1712         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1713         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1714
1715         return priv->is_search_result;
1716 }
1717
1718 static gboolean
1719 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1720 {
1721         if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1722                 return FALSE;
1723         if (!check_outbox) {
1724                 return TRUE;
1725         } else {
1726                 ModestTnySendQueueStatus status;
1727                 status = modest_tny_all_send_queues_get_msg_status (header);
1728                 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1729                         (status != MODEST_TNY_SEND_QUEUE_SENDING));
1730         }
1731 }
1732
1733 gboolean
1734 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1735 {
1736         GtkTreePath *path;
1737         ModestMsgViewWindowPrivate *priv;
1738         gboolean is_first_selected;
1739         GtkTreeIter tmp_iter;
1740
1741         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1742         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1743
1744         /*if no model (so no rows at all), then virtually we are the first*/
1745         if (!priv->header_model || !priv->row_reference)
1746                 return TRUE;
1747
1748         if (!gtk_tree_row_reference_valid (priv->row_reference))
1749                 return TRUE;
1750
1751         path = gtk_tree_row_reference_get_path (priv->row_reference);
1752         if (!path)
1753                 return TRUE;
1754
1755         is_first_selected = TRUE;
1756         while (is_first_selected) {
1757                 TnyHeader *header;
1758                 if(!gtk_tree_path_prev (path))
1759                         break;
1760                 /* Here the 'if' is needless for logic, but let make sure
1761                  * iter is valid for gtk_tree_model_get. */
1762                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1763                         break;
1764                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1765                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1766                                 &header, -1);
1767                 if (header) {
1768                         if (msg_is_visible (header, priv->is_outbox))
1769                                 is_first_selected = FALSE;
1770                         g_object_unref(G_OBJECT(header));
1771                 }
1772         }
1773         gtk_tree_path_free (path);
1774         return is_first_selected;
1775 }
1776
1777 typedef struct {
1778         TnyHeader *header;
1779         GtkTreeRowReference *row_reference;
1780 } MsgReaderInfo;
1781
1782 static void
1783 message_reader_performer (gboolean canceled, 
1784                           GError *err,
1785                           GtkWindow *parent_window, 
1786                           TnyAccount *account, 
1787                           gpointer user_data)
1788 {
1789         ModestMailOperation *mail_op = NULL;
1790         MsgReaderInfo *info;
1791
1792         info = (MsgReaderInfo *) user_data;
1793         if (canceled || err) {
1794                 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1795                 goto frees;
1796         }
1797
1798         /* Register the header - it'll be unregistered in the callback */
1799         modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1800
1801         /* New mail operation */
1802         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1803                                                                  modest_ui_actions_disk_operations_error_handler, 
1804                                                                  NULL, NULL);
1805
1806         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1807         modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1808         g_object_unref (mail_op);
1809
1810         /* Update dimming rules */
1811         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1812         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1813
1814  frees:
1815         /* Frees. The row_reference will be freed by the view_msg_cb callback */
1816         g_object_unref (info->header);
1817         g_slice_free (MsgReaderInfo, info);
1818 }
1819
1820
1821 /**
1822  * Reads the message whose summary item is @header. It takes care of
1823  * several things, among others:
1824  *
1825  * If the message was not previously downloaded then ask the user
1826  * before downloading. If there is no connection launch the connection
1827  * dialog. Update toolbar dimming rules.
1828  *
1829  * Returns: TRUE if the mail operation was started, otherwise if the
1830  * user do not want to download the message, or if the user do not
1831  * want to connect, then the operation is not issued
1832  **/
1833 static gboolean
1834 message_reader (ModestMsgViewWindow *window,
1835                 ModestMsgViewWindowPrivate *priv,
1836                 TnyHeader *header,
1837                 GtkTreeRowReference *row_reference)
1838 {
1839         ModestWindowMgr *mgr;
1840         TnyAccount *account;
1841         TnyFolder *folder;
1842         MsgReaderInfo *info;
1843
1844         g_return_val_if_fail (row_reference != NULL, FALSE);
1845
1846         mgr = modest_runtime_get_window_mgr ();
1847         /* Msg download completed */
1848         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1849
1850                 /* We set the header from model while we're loading */
1851                 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1852                 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1853
1854                 /* Ask the user if he wants to download the message if
1855                    we're not online */
1856                 if (!tny_device_is_online (modest_runtime_get_device())) {
1857                         GtkResponseType response;
1858
1859                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1860                                                                             _("mcen_nc_get_msg"));
1861                         if (response == GTK_RESPONSE_CANCEL) {
1862                                 update_window_title (window);
1863                                 return FALSE;
1864                         }
1865
1866                         folder = tny_header_get_folder (header);
1867                         info = g_slice_new (MsgReaderInfo);
1868                         info->header = g_object_ref (header);
1869                         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1870
1871                         /* Offer the connection dialog if necessary */
1872                         modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
1873                                                                        TRUE,
1874                                                                        TNY_FOLDER_STORE (folder),
1875                                                                        message_reader_performer, 
1876                                                                        info);
1877                         g_object_unref (folder);
1878                         return TRUE;
1879                 }
1880         }
1881         
1882         folder = tny_header_get_folder (header);
1883         account = tny_folder_get_account (folder);
1884         info = g_slice_new (MsgReaderInfo);
1885         info->header = g_object_ref (header);
1886         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1887         
1888         message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1889         g_object_unref (account);
1890         g_object_unref (folder);
1891
1892         return TRUE;
1893 }
1894
1895 gboolean        
1896 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1897 {
1898         ModestMsgViewWindowPrivate *priv;
1899         GtkTreePath *path= NULL;
1900         GtkTreeIter tmp_iter;
1901         TnyHeader *header;
1902         gboolean retval = TRUE;
1903         GtkTreeRowReference *row_reference = NULL;
1904
1905         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1906         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1907
1908         if (!priv->row_reference)
1909                 return FALSE;
1910
1911         /* Update the next row reference if it's not valid. This could
1912            happen if for example the header which it was pointing to,
1913            was deleted. The best place to do it is in the row-deleted
1914            handler but the tinymail model do not work like the glib
1915            tree models and reports the deletion when the row is still
1916            there */
1917         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1918                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1919                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1920                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1921                 }
1922         }
1923         if (priv->next_row_reference)
1924                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1925         if (path == NULL)
1926                 return FALSE;
1927
1928         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1929
1930         gtk_tree_model_get_iter (priv->header_model,
1931                                  &tmp_iter,
1932                                  path);
1933         gtk_tree_path_free (path);
1934
1935         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1936                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1937                             &header, -1);
1938         
1939         /* Read the message & show it */
1940         if (!message_reader (window, priv, header, row_reference)) {
1941                 retval = FALSE;
1942         }
1943         gtk_tree_row_reference_free (row_reference);
1944
1945         /* Free */
1946         g_object_unref (header);
1947
1948         return retval;
1949 }
1950
1951 gboolean        
1952 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1953 {
1954         ModestMsgViewWindowPrivate *priv = NULL;
1955         GtkTreePath *path;
1956         gboolean finished = FALSE;
1957         gboolean retval = FALSE;
1958
1959         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1960         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1961
1962         /* Return inmediatly if there is no header model */
1963         if (!priv->header_model || !priv->row_reference)
1964                 return FALSE;
1965
1966         path = gtk_tree_row_reference_get_path (priv->row_reference);
1967         while (!finished && gtk_tree_path_prev (path)) {
1968                 TnyHeader *header;
1969                 GtkTreeIter iter;
1970
1971                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1972                 gtk_tree_model_get (priv->header_model, &iter, 
1973                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1974                                     &header, -1);
1975                 finished = TRUE;
1976                 if (header) {
1977                         if (msg_is_visible (header, priv->is_outbox)) {
1978                                 GtkTreeRowReference *row_reference;
1979                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1980                                 /* Read the message & show it */
1981                                 retval = message_reader (window, priv, header, row_reference);
1982                                 gtk_tree_row_reference_free (row_reference);
1983                         } else {
1984                                 finished = FALSE;
1985                         }
1986                         g_object_unref (header);
1987                 }
1988         }
1989
1990         gtk_tree_path_free (path);
1991         return retval;
1992 }
1993
1994 static void
1995 view_msg_cb (ModestMailOperation *mail_op, 
1996              TnyHeader *header, 
1997              gboolean canceled,
1998              TnyMsg *msg, 
1999              GError *error,
2000              gpointer user_data)
2001 {
2002         ModestMsgViewWindow *self = NULL;
2003         ModestMsgViewWindowPrivate *priv = NULL;
2004         GtkTreeRowReference *row_reference = NULL;
2005
2006         /* Unregister the header (it was registered before creating the mail operation) */
2007         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2008
2009         row_reference = (GtkTreeRowReference *) user_data;
2010         if (canceled) {
2011                 gtk_tree_row_reference_free (row_reference);
2012                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2013                 if (self) {
2014                         /* Restore window title */
2015                         update_window_title (self);
2016                         g_object_unref (self);
2017                 }
2018                 return;
2019         }
2020
2021         /* If there was any error */
2022         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2023                 gtk_tree_row_reference_free (row_reference);
2024                 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2025                 if (self) {
2026                         /* Restore window title */
2027                         update_window_title (self);
2028                         g_object_unref (self);
2029                 }
2030                 return;
2031         }
2032
2033         /* Get the window */ 
2034         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2035         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2036         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2037
2038         /* Update the row reference */
2039         if (priv->row_reference != NULL) {
2040                 gtk_tree_row_reference_free (priv->row_reference);
2041                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2042                 if (priv->next_row_reference != NULL) {
2043                         gtk_tree_row_reference_free (priv->next_row_reference);
2044                 }
2045                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2046                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2047         }
2048
2049         /* Mark header as read */
2050         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2051                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2052
2053         /* Set new message */
2054         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2055                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2056                 modest_msg_view_window_update_priority (self);
2057                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2058                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2059         }
2060
2061         /* Set the new message uid of the window  */
2062         if (priv->msg_uid) {
2063                 g_free (priv->msg_uid);
2064                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2065         }
2066
2067         /* Notify the observers */
2068         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
2069                        0, priv->header_model, priv->row_reference);
2070
2071         /* Frees */
2072         g_object_unref (self);
2073         gtk_tree_row_reference_free (row_reference);            
2074 }
2075
2076 TnyFolderType
2077 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2078 {
2079         ModestMsgViewWindowPrivate *priv;
2080         TnyMsg *msg;
2081         TnyFolderType folder_type;
2082
2083         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2084
2085         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2086
2087         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2088         if (msg) {
2089                 TnyFolder *folder;
2090
2091                 folder = tny_msg_get_folder (msg);
2092                 if (folder) {
2093                         folder_type = modest_tny_folder_guess_folder_type (folder);
2094                         g_object_unref (folder);
2095                 }
2096                 g_object_unref (msg);
2097         }
2098
2099         return folder_type;
2100 }
2101
2102
2103 static void
2104 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2105 {
2106         ModestMsgViewWindowPrivate *priv;
2107         TnyHeader *header = NULL;
2108         TnyHeaderFlags flags = 0;
2109
2110         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2111
2112         if (priv->header_model && priv->row_reference) {
2113                 GtkTreeIter iter;
2114                 GtkTreePath *path = NULL;
2115
2116                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2117                 g_return_if_fail (path != NULL);
2118                 gtk_tree_model_get_iter (priv->header_model, 
2119                                          &iter, 
2120                                          gtk_tree_row_reference_get_path (priv->row_reference));
2121
2122                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2123                                     &header, -1);
2124                 gtk_tree_path_free (path);
2125         } else {
2126                 TnyMsg *msg;
2127                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2128                 if (msg) {
2129                         header = tny_msg_get_header (msg);
2130                         g_object_unref (msg);
2131                 }
2132         }
2133
2134         if (header) {
2135                 flags = tny_header_get_flags (header);
2136                 g_object_unref(G_OBJECT(header));
2137         }
2138
2139         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2140
2141 }
2142
2143 static void
2144 toolbar_resize (ModestMsgViewWindow *self)
2145 {
2146         ModestMsgViewWindowPrivate *priv = NULL;
2147         ModestWindowPrivate *parent_priv = NULL;
2148         GtkWidget *widget;
2149         gint static_button_size;
2150         ModestWindowMgr *mgr;
2151
2152         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2153         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2154         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2155
2156         mgr = modest_runtime_get_window_mgr ();
2157         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2158
2159         if (parent_priv->toolbar) {
2160                 /* left size buttons */
2161                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2162                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2163                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2164                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2165                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2166                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2167                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2168                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2169                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2170                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2171                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2172                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2173                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2174                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2175                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2176                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2177
2178                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2180                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2181                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2182         }
2183 }
2184
2185 static void
2186 modest_msg_view_window_show_toolbar (ModestWindow *self,
2187                                      gboolean show_toolbar)
2188 {
2189         ModestMsgViewWindowPrivate *priv = NULL;
2190         ModestWindowPrivate *parent_priv;
2191
2192         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2193         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2194
2195         /* Set optimized view status */
2196         priv->optimized_view = !show_toolbar;
2197
2198         if (!parent_priv->toolbar) {
2199                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2200                                                                   "/ToolBar");
2201                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2202                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2203
2204                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2205                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2206                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2207
2208                 /* Add to window */
2209                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2210                                            GTK_TOOLBAR (parent_priv->toolbar));
2211
2212         }
2213
2214         if (show_toolbar) {
2215                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2216                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2217                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2218
2219                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2220                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2221                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2222                 else
2223                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2224
2225         } else {
2226                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2227                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2228         }
2229 }
2230
2231 static void 
2232 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2233                                                GdkEvent *event,
2234                                                ModestMsgViewWindow *window)
2235 {
2236         if (!GTK_WIDGET_VISIBLE (window))
2237                 return;
2238
2239         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2240 }
2241
2242 gboolean 
2243 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2244 {
2245         ModestMsgViewWindowPrivate *priv;
2246         
2247         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2248         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2249
2250         return priv->progress_hint;
2251 }
2252
2253 static gboolean
2254 observers_empty (ModestMsgViewWindow *self)
2255 {
2256         GSList *tmp = NULL;
2257         ModestMsgViewWindowPrivate *priv;
2258         gboolean is_empty = TRUE;
2259         guint pending_ops = 0;
2260  
2261         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2262         tmp = priv->progress_widgets;
2263
2264         /* Check all observers */
2265         while (tmp && is_empty)  {
2266                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2267                 is_empty = pending_ops == 0;
2268                 
2269                 tmp = g_slist_next(tmp);
2270         }
2271         
2272         return is_empty;
2273 }
2274
2275 static void
2276 on_account_removed (TnyAccountStore *account_store, 
2277                     TnyAccount *account,
2278                     gpointer user_data)
2279 {
2280         /* Do nothing if it's a transport account, because we only
2281            show the messages of a store account */
2282         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2283                 const gchar *parent_acc = NULL;
2284                 const gchar *our_acc = NULL;
2285
2286                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2287                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2288
2289                 /* Close this window if I'm showing a message of the removed account */
2290                 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2291                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2292         }
2293 }
2294
2295 static void 
2296 on_mail_operation_started (ModestMailOperation *mail_op,
2297                            gpointer user_data)
2298 {
2299         ModestMsgViewWindow *self;
2300         ModestMailOperationTypeOperation op_type;
2301         GSList *tmp;
2302         ModestMsgViewWindowPrivate *priv;
2303         GObject *source = NULL;
2304
2305         self = MODEST_MSG_VIEW_WINDOW (user_data);
2306         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2307         op_type = modest_mail_operation_get_type_operation (mail_op);
2308         tmp = priv->progress_widgets;
2309         source = modest_mail_operation_get_source(mail_op);
2310         if (G_OBJECT (self) == source) {
2311                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2312                         set_toolbar_transfer_mode(self);
2313                         while (tmp) {
2314                                 modest_progress_object_add_operation (
2315                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2316                                                 mail_op);
2317                                 tmp = g_slist_next (tmp);
2318                         }
2319                 }
2320         }
2321         g_object_unref (source);
2322 }
2323
2324 static void 
2325 on_mail_operation_finished (ModestMailOperation *mail_op,
2326                             gpointer user_data)
2327 {
2328         ModestMsgViewWindow *self;
2329         ModestMailOperationTypeOperation op_type;
2330         GSList *tmp;
2331         ModestMsgViewWindowPrivate *priv;
2332         
2333         self = MODEST_MSG_VIEW_WINDOW (user_data);
2334         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2335         op_type = modest_mail_operation_get_type_operation (mail_op);
2336         tmp = priv->progress_widgets;
2337         
2338         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2339                 while (tmp) {
2340                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2341                                                                  mail_op);
2342                         tmp = g_slist_next (tmp);
2343                 }
2344
2345                 /* If no more operations are being observed, NORMAL mode is enabled again */
2346                 if (observers_empty (self)) {
2347                         set_progress_hint (self, FALSE);
2348                 }
2349         }
2350
2351         /* Update dimming rules. We have to do this right here
2352            and not in view_msg_cb because at that point the
2353            transfer mode is still enabled so the dimming rule
2354            won't let the user delete the message that has been
2355            readed for example */
2356         check_dimming_rules_after_change (self);
2357
2358 }
2359
2360 static void
2361 on_queue_changed (ModestMailOperationQueue *queue,
2362                   ModestMailOperation *mail_op,
2363                   ModestMailOperationQueueNotification type,
2364                   ModestMsgViewWindow *self)
2365 {       
2366         ModestMsgViewWindowPrivate *priv;
2367
2368         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2369
2370         /* If this operations was created by another window, do nothing */
2371         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2372             return;
2373
2374         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2375                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2376                                                                G_OBJECT (mail_op),
2377                                                                "operation-started",
2378                                                                G_CALLBACK (on_mail_operation_started),
2379                                                                self);
2380                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2381                                                                G_OBJECT (mail_op),
2382                                                                "operation-finished",
2383                                                                G_CALLBACK (on_mail_operation_finished),
2384                                                                self);
2385         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2386                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2387                                                                   G_OBJECT (mail_op),
2388                                                                   "operation-started");
2389                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2390                                                                   G_OBJECT (mail_op),
2391                                                                   "operation-finished");
2392         }
2393 }
2394
2395 TnyList *
2396 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2397 {
2398         ModestMsgViewWindowPrivate *priv;
2399         TnyList *selected_attachments = NULL;
2400         
2401         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2402         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2403
2404         /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2405         selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2406         
2407         return selected_attachments;
2408 }
2409
2410 typedef struct {
2411         ModestMsgViewWindow *self;
2412         gchar *file_path;
2413 } DecodeAsyncHelper;
2414
2415 static void
2416 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2417                                    gboolean cancelled, 
2418                                    TnyStream *stream, 
2419                                    GError *err, 
2420                                    gpointer user_data)
2421 {
2422         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2423
2424         /* It could happen that the window was closed */
2425         if (GTK_WIDGET_VISIBLE (helper->self))
2426                 set_progress_hint (helper->self, FALSE);
2427
2428         if (cancelled || err) {
2429                 if (err) {
2430                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2431                         modest_platform_information_banner (NULL, NULL, msg);
2432                         g_free (msg);
2433                 }
2434                 goto free;
2435         }
2436
2437         /* make the file read-only */
2438         g_chmod(helper->file_path, 0444);
2439
2440         /* Activate the file */
2441         modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2442
2443  free:
2444         /* Frees */
2445         g_object_unref (helper->self);
2446         g_free (helper->file_path);
2447         g_slice_free (DecodeAsyncHelper, helper);
2448 }
2449
2450 void
2451 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2452                                         TnyMimePart *mime_part)
2453 {
2454         ModestMsgViewWindowPrivate *priv;
2455         const gchar *msg_uid;
2456         gchar *attachment_uid = NULL;
2457         gint attachment_index = 0;
2458         TnyList *attachments;
2459
2460         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2461         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2462         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2463
2464         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2465         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2466         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2467         g_object_unref (attachments);
2468
2469         if (msg_uid && attachment_index >= 0) {
2470                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2471         }
2472
2473         if (mime_part == NULL) {
2474                 gboolean error = FALSE;
2475                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2476                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2477                         error = TRUE;
2478                 } else if (tny_list_get_length (selected_attachments) > 1) {
2479                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2480                         error = TRUE;
2481                 } else {
2482                         TnyIterator *iter;
2483                         iter = tny_list_create_iterator (selected_attachments);
2484                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2485                         g_object_unref (iter);
2486                 }
2487                 if (selected_attachments)
2488                         g_object_unref (selected_attachments);
2489
2490                 if (error)
2491                         goto frees;
2492         } else {
2493                 g_object_ref (mime_part);
2494         }
2495
2496         if (tny_mime_part_is_purged (mime_part))
2497                 goto frees;
2498
2499         if (!modest_tny_mime_part_is_msg (mime_part)) {
2500                 gchar *filepath = NULL;
2501                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2502                 gboolean show_error_banner = FALSE;
2503                 TnyFsStream *temp_stream = NULL;
2504                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2505                                                                &filepath);
2506
2507                 if (temp_stream != NULL) {
2508                         DecodeAsyncHelper *helper;
2509
2510                         /* Activate progress hint */
2511                         set_progress_hint (window, TRUE);
2512
2513                         helper = g_slice_new0 (DecodeAsyncHelper);
2514                         helper->self = g_object_ref (window);
2515                         helper->file_path = g_strdup (filepath);
2516
2517                         tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2518                                                               on_decode_to_stream_async_handler,
2519                                                               NULL,
2520                                                               helper);
2521                         g_object_unref (temp_stream);
2522                         /* NOTE: files in the temporary area will be automatically
2523                          * cleaned after some time if they are no longer in use */
2524                 } else {
2525                         if (filepath) {
2526                                 const gchar *content_type;
2527                                 /* the file may already exist but it isn't writable,
2528                                  * let's try to open it anyway */
2529                                 content_type = tny_mime_part_get_content_type (mime_part);
2530                                 modest_platform_activate_file (filepath, content_type);
2531                         } else {
2532                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2533                                 show_error_banner = TRUE;
2534                         }
2535                 }
2536                 if (filepath)
2537                         g_free (filepath);
2538                 if (show_error_banner)
2539                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2540         } else {
2541                 /* message attachment */
2542                 TnyHeader *header = NULL;
2543                 ModestWindowMgr *mgr;
2544                 ModestWindow *msg_win = NULL;
2545                 gboolean found;
2546
2547                 header = tny_msg_get_header (TNY_MSG (mime_part));
2548                 mgr = modest_runtime_get_window_mgr ();
2549                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2550
2551                 if (found) {
2552                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
2553                          * thus, we don't do anything */
2554                         g_warning ("window for is already being created");
2555                 } else {
2556                         /* it's not found, so create a new window for it */
2557                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2558                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2559                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2560                         if (!account)
2561                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2562                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, 
2563                                                                              mailbox, attachment_uid);
2564                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2565                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2566                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2567                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2568                         else
2569                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2570                 }
2571         }
2572
2573  frees:
2574         if (attachment_uid)
2575                 g_free (attachment_uid);
2576         if (mime_part)
2577                 g_object_unref (mime_part);
2578 }
2579
2580 typedef struct
2581 {
2582         gchar *filename;
2583         TnyMimePart *part;
2584 } SaveMimePartPair;
2585
2586 typedef struct
2587 {
2588         GList *pairs;
2589         GnomeVFSResult result;
2590 } SaveMimePartInfo;
2591
2592 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2593 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2594 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2595 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2596
2597 static void
2598 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2599 {
2600         GList *node;
2601
2602         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2603                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2604                 g_free (pair->filename);
2605                 g_object_unref (pair->part);
2606                 g_slice_free (SaveMimePartPair, pair);
2607         }
2608         g_list_free (info->pairs);
2609         info->pairs = NULL;
2610         if (with_struct) {
2611                 g_slice_free (SaveMimePartInfo, info);
2612         }
2613 }
2614
2615 static gboolean
2616 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2617 {
2618         if (info->pairs != NULL) {
2619                 save_mime_part_to_file (info);
2620         } else {
2621                 /* This is a GDK lock because we are an idle callback and
2622                  * hildon_banner_show_information is or does Gtk+ code */
2623
2624                 gdk_threads_enter (); /* CHECKED */
2625                 save_mime_part_info_free (info, TRUE);
2626                 if (info->result == GNOME_VFS_OK) {
2627                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2628                 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2629                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2630                         modest_platform_information_banner (NULL, NULL, msg);
2631                         g_free (msg);
2632                 } else {
2633                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2634                 }
2635                 gdk_threads_leave (); /* CHECKED */
2636         }
2637
2638         return FALSE;
2639 }
2640
2641 static gpointer
2642 save_mime_part_to_file (SaveMimePartInfo *info)
2643 {
2644         GnomeVFSHandle *handle;
2645         TnyStream *stream;
2646         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2647
2648         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2649         if (info->result == GNOME_VFS_OK) {
2650                 GError *error = NULL;
2651                 stream = tny_vfs_stream_new (handle);
2652                 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2653                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2654
2655                         if ((error->domain == TNY_ERROR_DOMAIN) && 
2656                             (error->code == TNY_IO_ERROR_WRITE) &&
2657                             (errno == ENOSPC)) {
2658                                 info->result = GNOME_VFS_ERROR_NO_SPACE;
2659                         } else {
2660                                 info->result = GNOME_VFS_ERROR_IO;
2661                         }
2662                 }
2663                 g_object_unref (G_OBJECT (stream));
2664                 g_object_unref (pair->part);
2665                 g_slice_free (SaveMimePartPair, pair);
2666                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2667         } else {
2668                 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2669                 save_mime_part_info_free (info, FALSE);
2670         }
2671
2672         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2673         return NULL;
2674 }
2675
2676 static void
2677 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2678 {
2679         gboolean is_ok = TRUE;
2680         gint replaced_files = 0;
2681         const GList *files = info->pairs;
2682         const GList *iter;
2683
2684         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2685                 SaveMimePartPair *pair = iter->data;
2686                 if (modest_utils_file_exists (pair->filename)) {
2687                         replaced_files++;
2688                 }
2689         }
2690         if (replaced_files) {
2691                 GtkWidget *confirm_overwrite_dialog;
2692
2693                 if (replaced_files == 1) {
2694                         SaveMimePartPair *pair = files->data;
2695                         const gchar *filename = tny_mime_part_get_filename (pair->part);
2696                         gchar *message = g_strdup_printf ("%s\n%s",
2697                                                           _FM("docm_nc_replace_file"),
2698                                                           (filename) ? filename : "");
2699                         confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2700                         g_free (message);
2701                 } else {
2702                         confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2703                                                                                  _FM("docm_nc_replace_multiple"));
2704                 }
2705                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2706                         is_ok = FALSE;
2707
2708                 gtk_widget_destroy (confirm_overwrite_dialog);
2709         }
2710
2711         if (!is_ok) {
2712                 save_mime_part_info_free (info, TRUE);
2713         } else {
2714                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2715         }
2716
2717 }
2718
2719 static void
2720 save_attachments_response (GtkDialog *dialog,
2721                            gint       arg1,
2722                            gpointer   user_data)  
2723 {
2724         TnyList *mime_parts;
2725         gchar *chooser_uri;
2726         GList *files_to_save = NULL;
2727         gchar *current_folder;
2728
2729         mime_parts = TNY_LIST (user_data);
2730
2731         if (arg1 != GTK_RESPONSE_OK)
2732                 goto end;
2733
2734         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2735         current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2736         if (current_folder && current_folder != '\0') {
2737                 GError *err = NULL;
2738                 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, 
2739                                         current_folder,&err);
2740                 if (err != NULL) {
2741                         g_debug ("Error storing latest used folder: %s", err->message);
2742                         g_error_free (err);
2743                 }
2744         }
2745         g_free (current_folder);
2746
2747         if (!modest_utils_folder_writable (chooser_uri)) {
2748                 hildon_banner_show_information 
2749                         (NULL, NULL, _FM("sfil_ib_readonly_location"));
2750         } else {
2751                 TnyIterator *iter;
2752
2753                 iter = tny_list_create_iterator (mime_parts);
2754                 while (!tny_iterator_is_done (iter)) {
2755                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2756
2757                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2758                             !tny_mime_part_is_purged (mime_part) &&
2759                             (tny_mime_part_get_filename (mime_part) != NULL)) {
2760                                 SaveMimePartPair *pair;
2761
2762                                 pair = g_slice_new0 (SaveMimePartPair);
2763
2764                                 if (tny_list_get_length (mime_parts) > 1) {
2765                                         gchar *escaped = 
2766                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2767                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2768                                         g_free (escaped);
2769                                 } else {
2770                                         pair->filename = g_strdup (chooser_uri);
2771                                 }
2772                                 pair->part = mime_part;
2773                                 files_to_save = g_list_prepend (files_to_save, pair);
2774                         }
2775                         tny_iterator_next (iter);
2776                 }
2777                 g_object_unref (iter);
2778         }
2779         g_free (chooser_uri);
2780
2781         if (files_to_save != NULL) {
2782                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2783                 info->pairs = files_to_save;
2784                 info->result = TRUE;
2785                 save_mime_parts_to_file_with_checks (info);
2786         }
2787
2788  end:
2789         /* Free and close the dialog */
2790         g_object_unref (mime_parts);
2791         gtk_widget_destroy (GTK_WIDGET (dialog));
2792 }
2793
2794 void
2795 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, 
2796                                          TnyList *mime_parts)
2797 {
2798         ModestMsgViewWindowPrivate *priv;
2799         GtkWidget *save_dialog = NULL;
2800         gchar *conf_folder = NULL;
2801         gchar *filename = NULL;
2802         gchar *save_multiple_str = NULL;
2803
2804         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2805         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2806
2807         if (mime_parts == NULL) {
2808                 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2809                  * selection available */
2810                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2811                 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2812                         g_object_unref (mime_parts);
2813                         return;
2814                 }
2815                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2816                         if (mime_parts) {
2817                                 g_object_unref (mime_parts);
2818                                 mime_parts = NULL;
2819                         }
2820                         return;
2821                 }
2822         } else {
2823                 g_object_ref (mime_parts);
2824         }
2825
2826         /* prepare dialog */
2827         if (tny_list_get_length (mime_parts) == 1) {
2828                 TnyIterator *iter;
2829                 /* only one attachment selected */
2830                 iter = tny_list_create_iterator (mime_parts);
2831                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2832                 g_object_unref (iter);
2833                 if (!modest_tny_mime_part_is_msg (mime_part) && 
2834                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2835                     !tny_mime_part_is_purged (mime_part)) {
2836                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
2837                 } else {
2838                         /* TODO: show any error? */
2839                         g_warning ("Tried to save a non-file attachment");
2840                         g_object_unref (mime_parts);
2841                         return;
2842                 }
2843                 g_object_unref (mime_part);
2844         } else {
2845                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2846                                                      tny_list_get_length (mime_parts));
2847         }
2848
2849         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2850                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2851
2852         /* set folder */
2853         conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2854         if (conf_folder && conf_folder[0] != '\0') {
2855                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2856         } else {
2857                 gchar *docs_folder;
2858                 /* Set the default folder to images folder */
2859                 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2860                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2861                 g_free (docs_folder);
2862         }
2863         g_free (conf_folder);
2864
2865         /* set filename */
2866         if (filename) {
2867                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2868                                                    filename);
2869                 g_free (filename);
2870         }
2871
2872         /* if multiple, set multiple string */
2873         if (save_multiple_str) {
2874                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2875                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2876         }
2877
2878         /* We must run this asynchronously, because the hildon dialog
2879            performs a gtk_dialog_run by itself which leads to gdk
2880            deadlocks */
2881         g_signal_connect (save_dialog, "response", 
2882                           G_CALLBACK (save_attachments_response), mime_parts);
2883
2884         gtk_widget_show_all (save_dialog);
2885 }
2886
2887 static gboolean
2888 show_remove_attachment_information (gpointer userdata)
2889 {
2890         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2891         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2892
2893         /* We're outside the main lock */
2894         gdk_threads_enter ();
2895
2896         if (priv->remove_attachment_banner != NULL) {
2897                 gtk_widget_destroy (priv->remove_attachment_banner);
2898                 g_object_unref (priv->remove_attachment_banner);
2899         }
2900
2901         priv->remove_attachment_banner = g_object_ref (
2902                 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2903
2904         gdk_threads_leave ();
2905
2906         return FALSE;
2907 }
2908
2909 void
2910 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2911 {
2912         ModestMsgViewWindowPrivate *priv;
2913         TnyList *mime_parts = NULL, *tmp;
2914         gchar *confirmation_message;
2915         gint response;
2916         gint n_attachments;
2917         TnyMsg *msg;
2918         TnyIterator *iter;
2919
2920         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2921         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2922
2923         /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2924          * because we don't have selection
2925          */
2926         mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2927
2928         /* Remove already purged messages from mime parts list. We use
2929            a copy of the list to remove items in the original one */
2930         tmp = tny_list_copy (mime_parts);
2931         iter = tny_list_create_iterator (tmp);
2932         while (!tny_iterator_is_done (iter)) {
2933                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2934                 if (tny_mime_part_is_purged (part))
2935                         tny_list_remove (mime_parts, (GObject *) part);
2936
2937                 g_object_unref (part);
2938                 tny_iterator_next (iter);
2939         }
2940         g_object_unref (tmp);
2941         g_object_unref (iter);
2942
2943         if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2944             tny_list_get_length (mime_parts) == 0) {
2945                 g_object_unref (mime_parts);
2946                 return;
2947         }
2948
2949         n_attachments = tny_list_get_length (mime_parts);
2950         if (n_attachments == 1) {
2951                 gchar *filename;
2952                 TnyMimePart *part;
2953
2954                 iter = tny_list_create_iterator (mime_parts);
2955                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2956                 g_object_unref (iter);
2957                 if (modest_tny_mime_part_is_msg (part)) {
2958                         TnyHeader *header;
2959                         header = tny_msg_get_header (TNY_MSG (part));
2960                         filename = tny_header_dup_subject (header);
2961                         g_object_unref (header);
2962                         if (filename == NULL)
2963                                 filename = g_strdup (_("mail_va_no_subject"));
2964                 } else {
2965                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2966                 }
2967                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2968                 g_free (filename);
2969                 g_object_unref (part);
2970         } else {
2971                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2972                                                                  "mcen_nc_purge_files_text", 
2973                                                                  n_attachments), n_attachments);
2974         }
2975         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2976                                                             confirmation_message);
2977         g_free (confirmation_message);
2978
2979         if (response != GTK_RESPONSE_OK) {
2980                 g_object_unref (mime_parts);
2981                 return;
2982         }
2983
2984         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2985
2986         iter = tny_list_create_iterator (mime_parts);
2987         while (!tny_iterator_is_done (iter)) {
2988                 TnyMimePart *part;
2989
2990                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2991                 tny_mime_part_set_purged (TNY_MIME_PART (part));
2992                 g_object_unref (part);
2993                 tny_iterator_next (iter);
2994         }
2995         g_object_unref (iter);
2996
2997         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2998         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2999         tny_msg_rewrite_cache (msg);
3000         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3001         g_object_unref (msg);
3002
3003         g_object_unref (mime_parts);
3004
3005         if (priv->purge_timeout > 0) {
3006                 g_source_remove (priv->purge_timeout);
3007                 priv->purge_timeout = 0;
3008         }
3009
3010         if (priv->remove_attachment_banner) {
3011                 gtk_widget_destroy (priv->remove_attachment_banner);
3012                 g_object_unref (priv->remove_attachment_banner);
3013                 priv->remove_attachment_banner = NULL;
3014         }
3015 }
3016
3017
3018 static void
3019 update_window_title (ModestMsgViewWindow *window)
3020 {
3021         ModestMsgViewWindowPrivate *priv;
3022         TnyMsg *msg = NULL;
3023         TnyHeader *header = NULL;
3024         gchar *subject = NULL;
3025
3026         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3027
3028         /* Note that if the window is closed while we're retrieving
3029            the message, this widget could de deleted */
3030         if (!priv->msg_view)
3031                 return;
3032
3033         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3034
3035         if (msg != NULL) {
3036                 header = tny_msg_get_header (msg);
3037                 subject = tny_header_dup_subject (header);
3038                 g_object_unref (header);
3039                 g_object_unref (msg);
3040         }
3041
3042         if ((subject == NULL)||(subject[0] == '\0')) {
3043                 g_free (subject);
3044                 subject = g_strdup (_("mail_va_no_subject"));
3045         }
3046
3047         gtk_window_set_title (GTK_WINDOW (window), subject);
3048 }
3049
3050
3051 static void
3052 on_move_focus (GtkWidget *widget,
3053                GtkDirectionType direction,
3054                gpointer userdata)
3055 {
3056         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3057 }
3058
3059 static TnyStream *
3060 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3061 {
3062         GnomeVFSResult result;
3063         GnomeVFSHandle *handle = NULL;
3064         GnomeVFSFileInfo *info = NULL;
3065         TnyStream *stream;
3066
3067         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3068         if (result != GNOME_VFS_OK) {
3069                 *expected_size = 0;
3070                 return NULL;
3071         }
3072         
3073         info = gnome_vfs_file_info_new ();
3074         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3075         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3076                 /* We put a "safe" default size for going to cache */
3077                 *expected_size = (300*1024);
3078         } else {
3079                 *expected_size = info->size;
3080         }
3081         gnome_vfs_file_info_unref (info);
3082
3083         stream = tny_vfs_stream_new (handle);
3084
3085         return stream;
3086
3087 }
3088
3089 typedef struct {
3090         gchar *uri;
3091         gchar *cache_id;
3092         TnyStream *output_stream;
3093         GtkWidget *msg_view;
3094         GtkWidget *window;
3095 } FetchImageData;
3096
3097 gboolean
3098 on_fetch_image_idle_refresh_view (gpointer userdata)
3099 {
3100
3101         FetchImageData *fidata = (FetchImageData *) userdata;
3102
3103         gdk_threads_enter ();
3104         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3105                 ModestMsgViewWindowPrivate *priv;
3106
3107                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3108                 priv->fetching_images--;
3109                 gtk_widget_queue_draw (fidata->msg_view);
3110                 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3111         }
3112         gdk_threads_leave ();
3113
3114         g_object_unref (fidata->msg_view);
3115         g_object_unref (fidata->window);
3116         g_slice_free (FetchImageData, fidata);
3117         return FALSE;
3118 }
3119
3120 static gpointer
3121 on_fetch_image_thread (gpointer userdata)
3122 {
3123         FetchImageData *fidata = (FetchImageData *) userdata;
3124         TnyStreamCache *cache;
3125         TnyStream *cache_stream;
3126
3127         cache = modest_runtime_get_images_cache ();
3128         cache_stream = 
3129                 tny_stream_cache_get_stream (cache, 
3130                                              fidata->cache_id, 
3131                                              (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, 
3132                                              (gpointer) fidata->uri);
3133         g_free (fidata->cache_id);
3134         g_free (fidata->uri);
3135
3136         if (cache_stream != NULL) {
3137                 char buffer[4096];
3138
3139                 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3140                         gssize nb_read;
3141
3142                         nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3143                         if (G_UNLIKELY (nb_read < 0)) {
3144                                 break;
3145                         } else if (G_LIKELY (nb_read > 0)) {
3146                                 gssize nb_written = 0;
3147
3148                                 while (G_UNLIKELY (nb_written < nb_read)) {
3149                                         gssize len;
3150
3151                                         len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3152                                                                 nb_read - nb_written);
3153                                         if (G_UNLIKELY (len < 0))
3154                                                 break;
3155                                         nb_written += len;
3156                                 }
3157                         }
3158                 }
3159                 tny_stream_close (cache_stream);
3160                 g_object_unref (cache_stream);
3161         }
3162
3163         tny_stream_close (fidata->output_stream);
3164         g_object_unref (fidata->output_stream);
3165
3166         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3167
3168         return NULL;
3169 }
3170
3171 static gboolean
3172 on_fetch_image (ModestMsgView *msgview,
3173                 const gchar *uri,
3174                 TnyStream *stream,
3175                 ModestMsgViewWindow *window)
3176 {
3177         const gchar *current_account;
3178         ModestMsgViewWindowPrivate *priv;
3179         FetchImageData *fidata;
3180
3181         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3182
3183         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3184
3185         fidata = g_slice_new0 (FetchImageData);
3186         fidata->msg_view = g_object_ref (msgview);
3187         fidata->window = g_object_ref (window);
3188         fidata->uri = g_strdup (uri);
3189         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3190         fidata->output_stream = g_object_ref (stream);
3191
3192         priv->fetching_images++;
3193         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3194                 g_object_unref (fidata->output_stream);
3195                 g_free (fidata->cache_id);
3196                 g_free (fidata->uri);
3197                 g_object_unref (fidata->msg_view);
3198                 g_slice_free (FetchImageData, fidata);
3199                 tny_stream_close (stream);
3200                 priv->fetching_images--;
3201                 update_progress_hint (window);
3202                 return FALSE;
3203         }
3204         update_progress_hint (window);
3205
3206         return TRUE;
3207 }
3208
3209 static void 
3210 setup_menu (ModestMsgViewWindow *self)
3211 {
3212         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3213
3214         /* Settings menu buttons */
3215         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3216                                            APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3217                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3218         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3219                                            APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3220                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3221
3222         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3223                                            APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3224                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3225         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3226                                            APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3227                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3228
3229         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3230                                            APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3231                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3232         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3233                                            APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3234                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3235
3236         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3237                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3238                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3239         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3240                                            APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3241                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3242
3243         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3244                                            APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3245                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3246         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3247                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
3248                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3249 }
3250
3251 void
3252 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3253 {
3254         ModestMsgViewWindowPrivate *priv;
3255         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3256         GSList *recipients = NULL;
3257         TnyMsg *msg = NULL;
3258         gboolean contacts_to_add = FALSE;
3259
3260         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3261         if (msg == NULL) {
3262                 TnyHeader *header;
3263
3264                 header = modest_msg_view_window_get_header (self);
3265                 if (header == NULL)
3266                         return;
3267                 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3268                 g_object_unref (header);
3269         } else {
3270                 recipients = modest_tny_msg_get_all_recipients_list (msg);
3271                 g_object_unref (msg);
3272         }
3273
3274         if (recipients != NULL) {
3275                 GtkWidget *picker_dialog;
3276                 GtkWidget *selector;
3277                 GSList *node;
3278                 gchar *selected = NULL;
3279
3280                 selector = hildon_touch_selector_new_text ();
3281                 g_object_ref (selector);
3282
3283                 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3284                         if (!modest_address_book_has_address ((const gchar *) node->data)) {
3285                                 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), 
3286                                                                    (const gchar *) node->data);
3287                                 contacts_to_add = TRUE;
3288                         }
3289                 }
3290
3291                 if (contacts_to_add) {
3292                         gint picker_result;
3293
3294                         picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3295                         gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3296
3297                         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog), 
3298                                                            HILDON_TOUCH_SELECTOR (selector));
3299                         
3300                         picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3301
3302                         if (picker_result == GTK_RESPONSE_OK) {
3303                                 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3304                         }
3305                         gtk_widget_destroy (picker_dialog);
3306
3307                         if (selected)
3308                                 modest_address_book_add_address (selected);
3309                         g_free (selected);
3310
3311                 } else {
3312
3313                         g_object_unref (selector);
3314
3315                 }
3316         }
3317         
3318         if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3319 }
3320
3321 static gboolean 
3322 _modest_msg_view_window_map_event (GtkWidget *widget,
3323                                    GdkEvent *event,
3324                                    gpointer userdata)
3325 {
3326         ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3327
3328         update_progress_hint (self);
3329
3330         return FALSE;
3331 }
3332
3333 void
3334 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3335 {
3336         ModestMsgViewWindowPrivate *priv;
3337         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3338
3339         modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3340 }
3341
3342 gboolean 
3343 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3344 {
3345         ModestMsgViewWindowPrivate *priv;
3346         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3347
3348         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3349
3350         return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3351 }
3352
3353 void 
3354 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3355 {
3356         ModestMsgViewWindowPrivate *priv;
3357         TnyHeader *header;
3358
3359         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3360
3361         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3362         header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));     
3363
3364         if (!message_reader (self, priv, header, priv->row_reference)) {
3365                 g_warning ("Shouldn't happen, trying to reload a message failed");
3366         }
3367
3368         g_object_unref (header);
3369 }