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