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