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