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