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