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