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