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