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