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