Reorganize the message view menu, following the new ui specs:
[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         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2213         if (canceled || !self || MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self)->msg_view == NULL ) {
2214                 if (row_reference)
2215                         gtk_tree_row_reference_free (row_reference);
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                 if (self) {
2230                         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2231                         /* First we check if the parent is a folder window */
2232                         if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2233                                 gboolean is_merge;
2234                                 TnyAccount *account = NULL;
2235                                 GtkWidget *header_window = NULL;
2236
2237                                 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2238
2239                                 /* Get the account */
2240                                 if (!is_merge)
2241                                         account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2242                                                                                   priv->msg_uid);
2243
2244                                 if (is_merge || account) {
2245                                         TnyFolder *folder = NULL;
2246
2247                                         /* Try to get the message, if it's already downloaded
2248                                            we don't need to connect */
2249                                         if (account) {
2250                                                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), 
2251                                                                                                        priv->msg_uid);
2252                                         } else {
2253                                                 ModestTnyAccountStore *account_store;
2254                                                 ModestTnyLocalFoldersAccount *local_folders_account;
2255
2256                                                 account_store = modest_runtime_get_account_store ();
2257                                                 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2258                                                         modest_tny_account_store_get_local_folders_account (account_store));
2259                                                 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2260                                                 g_object_unref (local_folders_account);
2261                                         }
2262                                         if (account) g_object_unref (account);
2263
2264                                         if (folder) {
2265                                                 header_window = (GtkWidget *)
2266                                                         modest_header_window_new (
2267                                                                 folder, 
2268                                                                 modest_window_get_active_account (MODEST_WINDOW (self)), 
2269                                                                 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2270                                                 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2271                                                                                         MODEST_WINDOW (header_window),
2272                                                                                         NULL)) {
2273                                                         gtk_widget_destroy (GTK_WIDGET (header_window));
2274                                                 } else {
2275                                                         gtk_widget_show_all (GTK_WIDGET (header_window));
2276                                                 }
2277                                                 g_object_unref (folder);
2278                                         }
2279                                 }
2280                         }
2281
2282
2283                         /* Restore window title */
2284                         update_window_title (self);
2285                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2286                         g_object_unref (self);
2287                 }
2288                 return;
2289         }
2290
2291         /* Get the window */ 
2292         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2293         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2294
2295         /* Update the row reference */
2296         if (priv->row_reference != NULL) {
2297                 gtk_tree_row_reference_free (priv->row_reference);
2298                 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2299                 if (priv->next_row_reference != NULL) {
2300                         gtk_tree_row_reference_free (priv->next_row_reference);
2301                 }
2302                 if (priv->row_reference) {
2303                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2304                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2305                 } else {
2306                         priv->next_row_reference = NULL;
2307                 }
2308         }
2309
2310         /* Mark header as read */
2311         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN)) {
2312                 gchar *uid;
2313
2314                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2315                 uid = modest_tny_folder_get_header_unique_id (header);
2316                 modest_platform_emit_msg_read_changed_signal (uid, TRUE);
2317                 g_free (uid);
2318         }
2319
2320         /* Set new message */
2321         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2322                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2323                 modest_msg_view_window_update_priority (self);
2324                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2325                 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2326                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2327         }
2328
2329         /* Set the new message uid of the window  */
2330         if (priv->msg_uid) {
2331                 g_free (priv->msg_uid);
2332                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2333         }
2334
2335         /* Notify the observers */
2336         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2337                        0, priv->header_model, priv->row_reference);
2338
2339         /* Sync the flags if the message is not opened from a header
2340            model, i.e, if it's opened from a notification */
2341         if (!priv->header_model)
2342                 sync_flags (self);
2343
2344         /* Frees */
2345         g_object_unref (self);
2346         if (row_reference)
2347                 gtk_tree_row_reference_free (row_reference);
2348 }
2349
2350 TnyFolderType
2351 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2352 {
2353         ModestMsgViewWindowPrivate *priv;
2354         TnyMsg *msg;
2355         TnyFolderType folder_type;
2356
2357         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2358
2359         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2360
2361         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2362         if (msg) {
2363                 TnyFolder *folder;
2364
2365                 folder = tny_msg_get_folder (msg);
2366                 if (folder) {
2367                         folder_type = modest_tny_folder_guess_folder_type (folder);
2368                         g_object_unref (folder);
2369                 }
2370                 g_object_unref (msg);
2371         }
2372
2373         return folder_type;
2374 }
2375
2376
2377 static void
2378 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2379 {
2380         ModestMsgViewWindowPrivate *priv;
2381         TnyHeader *header = NULL;
2382         TnyHeaderFlags flags = 0;
2383
2384         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2385
2386         if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2387                 GtkTreeIter iter;
2388                 GtkTreePath *path = NULL;
2389
2390                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2391                 g_return_if_fail (path != NULL);
2392                 gtk_tree_model_get_iter (priv->header_model, 
2393                                          &iter, 
2394                                          gtk_tree_row_reference_get_path (priv->row_reference));
2395
2396                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2397                                     &header, -1);
2398                 gtk_tree_path_free (path);
2399         } else {
2400                 TnyMsg *msg;
2401                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2402                 if (msg) {
2403                         header = tny_msg_get_header (msg);
2404                         g_object_unref (msg);
2405                 }
2406         }
2407
2408         if (header) {
2409                 flags = tny_header_get_flags (header);
2410                 g_object_unref(G_OBJECT(header));
2411         }
2412
2413         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2414
2415 }
2416
2417 static void
2418 toolbar_resize (ModestMsgViewWindow *self)
2419 {
2420         ModestMsgViewWindowPrivate *priv = NULL;
2421         ModestWindowPrivate *parent_priv = NULL;
2422         GtkWidget *widget;
2423         gint static_button_size;
2424         ModestWindowMgr *mgr;
2425
2426         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2427         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2428         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2429
2430         mgr = modest_runtime_get_window_mgr ();
2431         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2432
2433         if (parent_priv->toolbar) {
2434                 /* Set expandable and homogeneous tool buttons */
2435                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2436                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2437                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2438                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2439                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2440                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2441                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2442                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2443                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2444                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2445                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2446                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2447                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2448                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2449                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2450                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2451                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2452                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2453                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2454         }
2455 }
2456
2457 static void
2458 modest_msg_view_window_show_toolbar (ModestWindow *self,
2459                                      gboolean show_toolbar)
2460 {
2461         ModestMsgViewWindowPrivate *priv = NULL;
2462         ModestWindowPrivate *parent_priv;
2463
2464         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2465         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2466
2467         /* Set optimized view status */
2468         priv->optimized_view = !show_toolbar;
2469
2470         if (!parent_priv->toolbar) {
2471                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2472                                                                   "/ToolBar");
2473                 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2474                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2475
2476                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2477                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2478                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2479
2480                 /* Add to window */
2481                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2482                                            GTK_TOOLBAR (parent_priv->toolbar));
2483
2484         }
2485
2486         if (show_toolbar) {
2487                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2488                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2489                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2490
2491                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2492                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2493                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2494                 else
2495                         set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2496
2497         } else {
2498                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2499                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2500         }
2501 }
2502
2503 static void 
2504 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2505                                                GdkEvent *event,
2506                                                ModestMsgViewWindow *window)
2507 {
2508         if (!GTK_WIDGET_VISIBLE (window))
2509                 return;
2510
2511         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2512 }
2513
2514 gboolean 
2515 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2516 {
2517         ModestMsgViewWindowPrivate *priv;
2518         
2519         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2520         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2521
2522         return priv->progress_hint;
2523 }
2524
2525 static gboolean
2526 observers_empty (ModestMsgViewWindow *self)
2527 {
2528         GSList *tmp = NULL;
2529         ModestMsgViewWindowPrivate *priv;
2530         gboolean is_empty = TRUE;
2531         guint pending_ops = 0;
2532  
2533         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2534         tmp = priv->progress_widgets;
2535
2536         /* Check all observers */
2537         while (tmp && is_empty)  {
2538                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2539                 is_empty = pending_ops == 0;
2540                 
2541                 tmp = g_slist_next(tmp);
2542         }
2543         
2544         return is_empty;
2545 }
2546
2547 static void
2548 on_account_removed (TnyAccountStore *account_store, 
2549                     TnyAccount *account,
2550                     gpointer user_data)
2551 {
2552         /* Do nothing if it's a transport account, because we only
2553            show the messages of a store account */
2554         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2555                 const gchar *parent_acc = NULL;
2556                 const gchar *our_acc = NULL;
2557
2558                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2559                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2560
2561                 /* Close this window if I'm showing a message of the removed account */
2562                 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2563                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2564         }
2565 }
2566
2567 static void 
2568 on_mail_operation_started (ModestMailOperation *mail_op,
2569                            gpointer user_data)
2570 {
2571         ModestMsgViewWindow *self;
2572         ModestMailOperationTypeOperation op_type;
2573         GSList *tmp;
2574         ModestMsgViewWindowPrivate *priv;
2575         GObject *source = NULL;
2576
2577         self = MODEST_MSG_VIEW_WINDOW (user_data);
2578         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2579         op_type = modest_mail_operation_get_type_operation (mail_op);
2580         tmp = priv->progress_widgets;
2581         source = modest_mail_operation_get_source(mail_op);
2582         if (G_OBJECT (self) == source) {
2583                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2584                     op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2585                     op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2586                         set_progress_hint (self, TRUE);
2587                         while (tmp) {
2588                                 modest_progress_object_add_operation (
2589                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2590                                                 mail_op);
2591                                 tmp = g_slist_next (tmp);
2592                         }
2593                 }
2594         }
2595         g_object_unref (source);
2596
2597         /* Update dimming rules */
2598         check_dimming_rules_after_change (self);
2599 }
2600
2601 static void
2602 on_mail_operation_finished (ModestMailOperation *mail_op,
2603                             gpointer user_data)
2604 {
2605         ModestMsgViewWindow *self;
2606         ModestMailOperationTypeOperation op_type;
2607         GSList *tmp;
2608         ModestMsgViewWindowPrivate *priv;
2609
2610         self = MODEST_MSG_VIEW_WINDOW (user_data);
2611         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2612         op_type = modest_mail_operation_get_type_operation (mail_op);
2613         tmp = priv->progress_widgets;
2614
2615         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2616             op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2617             op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2618                 while (tmp) {
2619                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2620                                                                  mail_op);
2621                         tmp = g_slist_next (tmp);
2622                 }
2623
2624                 /* If no more operations are being observed, NORMAL mode is enabled again */
2625                 if (observers_empty (self)) {
2626                         set_progress_hint (self, FALSE);
2627                 }
2628         }
2629
2630         /* Update dimming rules. We have to do this right here
2631            and not in view_msg_cb because at that point the
2632            transfer mode is still enabled so the dimming rule
2633            won't let the user delete the message that has been
2634            readed for example */
2635         check_dimming_rules_after_change (self);
2636 }
2637
2638 static void
2639 on_queue_changed (ModestMailOperationQueue *queue,
2640                   ModestMailOperation *mail_op,
2641                   ModestMailOperationQueueNotification type,
2642                   ModestMsgViewWindow *self)
2643 {       
2644         ModestMsgViewWindowPrivate *priv;
2645
2646         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2647
2648         /* If this operations was created by another window, do nothing */
2649         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2650             return;
2651
2652         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2653                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2654                                                                G_OBJECT (mail_op),
2655                                                                "operation-started",
2656                                                                G_CALLBACK (on_mail_operation_started),
2657                                                                self);
2658                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2659                                                                G_OBJECT (mail_op),
2660                                                                "operation-finished",
2661                                                                G_CALLBACK (on_mail_operation_finished),
2662                                                                self);
2663         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2664                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2665                                                                   G_OBJECT (mail_op),
2666                                                                   "operation-started");
2667                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2668                                                                   G_OBJECT (mail_op),
2669                                                                   "operation-finished");
2670         }
2671 }
2672
2673 TnyList *
2674 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2675 {
2676         ModestMsgViewWindowPrivate *priv;
2677         TnyList *selected_attachments = NULL;
2678         
2679         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2680         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2681
2682         /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2683         selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2684         
2685         return selected_attachments;
2686 }
2687
2688 typedef struct {
2689         ModestMsgViewWindow *self;
2690         gchar *file_path;
2691         gchar *attachment_uid;
2692 } DecodeAsyncHelper;
2693
2694 static void
2695 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2696                                    gboolean cancelled, 
2697                                    TnyStream *stream, 
2698                                    GError *err, 
2699                                    gpointer user_data)
2700 {
2701         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2702         const gchar *content_type;
2703         ModestMsgViewWindowPrivate *priv;
2704
2705         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
2706
2707         if (cancelled || err) {
2708                 if (err) {
2709                         gchar *msg;
2710                         if ((err->domain == TNY_ERROR_DOMAIN) && 
2711                             (err->code == TNY_IO_ERROR_WRITE) &&
2712                             (errno == ENOSPC)) {
2713                                 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2714                         } else {
2715                                 msg = g_strdup (_("mail_ib_file_operation_failed"));
2716                         }
2717                         modest_platform_information_banner (NULL, NULL, msg);
2718                         g_free (msg);
2719                 }
2720                 goto free;
2721         }
2722
2723         /* It could happen that the window was closed. So we
2724            assume it is a cancelation */
2725         if (!GTK_WIDGET_VISIBLE (helper->self))
2726                 goto free;
2727
2728         /* Remove the progress hint */
2729         set_progress_hint (helper->self, FALSE);
2730
2731         content_type = tny_mime_part_get_content_type (mime_part);
2732         if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2733                 ModestWindowMgr *mgr;
2734                 ModestWindow *msg_win = NULL;
2735                 TnyMsg * msg;
2736                 gchar *account;
2737                 const gchar *mailbox;
2738                 TnyStream *file_stream;
2739                 gint fd;
2740
2741                 fd = g_open (helper->file_path, O_RDONLY, 0644);
2742                 if (fd != -1) {
2743                         TnyMsg *top_msg;
2744                         file_stream = tny_fs_stream_new (fd);
2745
2746                         mgr = modest_runtime_get_window_mgr ();
2747
2748                         account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2749                         mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2750
2751                         if (!account)
2752                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2753
2754                         msg = tny_camel_msg_new ();
2755                         tny_camel_msg_parse (msg, file_stream);
2756
2757                         if (priv->top_msg)
2758                                 top_msg = g_object_ref (priv->top_msg);
2759                         else
2760                                 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2761
2762                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg, 
2763                                                                              account, mailbox, helper->attachment_uid);
2764                         if (top_msg) g_object_unref (top_msg);
2765                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2766                                                 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2767                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2768                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2769                         else
2770                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2771                         g_object_unref (msg);
2772                         g_object_unref (file_stream);
2773                 } else {
2774                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2775                 }
2776
2777         } else {
2778
2779                 /* make the file read-only */
2780                 g_chmod(helper->file_path, 0444);
2781
2782                 /* Activate the file */
2783                 modest_platform_activate_file (helper->file_path, content_type);
2784         }
2785
2786  free:
2787         /* Frees */
2788         g_object_unref (helper->self);
2789         g_free (helper->file_path);
2790         g_free (helper->attachment_uid);
2791         g_slice_free (DecodeAsyncHelper, helper);
2792 }
2793
2794 static void
2795 view_attachment_connect_handler (gboolean canceled,
2796                                  GError *err,
2797                                  GtkWindow *parent_window,
2798                                  TnyAccount *account,
2799                                  TnyMimePart *part)
2800 {
2801
2802         if (canceled || err) {
2803                 g_object_unref (part);
2804                 return;
2805         }
2806
2807         modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2808                                                 part);
2809         g_object_unref (part);
2810 }
2811
2812 void
2813 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2814                                         TnyMimePart *mime_part)
2815 {
2816         ModestMsgViewWindowPrivate *priv;
2817         const gchar *msg_uid;
2818         gchar *attachment_uid = NULL;
2819         gint attachment_index = 0;
2820         TnyList *attachments;
2821
2822         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2823         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2824         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2825
2826         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2827         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2828         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2829         g_object_unref (attachments);
2830
2831         if (msg_uid && attachment_index >= 0) {
2832                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2833         }
2834
2835         if (mime_part == NULL) {
2836                 gboolean error = FALSE;
2837                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2838                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2839                         error = TRUE;
2840                 } else if (tny_list_get_length (selected_attachments) > 1) {
2841                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2842                         error = TRUE;
2843                 } else {
2844                         TnyIterator *iter;
2845                         iter = tny_list_create_iterator (selected_attachments);
2846                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2847                         g_object_unref (iter);
2848                 }
2849                 if (selected_attachments)
2850                         g_object_unref (selected_attachments);
2851
2852                 if (error)
2853                         goto frees;
2854         } else {
2855                 g_object_ref (mime_part);
2856         }
2857
2858         if (tny_mime_part_is_purged (mime_part))
2859                 goto frees;
2860
2861         if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2862             !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2863                 gboolean is_merge;
2864                 TnyAccount *account;
2865
2866                 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2867                 account = NULL;
2868                 /* Get the account */
2869                 if (!is_merge)
2870                         account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2871                                                                   priv->msg_uid);
2872
2873                 if (!tny_device_is_online (modest_runtime_get_device())) {
2874                         modest_platform_connect_and_perform (GTK_WINDOW (window),
2875                                                              TRUE,
2876                                                              TNY_ACCOUNT (account),
2877                                                              (ModestConnectedPerformer) view_attachment_connect_handler,
2878                                                              g_object_ref (mime_part));
2879                         goto frees;
2880                 }
2881         }
2882
2883         if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2884                 gchar *filepath = NULL;
2885                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2886                 gboolean show_error_banner = FALSE;
2887                 TnyFsStream *temp_stream = NULL;
2888                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2889                                                                &filepath);
2890
2891                 if (temp_stream != NULL) {
2892                         ModestAccountMgr *mgr;
2893                         DecodeAsyncHelper *helper;
2894                         gboolean decode_in_provider;
2895                         ModestProtocol *protocol;
2896                         const gchar *account; 
2897
2898                         /* Activate progress hint */
2899                         set_progress_hint (window, TRUE);
2900
2901                         helper = g_slice_new0 (DecodeAsyncHelper);
2902                         helper->self = g_object_ref (window);
2903                         helper->file_path = g_strdup (filepath);
2904                         helper->attachment_uid = g_strdup (attachment_uid);
2905
2906                         decode_in_provider = FALSE;
2907                         mgr = modest_runtime_get_account_mgr ();
2908                         account = modest_window_get_active_account (MODEST_WINDOW (window));
2909                         if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2910                                 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2911                                         gchar *uri;
2912                                         uri = g_strconcat ("file://", filepath, NULL);
2913                                         decode_in_provider = 
2914                                                 modest_account_protocol_decode_part_to_stream_async (
2915                                                         MODEST_ACCOUNT_PROTOCOL (protocol),
2916                                                         mime_part,
2917                                                         filepath,
2918                                                         TNY_STREAM (temp_stream),
2919                                                         on_decode_to_stream_async_handler,
2920                                                         NULL,
2921                                                         helper);
2922                                         g_free (uri);
2923                                 }
2924                         }
2925
2926                         if (!decode_in_provider)
2927                                 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2928                                                                       on_decode_to_stream_async_handler,
2929                                                                       NULL,
2930                                                                       helper);
2931                         g_object_unref (temp_stream);
2932                         /* NOTE: files in the temporary area will be automatically
2933                          * cleaned after some time if they are no longer in use */
2934                 } else {
2935                         if (filepath) {
2936                                 const gchar *content_type;
2937                                 /* the file may already exist but it isn't writable,
2938                                  * let's try to open it anyway */
2939                                 content_type = tny_mime_part_get_content_type (mime_part);
2940                                 modest_platform_activate_file (filepath, content_type);
2941                         } else {
2942                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2943                                 show_error_banner = TRUE;
2944                         }
2945                 }
2946                 if (filepath)
2947                         g_free (filepath);
2948                 if (show_error_banner)
2949                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2950         } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2951                 ModestWindowMgr *mgr;
2952                 ModestWindow *msg_win = NULL;
2953                 TnyMsg *current_msg;
2954                 gboolean found;
2955                 TnyHeader *header;
2956
2957                 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2958                 mgr = modest_runtime_get_window_mgr ();
2959                 header = tny_msg_get_header (TNY_MSG (current_msg));
2960                 found = modest_window_mgr_find_registered_message_uid (mgr,
2961                                                                        attachment_uid,
2962                                                                        &msg_win);
2963                 
2964                 if (found) {
2965                         g_debug ("window for this body is already being created");
2966                 } else {
2967                         TnyMsg *top_msg;
2968
2969                         /* it's not found, so create a new window for it */
2970                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2971                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2972                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2973                         if (!account)
2974                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2975
2976                         if (priv->top_msg)
2977                                 top_msg = g_object_ref (priv->top_msg);
2978                         else
2979                                 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2980                         
2981                         msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
2982                                                                               account, mailbox, attachment_uid);
2983
2984                         if (top_msg) g_object_unref (top_msg);
2985                         
2986                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2987                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2988                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2989                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
2990                         else
2991                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
2992                 }
2993                 g_object_unref (current_msg);           
2994         } else {
2995                 /* message attachment */
2996                 TnyHeader *header = NULL;
2997                 ModestWindowMgr *mgr;
2998                 ModestWindow *msg_win = NULL;
2999                 gboolean found;
3000
3001                 header = tny_msg_get_header (TNY_MSG (mime_part));
3002                 mgr = modest_runtime_get_window_mgr ();
3003                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
3004
3005                 if (found) {
3006                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
3007                          * thus, we don't do anything */
3008                         g_debug ("window for is already being created");
3009                 } else {
3010                         TnyMsg *top_msg;
3011                         /* it's not found, so create a new window for it */
3012                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
3013                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
3014                         const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
3015                         if (!account)
3016                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
3017                         if (priv->top_msg)
3018                                 top_msg = g_object_ref (priv->top_msg);
3019                         else
3020                                 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3021                         msg_win = modest_msg_view_window_new_for_attachment (
3022                                 TNY_MSG (mime_part), top_msg, account, 
3023                                 mailbox, attachment_uid);
3024                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
3025                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
3026                         if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3027                                 gtk_widget_show_all (GTK_WIDGET (msg_win));
3028                         else
3029                                 gtk_widget_destroy (GTK_WIDGET (msg_win));
3030                 }
3031         }
3032
3033  frees:
3034         if (attachment_uid)
3035                 g_free (attachment_uid);
3036         if (mime_part)
3037                 g_object_unref (mime_part);
3038 }
3039
3040 typedef struct
3041 {
3042         gchar *filename;
3043         TnyMimePart *part;
3044 } SaveMimePartPair;
3045
3046 typedef struct
3047 {
3048         GList *pairs;
3049         GnomeVFSResult result;
3050         gchar *uri;
3051         ModestMsgViewWindow *window;
3052 } SaveMimePartInfo;
3053
3054 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3055 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3056 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3057 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3058
3059 static void
3060 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3061 {
3062         GList *node;
3063
3064         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3065                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3066                 g_free (pair->filename);
3067                 g_object_unref (pair->part);
3068                 g_slice_free (SaveMimePartPair, pair);
3069         }
3070         g_list_free (info->pairs);
3071         info->pairs = NULL;
3072         g_free (info->uri);
3073         g_object_unref (info->window);
3074         info->window = NULL;
3075         if (with_struct) {
3076                 g_slice_free (SaveMimePartInfo, info);
3077         }
3078 }
3079
3080 static gboolean
3081 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3082 {
3083         /* This is a GDK lock because we are an idle callback and
3084          * hildon_banner_show_information is or does Gtk+ code */
3085
3086         gdk_threads_enter (); /* CHECKED */
3087         if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3088                 /* nothing */
3089         } else if (info->result == GNOME_VFS_OK) {
3090                 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3091         } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3092                 gchar *msg = NULL;
3093
3094                 /* Check if the uri belongs to the external mmc */
3095                 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3096                         msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3097                 else
3098                         msg = g_strdup (_KR("cerm_memory_card_full"));
3099                 modest_platform_information_banner (NULL, NULL, msg);
3100                 g_free (msg);
3101         } else {
3102                 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3103         }
3104         set_progress_hint (info->window, FALSE);
3105         save_mime_part_info_free (info, FALSE);
3106         gdk_threads_leave (); /* CHECKED */
3107
3108         return FALSE;
3109 }
3110
3111 static void
3112 save_mime_part_to_file_connect_handler (gboolean canceled,
3113                                         GError *err,
3114                                         GtkWindow *parent_window,
3115                                         TnyAccount *account,
3116                                         SaveMimePartInfo *info)
3117 {
3118         if (canceled || err) {
3119                 if (canceled && !err) {
3120                         info->result = GNOME_VFS_ERROR_CANCELLED;
3121                 }
3122                 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3123         } else {
3124                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3125         }
3126 }
3127
3128 static gboolean
3129 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3130 {
3131         gboolean is_merge;
3132         TnyAccount *account;
3133         ModestMsgViewWindowPrivate *priv;
3134
3135         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3136
3137         is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3138         account = NULL;
3139
3140         /* Get the account */
3141         if (!is_merge)
3142                 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3143                                                           priv->msg_uid);
3144
3145         modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3146                                              TRUE,
3147                                              TNY_ACCOUNT (account),
3148                                              (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3149                                              info);
3150
3151         if (account)
3152                 g_object_unref (account);
3153
3154         return FALSE;
3155 }
3156
3157 static gpointer
3158 save_mime_part_to_file (SaveMimePartInfo *info)
3159 {
3160         GnomeVFSHandle *handle;
3161         TnyStream *stream;
3162         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3163
3164         if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3165             !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3166                 gboolean check_online = TRUE;
3167                 ModestMsgViewWindowPrivate *priv = NULL;
3168
3169                 /* Check if we really need to connect to save the mime part */
3170                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3171                 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3172                         check_online = FALSE;
3173                 } else {
3174                         TnyAccountStore *acc_store;
3175                         TnyAccount *account = NULL;
3176
3177                         acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3178                         account = tny_account_store_find_account (acc_store, priv->msg_uid);
3179
3180                         if (account) {
3181                                 if (tny_account_get_connection_status (account) ==
3182                                     TNY_CONNECTION_STATUS_CONNECTED)
3183                                         check_online = FALSE;
3184                                 g_object_unref (account);
3185                         } else {
3186                                 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3187                         }
3188                 }
3189
3190                 if (check_online) {
3191                         g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3192                         return NULL;
3193                 }
3194         }
3195
3196         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3197         if (info->result == GNOME_VFS_OK) {
3198                 GError *error = NULL;
3199                 gboolean decode_in_provider;
3200                 gssize written;
3201                 ModestAccountMgr *mgr;
3202                 const gchar *account;
3203                 ModestProtocol *protocol = NULL;
3204
3205                 stream = tny_vfs_stream_new (handle);
3206
3207                 decode_in_provider = FALSE;
3208                 mgr = modest_runtime_get_account_mgr ();
3209                 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3210                 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3211                         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3212                                 decode_in_provider = 
3213                                         modest_account_protocol_decode_part_to_stream (
3214                                                 MODEST_ACCOUNT_PROTOCOL (protocol),
3215                                                 pair->part,
3216                                                 pair->filename,
3217                                                 stream,
3218                                                 &written,
3219                                                 &error);
3220                         }
3221                 }
3222                 if (!decode_in_provider)
3223                         written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3224
3225                 if (written < 0) {
3226                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3227
3228                         if (error && (error->domain == TNY_ERROR_DOMAIN) &&
3229                             (error->code == TNY_IO_ERROR_WRITE) &&
3230                             (errno == ENOSPC)) {
3231                                 info->result = GNOME_VFS_ERROR_NO_SPACE;
3232                         } else {
3233                                 info->result = GNOME_VFS_ERROR_IO;
3234                         }
3235                 }
3236                 g_object_unref (G_OBJECT (stream));
3237         } else {
3238                 g_warning ("Could not create save attachment %s: %s\n", 
3239                            pair->filename, gnome_vfs_result_to_string (info->result));
3240         }
3241
3242         /* Go on saving remaining files */
3243         info->pairs = g_list_remove_link (info->pairs, info->pairs);
3244         if (info->pairs != NULL) {
3245                 save_mime_part_to_file (info);
3246         } else {
3247                 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3248         }
3249
3250         return NULL;
3251 }
3252
3253 static void
3254 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3255                                      SaveMimePartInfo *info)
3256 {
3257         gboolean is_ok = TRUE;
3258         gint replaced_files = 0;
3259         const GList *files = info->pairs;
3260         const GList *iter, *to_replace = NULL;
3261
3262         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3263                 SaveMimePartPair *pair = iter->data;
3264                 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3265
3266                 if (modest_utils_file_exists (unescaped)) {
3267                         replaced_files++;
3268                         if (replaced_files == 1)
3269                                 to_replace = iter;
3270                 }
3271                 g_free (unescaped);
3272         }
3273         if (replaced_files) {
3274                 gint response;
3275
3276                 if (replaced_files == 1) {
3277                         SaveMimePartPair *pair = to_replace->data;
3278                         const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3279                         gchar *escaped_basename, *message;
3280
3281                         escaped_basename = g_uri_unescape_string (basename, NULL);
3282                         message = g_strdup_printf ("%s\n%s",
3283                                                    _FM("docm_nc_replace_file"),
3284                                                    (escaped_basename) ? escaped_basename : "");
3285                         response = modest_platform_run_confirmation_dialog (parent, message);
3286                         g_free (message);
3287                         g_free (escaped_basename);
3288                 } else {
3289                         response = modest_platform_run_confirmation_dialog (parent,
3290                                                                             _FM("docm_nc_replace_multiple"));
3291                 }
3292                 if (response != GTK_RESPONSE_OK)
3293                         is_ok = FALSE;
3294         }
3295
3296         if (!is_ok) {
3297                 save_mime_part_info_free (info, TRUE);
3298         } else {
3299                 /* Start progress and launch thread */
3300                 set_progress_hint (info->window, TRUE);
3301                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3302         }
3303
3304 }
3305
3306 typedef struct _SaveAttachmentsInfo {
3307         TnyList *attachments_list;
3308         ModestMsgViewWindow *window;
3309 } SaveAttachmentsInfo;
3310
3311 static void
3312 save_attachments_response (GtkDialog *dialog,
3313                            gint       arg1,
3314                            gpointer   user_data)  
3315 {
3316         TnyList *mime_parts;
3317         gchar *chooser_uri;
3318         GList *files_to_save = NULL;
3319         gchar *current_folder;
3320         SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3321
3322         mime_parts = TNY_LIST (sa_info->attachments_list);
3323
3324         if (arg1 != GTK_RESPONSE_OK)
3325                 goto end;
3326
3327         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3328         current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3329         if (current_folder && *current_folder != '\0') {
3330                 GError *err = NULL;
3331                 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3332                                         current_folder,&err);
3333                 if (err != NULL) {
3334                         g_debug ("Error storing latest used folder: %s", err->message);
3335                         g_error_free (err);
3336                 }
3337         }
3338         g_free (current_folder);
3339
3340         if (!modest_utils_folder_writable (chooser_uri)) {
3341                 const gchar *err_msg;
3342
3343 #ifdef MODEST_PLATFORM_MAEMO
3344                 if (modest_maemo_utils_in_usb_mode ()) {
3345                         err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3346                 } else {
3347                         err_msg = _FM("sfil_ib_readonly_location");
3348                 }
3349 #else
3350                 err_msg = _FM("sfil_ib_readonly_location");
3351 #endif
3352                 hildon_banner_show_information (NULL, NULL, err_msg);
3353         } else {
3354                 TnyIterator *iter;
3355
3356                 iter = tny_list_create_iterator (mime_parts);
3357                 while (!tny_iterator_is_done (iter)) {
3358                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3359
3360                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3361                             !tny_mime_part_is_purged (mime_part) &&
3362                             (tny_mime_part_get_filename (mime_part) != NULL)) {
3363                                 SaveMimePartPair *pair;
3364
3365                                 pair = g_slice_new0 (SaveMimePartPair);
3366
3367                                 if (tny_list_get_length (mime_parts) > 1) {
3368                                         gchar *escaped = 
3369                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3370                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3371                                         g_free (escaped);
3372                                 } else {
3373                                         pair->filename = g_strdup (chooser_uri);
3374                                 }
3375                                 pair->part = mime_part;
3376                                 files_to_save = g_list_prepend (files_to_save, pair);
3377                         }
3378                         tny_iterator_next (iter);
3379                 }
3380                 g_object_unref (iter);
3381         }
3382
3383         if (files_to_save != NULL) {
3384                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3385                 info->pairs = files_to_save;
3386                 info->result = TRUE;
3387                 info->uri = g_strdup (chooser_uri);
3388                 info->window = g_object_ref (sa_info->window);
3389                 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3390         }
3391         g_free (chooser_uri);
3392
3393  end:
3394         /* Free and close the dialog */
3395         g_object_unref (mime_parts);
3396         g_object_unref (sa_info->window);
3397         g_slice_free (SaveAttachmentsInfo, sa_info);
3398         gtk_widget_destroy (GTK_WIDGET (dialog));
3399 }
3400
3401 static gboolean
3402 msg_is_attachment (TnyList *mime_parts)
3403 {
3404         TnyIterator *iter;
3405         gboolean retval = FALSE;
3406
3407         if (tny_list_get_length (mime_parts) > 1)
3408                 return FALSE;
3409
3410         iter = tny_list_create_iterator (mime_parts);
3411         if (iter) {
3412                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3413                 if (part) {
3414                         if (TNY_IS_MSG (part))
3415                                 retval = TRUE;
3416                         g_object_unref (part);
3417                 }
3418                 g_object_unref (iter);
3419         }
3420         return retval;
3421 }
3422
3423 void
3424 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, 
3425                                          TnyList *mime_parts)
3426 {
3427         ModestMsgViewWindowPrivate *priv;
3428         GtkWidget *save_dialog = NULL;
3429         gchar *conf_folder = NULL;
3430         gchar *filename = NULL;
3431         gchar *save_multiple_str = NULL;
3432         const gchar *root_folder = "file:///";
3433
3434         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3435         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3436
3437         if (mime_parts == NULL) {
3438                 gboolean allow_msgs = FALSE;
3439
3440                 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3441                  * selection available */
3442                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3443
3444                 /* Check if the message is composed by an unique MIME
3445                    part whose content disposition is attachment. There
3446                    could be messages like this:
3447
3448                    Date: Tue, 12 Jan 2010 20:40:59 +0000
3449                    From: <sender@example.org>
3450                    To: <recipient@example.org>
3451                    Subject: no body
3452                    Content-Type: image/jpeg
3453                    Content-Disposition: attachment; filename="bug7718.jpeg"
3454
3455                    whose unique MIME part is the message itself whose
3456                    content disposition is attachment
3457                 */
3458                 if (mime_parts && msg_is_attachment (mime_parts))
3459                         allow_msgs = TRUE;
3460
3461                 if (mime_parts &&
3462                     !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, allow_msgs)) {
3463                         g_object_unref (mime_parts);
3464                         return;
3465                 }
3466                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3467                         if (mime_parts) {
3468                                 g_object_unref (mime_parts);
3469                                 mime_parts = NULL;
3470                         }
3471                         return;
3472                 }
3473         } else {
3474                 g_object_ref (mime_parts);
3475         }
3476
3477         /* prepare dialog */
3478         if (tny_list_get_length (mime_parts) == 1) {
3479                 TnyIterator *iter;
3480                 /* only one attachment selected */
3481                 iter = tny_list_create_iterator (mime_parts);
3482                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3483                 g_object_unref (iter);
3484                 if (!modest_tny_mime_part_is_msg (mime_part) && 
3485                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3486                     !tny_mime_part_is_purged (mime_part)) {
3487                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
3488                 } else {
3489                         /* TODO: show any error? */
3490                         g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3491                         g_object_unref (mime_parts);
3492                         return;
3493                 }
3494                 g_object_unref (mime_part);
3495         } else {
3496                 gint num = tny_list_get_length (mime_parts);
3497                 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3498                                                                "sfil_va_number_of_objects_attachment",
3499                                                               "sfil_va_number_of_objects_attachments",
3500                                                               num), num);
3501         }
3502
3503         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
3504                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
3505
3506         /* Get last used folder */
3507         conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3508                                               MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3509
3510         /* File chooser stops working if we select "file:///" as current folder */
3511         if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3512                 g_free (conf_folder);
3513                 conf_folder = NULL;
3514         }
3515
3516         if (conf_folder && conf_folder[0] != '\0') {
3517                 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3518         } else {
3519                 gchar *docs_folder;
3520                 /* Set the default folder to documents folder */
3521                 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3522                 if (!docs_folder) {
3523                         /* fallback */
3524                         docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3525                 }
3526                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3527                 g_free (docs_folder);
3528         }
3529         g_free (conf_folder);
3530
3531         /* set filename */
3532         if (filename) {
3533                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
3534                                                    filename);
3535                 g_free (filename);
3536         }
3537
3538         /* if multiple, set multiple string */
3539         if (save_multiple_str) {
3540                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3541                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3542                 g_free (save_multiple_str);
3543         }
3544
3545         /* We must run this asynchronously, because the hildon dialog
3546            performs a gtk_dialog_run by itself which leads to gdk
3547            deadlocks */
3548         SaveAttachmentsInfo *sa_info;
3549         sa_info = g_slice_new (SaveAttachmentsInfo);
3550         sa_info->attachments_list = mime_parts;
3551         sa_info->window = g_object_ref (window);
3552         g_signal_connect (save_dialog, "response", 
3553                           G_CALLBACK (save_attachments_response), sa_info);
3554
3555         gtk_widget_show_all (save_dialog);
3556 }
3557
3558 static gboolean
3559 show_remove_attachment_information (gpointer userdata)
3560 {
3561         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3562         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3563
3564         /* We're outside the main lock */
3565         gdk_threads_enter ();
3566
3567         if (priv->remove_attachment_banner != NULL) {
3568                 gtk_widget_destroy (priv->remove_attachment_banner);
3569                 g_object_unref (priv->remove_attachment_banner);
3570         }
3571
3572         priv->remove_attachment_banner = g_object_ref (
3573                 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3574
3575         gdk_threads_leave ();
3576
3577         return FALSE;
3578 }
3579
3580 void
3581 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3582 {
3583         ModestMsgViewWindowPrivate *priv;
3584         TnyList *mime_parts = NULL, *tmp;
3585         gchar *confirmation_message;
3586         gint response;
3587         gint n_attachments;
3588         TnyMsg *msg;
3589         TnyIterator *iter;
3590
3591         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3592         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3593
3594         /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3595          * because we don't have selection
3596          */
3597         mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3598
3599         /* Remove already purged messages from mime parts list. We use
3600            a copy of the list to remove items in the original one */
3601         tmp = tny_list_copy (mime_parts);
3602         iter = tny_list_create_iterator (tmp);
3603         while (!tny_iterator_is_done (iter)) {
3604                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3605                 if (tny_mime_part_is_purged (part))
3606                         tny_list_remove (mime_parts, (GObject *) part);
3607
3608                 g_object_unref (part);
3609                 tny_iterator_next (iter);
3610         }
3611         g_object_unref (tmp);
3612         g_object_unref (iter);
3613
3614         if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3615             tny_list_get_length (mime_parts) == 0) {
3616                 g_object_unref (mime_parts);
3617                 return;
3618         }
3619
3620         n_attachments = tny_list_get_length (mime_parts);
3621         if (n_attachments == 1) {
3622                 gchar *filename;
3623                 TnyMimePart *part;
3624
3625                 iter = tny_list_create_iterator (mime_parts);
3626                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3627                 g_object_unref (iter);
3628                 if (modest_tny_mime_part_is_msg (part)) {
3629                         TnyHeader *header;
3630                         header = tny_msg_get_header (TNY_MSG (part));
3631                         filename = tny_header_dup_subject (header);
3632                         g_object_unref (header);
3633                         if (filename == NULL)
3634                                 filename = g_strdup (_("mail_va_no_subject"));
3635                 } else {
3636                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3637                 }
3638                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3639                 g_free (filename);
3640                 g_object_unref (part);
3641         } else {
3642                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
3643                                                                  "mcen_nc_purge_files_text", 
3644                                                                  n_attachments), n_attachments);
3645         }
3646         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3647                                                             confirmation_message);
3648         g_free (confirmation_message);
3649
3650         if (response != GTK_RESPONSE_OK) {
3651                 g_object_unref (mime_parts);
3652                 return;
3653         }
3654
3655         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3656
3657         iter = tny_list_create_iterator (mime_parts);
3658         while (!tny_iterator_is_done (iter)) {
3659                 TnyMimePart *part;
3660
3661                 part = (TnyMimePart *) tny_iterator_get_current (iter);
3662                 tny_mime_part_set_purged (TNY_MIME_PART (part));
3663                 g_object_unref (part);
3664                 tny_iterator_next (iter);
3665         }
3666         g_object_unref (iter);
3667
3668         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3669         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3670         tny_msg_rewrite_cache (msg);
3671         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3672         g_object_unref (msg);
3673         update_branding (MODEST_MSG_VIEW_WINDOW (window));
3674
3675         g_object_unref (mime_parts);
3676
3677         if (priv->purge_timeout > 0) {
3678                 g_source_remove (priv->purge_timeout);
3679                 priv->purge_timeout = 0;
3680         }
3681
3682         if (priv->remove_attachment_banner) {
3683                 gtk_widget_destroy (priv->remove_attachment_banner);
3684                 g_object_unref (priv->remove_attachment_banner);
3685                 priv->remove_attachment_banner = NULL;
3686         }
3687 }
3688
3689
3690 static void
3691 update_window_title (ModestMsgViewWindow *window)
3692 {
3693         ModestMsgViewWindowPrivate *priv;
3694         TnyMsg *msg = NULL;
3695         TnyHeader *header = NULL;
3696         gchar *subject = NULL;
3697
3698         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3699
3700         /* Note that if the window is closed while we're retrieving
3701            the message, this widget could de deleted */
3702         if (!priv->msg_view)
3703                 return;
3704
3705         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3706
3707         if (priv->other_body) {
3708                 gchar *description;
3709
3710                 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3711                 if (description) {
3712                         g_strstrip (description);
3713                         subject = description;
3714                 }
3715         } else if (msg != NULL) {
3716                 header = tny_msg_get_header (msg);
3717                 subject = tny_header_dup_subject (header);
3718                 g_object_unref (header);
3719                 g_object_unref (msg);
3720         }
3721
3722         if ((subject == NULL)||(subject[0] == '\0')) {
3723                 g_free (subject);
3724                 subject = g_strdup (_("mail_va_no_subject"));
3725         }
3726
3727         gtk_window_set_title (GTK_WINDOW (window), subject);
3728 }
3729
3730
3731 static void
3732 on_move_focus (GtkWidget *widget,
3733                GtkDirectionType direction,
3734                gpointer userdata)
3735 {
3736         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3737 }
3738
3739 static TnyStream *
3740 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3741 {
3742         GnomeVFSResult result;
3743         GnomeVFSHandle *handle = NULL;
3744         GnomeVFSFileInfo *info = NULL;
3745         TnyStream *stream;
3746
3747         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3748         if (result != GNOME_VFS_OK) {
3749                 *expected_size = 0;
3750                 return NULL;
3751         }
3752         
3753         info = gnome_vfs_file_info_new ();
3754         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3755         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3756                 /* We put a "safe" default size for going to cache */
3757                 *expected_size = (300*1024);
3758         } else {
3759                 *expected_size = info->size;
3760         }
3761         gnome_vfs_file_info_unref (info);
3762
3763         stream = tny_vfs_stream_new (handle);
3764
3765         return stream;
3766
3767 }
3768
3769 typedef struct {
3770         gchar *uri;
3771         gchar *cache_id;
3772         TnyStream *output_stream;
3773         GtkWidget *msg_view;
3774         GtkWidget *window;
3775 } FetchImageData;
3776
3777 gboolean
3778 on_fetch_image_timeout_refresh_view (gpointer userdata)
3779 {
3780         ModestMsgViewWindowPrivate *priv;
3781
3782         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3783         update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3784         /* Note that priv->msg_view is set to NULL when this window is
3785            distroyed */
3786         if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3787                 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3788         }
3789         priv->fetch_image_redraw_handler = 0;
3790         g_object_unref (userdata);
3791         return FALSE;
3792 }
3793
3794 gboolean
3795 on_fetch_image_idle_refresh_view (gpointer userdata)
3796 {
3797
3798         FetchImageData *fidata = (FetchImageData *) userdata;
3799
3800         gdk_threads_enter ();
3801         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3802                 ModestMsgViewWindowPrivate *priv;
3803
3804                 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3805                 priv->fetching_images--;
3806                 if (priv->fetch_image_redraw_handler == 0) {
3807                         priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3808                 }
3809
3810         }
3811         gdk_threads_leave ();
3812
3813         g_object_unref (fidata->msg_view);
3814         g_object_unref (fidata->window);
3815         g_slice_free (FetchImageData, fidata);
3816         return FALSE;
3817 }
3818
3819 static gpointer
3820 on_fetch_image_thread (gpointer userdata)
3821 {
3822         FetchImageData *fidata = (FetchImageData *) userdata;
3823         TnyStreamCache *cache;
3824         TnyStream *cache_stream;
3825
3826         cache = modest_runtime_get_images_cache ();
3827         cache_stream = 
3828                 tny_stream_cache_get_stream (cache, 
3829                                              fidata->cache_id, 
3830                                              (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, 
3831                                              (gpointer) fidata->uri);
3832         g_free (fidata->cache_id);
3833         g_free (fidata->uri);
3834
3835         if (cache_stream != NULL) {
3836                 char buffer[4096];
3837
3838                 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3839                         gssize nb_read;
3840
3841                         nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3842                         if (G_UNLIKELY (nb_read < 0)) {
3843                                 break;
3844                         } else if (G_LIKELY (nb_read > 0)) {
3845                                 gssize nb_written = 0;
3846
3847                                 while (G_UNLIKELY (nb_written < nb_read)) {
3848                                         gssize len;
3849
3850                                         len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3851                                                                 nb_read - nb_written);
3852                                         if (G_UNLIKELY (len < 0))
3853                                                 break;
3854                                         nb_written += len;
3855                                 }
3856                         }
3857                 }
3858                 tny_stream_close (cache_stream);
3859                 g_object_unref (cache_stream);
3860         }
3861
3862         tny_stream_close (fidata->output_stream);
3863         g_object_unref (fidata->output_stream);
3864
3865         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3866
3867         return NULL;
3868 }
3869
3870 static gboolean
3871 on_fetch_image (ModestMsgView *msgview,
3872                 const gchar *uri,
3873                 TnyStream *stream,
3874                 ModestMsgViewWindow *window)
3875 {
3876         const gchar *current_account;
3877         ModestMsgViewWindowPrivate *priv;
3878         FetchImageData *fidata;
3879
3880         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3881
3882         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3883
3884         fidata = g_slice_new0 (FetchImageData);
3885         fidata->msg_view = g_object_ref (msgview);
3886         fidata->window = g_object_ref (window);
3887         fidata->uri = g_strdup (uri);
3888         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3889         fidata->output_stream = g_object_ref (stream);
3890
3891         priv->fetching_images++;
3892         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3893                 g_object_unref (fidata->output_stream);
3894                 g_free (fidata->cache_id);
3895                 g_free (fidata->uri);
3896                 g_object_unref (fidata->msg_view);
3897                 g_slice_free (FetchImageData, fidata);
3898                 tny_stream_close (stream);
3899                 priv->fetching_images--;
3900                 update_progress_hint (window);
3901                 return FALSE;
3902         }
3903         update_progress_hint (window);
3904
3905         return TRUE;
3906 }
3907
3908 static void 
3909 setup_menu (ModestMsgViewWindow *self)
3910 {
3911         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3912
3913         /* Settings menu buttons */
3914         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3915                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3916                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3917
3918         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3919                                            dngettext(GETTEXT_PACKAGE,
3920                                                      "mcen_me_move_message",
3921                                                      "mcen_me_move_messages",
3922                                                      1),
3923                                            NULL,
3924                                            APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3925                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3926
3927         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3928                                            APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3929                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3930
3931         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3932                                            APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3933                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3934
3935         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3936                                            APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3937                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3938         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3939                                            APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3940                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3941
3942         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3943                                            APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3944                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3945
3946         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3947                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
3948                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3949 }
3950
3951 void
3952 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3953 {
3954         ModestMsgViewWindowPrivate *priv;
3955         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3956         GSList *recipients = NULL;
3957         TnyMsg *msg = NULL;
3958
3959         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3960         if (msg == NULL) {
3961                 TnyHeader *header;
3962
3963                 header = modest_msg_view_window_get_header (self);
3964                 if (header == NULL)
3965                         return;
3966                 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3967                 g_object_unref (header);
3968         } else {
3969                 recipients = modest_tny_msg_get_all_recipients_list (msg);
3970                 g_object_unref (msg);
3971         }
3972
3973         if (recipients) {
3974                 /* Offer the user to add recipients to the address book */
3975                 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3976                 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3977         }
3978 }
3979
3980 static gboolean 
3981 _modest_msg_view_window_map_event (GtkWidget *widget,
3982                                    GdkEvent *event,
3983                                    gpointer userdata)
3984 {
3985         ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3986
3987         update_progress_hint (self);
3988
3989         return FALSE;
3990 }
3991
3992 void
3993 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3994 {
3995         ModestMsgViewWindowPrivate *priv;
3996         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3997
3998         modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3999 }
4000
4001 gboolean 
4002 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
4003 {
4004         ModestMsgViewWindowPrivate *priv;
4005         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4006
4007         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
4008
4009         return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
4010 }
4011
4012 void 
4013 modest_msg_view_window_reload (ModestMsgViewWindow *self)
4014 {
4015         ModestMsgViewWindowPrivate *priv;
4016         const gchar *msg_uid;
4017         TnyHeader *header = NULL;
4018         TnyFolder *folder = NULL;
4019
4020         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
4021
4022         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4023
4024         header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
4025         if (!header)
4026                 return;
4027
4028         folder = tny_header_get_folder (header);
4029         g_object_unref (header);
4030
4031         if (!folder)
4032                 return;
4033
4034         msg_uid = modest_msg_view_window_get_message_uid (self);
4035         if (msg_uid) {
4036                 GtkTreeRowReference *row_reference;
4037
4038                 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
4039                         row_reference = priv->row_reference;
4040                 } else {
4041                         row_reference = NULL;
4042                 }
4043                 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
4044                         g_warning ("Shouldn't happen, trying to reload a message failed");
4045         }
4046
4047         g_object_unref (folder);
4048 }
4049
4050 static void
4051 update_branding (ModestMsgViewWindow *self)
4052 {
4053         const gchar *account; 
4054         const gchar *mailbox;
4055         ModestAccountMgr *mgr;
4056         ModestProtocol *protocol = NULL;
4057         gchar *service_name = NULL;
4058         const GdkPixbuf *service_icon = NULL;
4059         ModestMsgViewWindowPrivate *priv;
4060
4061         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4062
4063         account = modest_window_get_active_account (MODEST_WINDOW (self));
4064         mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
4065
4066         mgr = modest_runtime_get_account_mgr ();
4067
4068         if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
4069                 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4070                         service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4071                                                                                  account, mailbox);
4072                         service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4073                                                                                  account, mailbox, MODEST_ICON_SIZE_SMALL);
4074                 }
4075         }
4076
4077         modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4078         g_free (service_name);
4079 }
4080
4081 static void
4082 sync_flags (ModestMsgViewWindow *self)
4083 {
4084         TnyHeader *header = NULL;
4085
4086         header = modest_msg_view_window_get_header (self);
4087         if (!header) {
4088                 TnyMsg *msg = modest_msg_view_window_get_message (self);
4089                 if (msg) {
4090                         header = tny_msg_get_header (msg);
4091                         g_object_unref (msg);
4092                 }
4093         }
4094
4095         if (header) {
4096                 TnyFolder *folder = tny_header_get_folder (header);
4097
4098                 if (folder) {
4099                         ModestMailOperation *mail_op;
4100
4101                         /* Sync folder, we need this to save the seen flag */
4102                         mail_op = modest_mail_operation_new (NULL);
4103                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4104                                                          mail_op);
4105                         modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4106                         g_object_unref (mail_op);
4107                         g_object_unref (folder);
4108                 }
4109                 g_object_unref (header);
4110         }
4111 }
4112
4113 static gboolean
4114 on_realize (GtkWidget *widget,
4115             gpointer userdata)
4116 {
4117         GdkDisplay *display;
4118         Atom atom;
4119         unsigned long val = 1;
4120
4121         display = gdk_drawable_get_display (widget->window);
4122         atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
4123         XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
4124                          GDK_WINDOW_XID (widget->window), atom,
4125                          XA_INTEGER, 32, PropModeReplace,
4126                          (unsigned char *) &val, 1);
4127
4128         return FALSE;
4129 }
4130
4131 static gboolean
4132 on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part, GtkContainer *container, ModestMsgViewWindow *self)
4133 {
4134         const gchar *account_name;
4135         ModestProtocolType proto_type;
4136         ModestProtocol *protocol;
4137         gboolean retval = FALSE;
4138
4139         account_name = modest_window_get_active_account (MODEST_WINDOW (self));
4140         
4141         /* Get proto */
4142         proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (), 
4143                                                             account_name);
4144         protocol = 
4145                 modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (), 
4146                                                                proto_type);
4147
4148         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4149                 retval = modest_account_protocol_handle_calendar (MODEST_ACCOUNT_PROTOCOL (protocol), MODEST_WINDOW (self),
4150                                                                   calendar_part, container);
4151         }
4152         return retval;
4153 }