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