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