cf48dc772576527f933aebc3ce791f8eacc144c0
[modest] / src / hildon2 / modest-msg-view-window.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <glib/gi18n.h>
30 #include <string.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
33 #include <tny-msg.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include <hildon/hildon-pannable-area.h>
52 #include <hildon/hildon-picker-dialog.h>
53 #include "modest-defs.h"
54 #include "modest-hildon-includes.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
62 #include <math.h>
63 #include <errno.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
66
67 #define MYDOCS_ENV "MYDOCSDIR"
68 #define DOCS_FOLDER ".documents"
69
70 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
71 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
72 static void  modest_header_view_observer_init(
73                 ModestHeaderViewObserverIface *iface_class);
74 static void  modest_msg_view_window_finalize     (GObject *obj);
75 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
76                                                          gpointer data);
77 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
78                                                         ModestMsgViewWindow *obj);
79 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
80                                                         ModestMsgViewWindow *obj);
81
82 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
83
84 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
85 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
86                                                   GdkEventKey *event,
87                                                   gpointer userdata);
88
89 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
90
91 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
92                                                    gboolean show_toolbar);
93
94 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
95                                                            GdkEvent *event,
96                                                            ModestMsgViewWindow *window);
97
98 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
99                                                    GtkTreePath *arg1,
100                                                    GtkTreeIter *arg2,
101                                                    ModestMsgViewWindow *window);
102
103 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
104                                                    GtkTreePath *arg1,
105                                                    ModestMsgViewWindow *window);
106
107 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
108                                                     GtkTreePath *tree_path,
109                                                     GtkTreeIter *tree_iter,
110                                                     ModestMsgViewWindow *window);
111
112 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
113                                                      GtkTreePath *arg1,
114                                                      GtkTreeIter *arg2,
115                                                      gpointer arg3,
116                                                      ModestMsgViewWindow *window);
117
118 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
119                                                           GtkTreeModel *model,
120                                                           const gchar *tny_folder_id);
121
122 static void cancel_progressbar  (GtkToolButton *toolbutton,
123                                  ModestMsgViewWindow *self);
124
125 static void on_queue_changed    (ModestMailOperationQueue *queue,
126                                  ModestMailOperation *mail_op,
127                                  ModestMailOperationQueueNotification type,
128                                  ModestMsgViewWindow *self);
129
130 static void on_account_removed  (TnyAccountStore *account_store, 
131                                  TnyAccount *account,
132                                  gpointer user_data);
133
134 static void on_move_focus (GtkWidget *widget,
135                            GtkDirectionType direction,
136                            gpointer userdata);
137
138 static void view_msg_cb         (ModestMailOperation *mail_op, 
139                                  TnyHeader *header, 
140                                  gboolean canceled,
141                                  TnyMsg *msg, 
142                                  GError *error,
143                                  gpointer user_data);
144
145 static void set_toolbar_mode    (ModestMsgViewWindow *self, 
146                                  ModestToolBarModes mode);
147
148 static void update_window_title (ModestMsgViewWindow *window);
149
150 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
151 static void init_window (ModestMsgViewWindow *obj);
152
153 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
154
155 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
156
157 static gboolean on_fetch_image (ModestMsgView *msgview,
158                                 const gchar *uri,
159                                 TnyStream *stream,
160                                 ModestMsgViewWindow *window);
161
162 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
163                                                      GtkScrollType scroll_type,
164                                                      gboolean horizontal,
165                                                      gpointer userdata);
166
167 /* list my signals */
168 enum {
169         MSG_CHANGED_SIGNAL,
170         SCROLL_CHILD_SIGNAL,
171         LAST_SIGNAL
172 };
173
174 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
175         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
176 };
177
178 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
179 struct _ModestMsgViewWindowPrivate {
180
181         GtkWidget   *msg_view;
182         GtkWidget   *main_scroll;
183         GtkWidget   *find_toolbar;
184         gchar       *last_search;
185
186         /* Progress observers */
187         GtkWidget        *progress_bar;
188         GSList           *progress_widgets;
189
190         /* Tollbar items */
191         GtkWidget   *progress_toolitem;
192         GtkWidget   *cancel_toolitem;
193         GtkWidget   *prev_toolitem;
194         GtkWidget   *next_toolitem;
195         ModestToolBarModes current_toolbar_mode;
196
197         /* Optimized view enabled */
198         gboolean optimized_view;
199
200         /* Whether this was created via the *_new_for_search_result() function. */
201         gboolean is_search_result;
202
203         /* Whether the message is in outbox */
204         gboolean is_outbox;
205         
206         /* A reference to the @model of the header view 
207          * to allow selecting previous/next messages,
208          * if the message is currently selected in the header view.
209          */
210         const gchar *header_folder_id;
211         GtkTreeModel *header_model;
212         GtkTreeRowReference *row_reference;
213         GtkTreeRowReference *next_row_reference;
214
215         gulong clipboard_change_handler;
216         gulong queue_change_handler;
217         gulong account_removed_handler;
218         gulong row_changed_handler;
219         gulong row_deleted_handler;
220         gulong row_inserted_handler;
221         gulong rows_reordered_handler;
222
223         guint purge_timeout;
224         GtkWidget *remove_attachment_banner;
225
226         guint progress_bar_timeout;
227
228         gchar *msg_uid;
229         
230         GSList *sighandlers;
231 };
232
233 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
234                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
235                                                     ModestMsgViewWindowPrivate))
236 /* globals */
237 static GtkWindowClass *parent_class = NULL;
238
239 /* uncomment the following if you have defined any signals */
240 static guint signals[LAST_SIGNAL] = {0};
241
242 GType
243 modest_msg_view_window_get_type (void)
244 {
245         static GType my_type = 0;
246         if (!my_type) {
247                 static const GTypeInfo my_info = {
248                         sizeof(ModestMsgViewWindowClass),
249                         NULL,           /* base init */
250                         NULL,           /* base finalize */
251                         (GClassInitFunc) modest_msg_view_window_class_init,
252                         NULL,           /* class finalize */
253                         NULL,           /* class data */
254                         sizeof(ModestMsgViewWindow),
255                         1,              /* n_preallocs */
256                         (GInstanceInitFunc) modest_msg_view_window_init,
257                         NULL
258                 };
259                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
260                                                   "ModestMsgViewWindow",
261                                                   &my_info, 0);
262
263                 static const GInterfaceInfo modest_header_view_observer_info = 
264                 {
265                         (GInterfaceInitFunc) modest_header_view_observer_init,
266                         NULL,         /* interface_finalize */
267                         NULL          /* interface_data */
268                 };
269
270                 g_type_add_interface_static (my_type,
271                                 MODEST_TYPE_HEADER_VIEW_OBSERVER,
272                                 &modest_header_view_observer_info);
273         }
274         return my_type;
275 }
276
277 static void
278 save_state (ModestWindow *self)
279 {
280         modest_widget_memory_save (modest_runtime_get_conf (),
281                                    G_OBJECT(self), 
282                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
283 }
284
285 static 
286 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
287                                               GtkScrollType scroll_type,
288                                               gboolean horizontal,
289                                               gpointer userdata)
290 {
291         ModestMsgViewWindowPrivate *priv;
292         gboolean return_value;
293
294         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
295         g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
296         return return_value;
297 }
298
299 static void
300 add_scroll_binding (GtkBindingSet *binding_set,
301                     guint keyval,
302                     GtkScrollType scroll)
303 {
304         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
305         
306         gtk_binding_entry_add_signal (binding_set, keyval, 0,
307                                       "scroll_child", 2,
308                                       GTK_TYPE_SCROLL_TYPE, scroll,
309                                       G_TYPE_BOOLEAN, FALSE);
310         gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
311                                       "scroll_child", 2,
312                                       GTK_TYPE_SCROLL_TYPE, scroll,
313                                       G_TYPE_BOOLEAN, FALSE);
314 }
315
316 static void
317 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
318 {
319         GObjectClass *gobject_class;
320         ModestWindowClass *modest_window_class;
321         GtkBindingSet *binding_set;
322
323         gobject_class = (GObjectClass*) klass;
324         modest_window_class = (ModestWindowClass *) klass;
325
326         parent_class            = g_type_class_peek_parent (klass);
327         gobject_class->finalize = modest_msg_view_window_finalize;
328
329         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
330         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
331         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
332
333         modest_window_class->save_state_func = save_state;
334
335         klass->scroll_child = modest_msg_view_window_scroll_child;
336
337         signals[MSG_CHANGED_SIGNAL] =
338                 g_signal_new ("msg-changed",
339                               G_TYPE_FROM_CLASS (gobject_class),
340                               G_SIGNAL_RUN_FIRST,
341                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
342                               NULL, NULL,
343                               modest_marshal_VOID__POINTER_POINTER,
344                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
345
346         signals[SCROLL_CHILD_SIGNAL] =
347                 g_signal_new ("scroll-child",
348                               G_TYPE_FROM_CLASS (gobject_class),
349                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
350                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
351                               NULL, NULL,
352                               modest_marshal_BOOLEAN__ENUM_BOOLEAN,
353                               G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
354
355         binding_set = gtk_binding_set_by_class (klass);
356         add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
357         add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
358         add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
359         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
360         add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
361         add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
362
363         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
364
365 }
366
367 static void modest_header_view_observer_init(
368                 ModestHeaderViewObserverIface *iface_class)
369 {
370         iface_class->update_func = modest_msg_view_window_update_model_replaced;
371 }
372
373 static void
374 modest_msg_view_window_init (ModestMsgViewWindow *obj)
375 {
376         ModestMsgViewWindowPrivate *priv;
377         ModestWindowPrivate *parent_priv = NULL;
378         GtkActionGroup *action_group = NULL;
379         GError *error = NULL;
380         GdkPixbuf *window_icon;
381
382         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
383         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
384         parent_priv->ui_manager = gtk_ui_manager_new();
385
386         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
387         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
388
389         /* Add common actions */
390         gtk_action_group_add_actions (action_group,
391                                       modest_action_entries,
392                                       G_N_ELEMENTS (modest_action_entries),
393                                       obj);
394         gtk_action_group_add_toggle_actions (action_group,
395                                              msg_view_toggle_action_entries,
396                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
397                                              obj);
398
399         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
400         g_object_unref (action_group);
401
402         /* Load the UI definition */
403         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
404                                          &error);
405         if (error) {
406                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
407                 g_error_free (error);
408                 error = NULL;
409         }
410         /* ****** */
411
412         /* Add accelerators */
413         gtk_window_add_accel_group (GTK_WINDOW (obj), 
414                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
415         
416         priv->is_search_result = FALSE;
417         priv->is_outbox = FALSE;
418
419         priv->msg_view      = NULL;
420         priv->header_model  = NULL;
421         priv->header_folder_id  = NULL;
422         priv->clipboard_change_handler = 0;
423         priv->queue_change_handler = 0;
424         priv->account_removed_handler = 0;
425         priv->row_changed_handler = 0;
426         priv->row_deleted_handler = 0;
427         priv->row_inserted_handler = 0;
428         priv->rows_reordered_handler = 0;
429         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
430
431         priv->optimized_view  = FALSE;
432         priv->progress_bar_timeout = 0;
433         priv->purge_timeout = 0;
434         priv->remove_attachment_banner = NULL;
435         priv->msg_uid = NULL;
436         
437         priv->sighandlers = NULL;
438         
439         /* Init window */
440         init_window (MODEST_MSG_VIEW_WINDOW(obj));
441         
442         /* Set window icon */
443         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG); 
444         if (window_icon) {
445                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
446                 g_object_unref (window_icon);
447         }       
448         
449         hildon_program_add_window (hildon_program_get_instance(),
450                                    HILDON_WINDOW(obj));
451
452 }
453
454
455 static gboolean
456 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
457 {
458         ModestMsgViewWindowPrivate *priv = NULL;
459         
460         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
461
462         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
463
464         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
465         
466         if (priv->progress_bar_timeout > 0) {
467                 g_source_remove (priv->progress_bar_timeout);
468                 priv->progress_bar_timeout = 0;
469         }
470         
471         return FALSE;
472 }
473
474 static void 
475 set_toolbar_mode (ModestMsgViewWindow *self, 
476                   ModestToolBarModes mode)
477 {
478         ModestWindowPrivate *parent_priv;
479         ModestMsgViewWindowPrivate *priv;
480 /*      GtkWidget *widget = NULL; */
481
482         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
483
484         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
485         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
486                         
487         /* Sets current toolbar mode */
488         priv->current_toolbar_mode = mode;
489
490         /* Update toolbar dimming state */
491         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
492
493         switch (mode) {
494         case TOOLBAR_MODE_NORMAL:               
495                 if (priv->progress_toolitem) {
496                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
497                         gtk_widget_hide (priv->progress_toolitem);
498                 }
499
500                 if (priv->progress_bar)
501                         gtk_widget_hide (priv->progress_bar);
502                         
503                 if (priv->cancel_toolitem)
504                         gtk_widget_hide (priv->cancel_toolitem);
505
506                 if (priv->prev_toolitem)
507                         gtk_widget_show (priv->prev_toolitem);
508                 
509                 if (priv->next_toolitem)
510                         gtk_widget_show (priv->next_toolitem);
511                         
512                 /* Hide toolbar if optimized view is enabled */
513                 if (priv->optimized_view) {
514                         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
515                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
516                 }
517
518                 break;
519         case TOOLBAR_MODE_TRANSFER:
520                 if (priv->prev_toolitem)
521                         gtk_widget_hide (priv->prev_toolitem);
522                 
523                 if (priv->next_toolitem)
524                         gtk_widget_hide (priv->next_toolitem);
525                 
526                 if (priv->progress_bar)
527                         gtk_widget_show (priv->progress_bar);
528
529                 if (priv->progress_toolitem) {
530                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
531                         gtk_widget_show (priv->progress_toolitem);
532                 }
533                         
534                 if (priv->cancel_toolitem)
535                         gtk_widget_show (priv->cancel_toolitem);
536
537                 /* Show toolbar if it's hiden (optimized view ) */
538                 if (priv->optimized_view) {
539                         gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
540                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
541                 }
542
543                 break;
544         default:
545                 g_return_if_reached ();
546         }
547
548 }
549
550
551 static void
552 init_window (ModestMsgViewWindow *obj)
553 {
554         GtkWidget *main_vbox;
555         ModestMsgViewWindowPrivate *priv;
556
557         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
558
559         priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
560         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
561         main_vbox = gtk_vbox_new  (FALSE, 6);
562 #ifdef MODEST_TOOLKIT_HILDON2
563         priv->main_scroll = hildon_pannable_area_new ();
564         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
565 #else
566 #ifdef MODEST_USE_MOZEMBED
567         priv->main_scroll = priv->msg_view;
568         gtk_widget_set_size_request (priv->msg_view, -1, 1600);
569 #else
570         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
571         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
572 #endif
573         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
574         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
575         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
576
577 #endif
578         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
579         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
580
581         priv->find_toolbar = hildon_find_toolbar_new (NULL);
582         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
583         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
584         
585         gtk_widget_show_all (GTK_WIDGET(main_vbox));
586 }
587
588 static void
589 modest_msg_view_window_disconnect_signals (ModestWindow *self)
590 {
591         ModestMsgViewWindowPrivate *priv;
592         ModestHeaderView *header_view = NULL;
593         ModestWindow *main_window = NULL;
594         
595         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
596
597         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
598             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
599                                            priv->clipboard_change_handler)) 
600                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
601                                              priv->clipboard_change_handler);
602
603         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
604                                            priv->queue_change_handler))
605                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
606                                              priv->queue_change_handler);
607
608         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
609                                            priv->account_removed_handler))
610                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
611                                              priv->account_removed_handler);
612
613         if (priv->header_model) {
614                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
615                                                   priv->row_changed_handler))
616                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
617                                                     priv->row_changed_handler);
618                 
619                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
620                                                   priv->row_deleted_handler))
621                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
622                                              priv->row_deleted_handler);
623                 
624                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
625                                                   priv->row_inserted_handler))
626                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
627                                                     priv->row_inserted_handler);
628                 
629                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
630                                                   priv->rows_reordered_handler))
631                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
632                                                     priv->rows_reordered_handler);
633         }
634
635         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
636         priv->sighandlers = NULL;
637         
638         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
639                                                          FALSE); /* don't create */
640         if (!main_window)
641                 return;
642         
643         header_view = MODEST_HEADER_VIEW(
644                         modest_main_window_get_child_widget(
645                                 MODEST_MAIN_WINDOW(main_window),
646                                 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
647         if (header_view == NULL)
648                 return;
649         
650         modest_header_view_remove_observer(header_view,
651                         MODEST_HEADER_VIEW_OBSERVER(self));
652 }       
653
654 static void
655 modest_msg_view_window_finalize (GObject *obj)
656 {
657         ModestMsgViewWindowPrivate *priv;
658
659         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
660
661         /* Sanity check: shouldn't be needed, the window mgr should
662            call this function before */
663         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
664
665         if (priv->header_model != NULL) {
666                 g_object_unref (priv->header_model);
667                 priv->header_model = NULL;
668         }
669
670         if (priv->progress_bar_timeout > 0) {
671                 g_source_remove (priv->progress_bar_timeout);
672                 priv->progress_bar_timeout = 0;
673         }
674
675         if (priv->remove_attachment_banner) {
676                 gtk_widget_destroy (priv->remove_attachment_banner);
677                 g_object_unref (priv->remove_attachment_banner);
678                 priv->remove_attachment_banner = NULL;
679         }
680
681         if (priv->purge_timeout > 0) {
682                 g_source_remove (priv->purge_timeout);
683                 priv->purge_timeout = 0;
684         }
685
686         if (priv->row_reference) {
687                 gtk_tree_row_reference_free (priv->row_reference);
688                 priv->row_reference = NULL;
689         }
690
691         if (priv->next_row_reference) {
692                 gtk_tree_row_reference_free (priv->next_row_reference);
693                 priv->next_row_reference = NULL;
694         }
695
696         if (priv->msg_uid) {
697                 g_free (priv->msg_uid);
698                 priv->msg_uid = NULL;
699         }
700
701         G_OBJECT_CLASS(parent_class)->finalize (obj);
702 }
703
704 static gboolean
705 select_next_valid_row (GtkTreeModel *model,
706                        GtkTreeRowReference **row_reference,
707                        gboolean cycle,
708                        gboolean is_outbox)
709 {
710         GtkTreeIter tmp_iter;
711         GtkTreePath *path;
712         GtkTreePath *next = NULL;
713         gboolean retval = FALSE, finished;
714
715         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
716
717         path = gtk_tree_row_reference_get_path (*row_reference);
718         gtk_tree_model_get_iter (model, &tmp_iter, path);
719         gtk_tree_row_reference_free (*row_reference);
720         *row_reference = NULL;
721
722         finished = FALSE;
723         do {
724                 TnyHeader *header = NULL;
725
726                 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
727                         gtk_tree_model_get (model, &tmp_iter, 
728                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
729                                             &header, -1);
730
731                         if (header) {
732                                 if (msg_is_visible (header, is_outbox)) {
733                                         next = gtk_tree_model_get_path (model, &tmp_iter);
734                                         *row_reference = gtk_tree_row_reference_new (model, next);
735                                         gtk_tree_path_free (next);
736                                         retval = TRUE;
737                                         finished = TRUE;
738                                 }
739                                 g_object_unref (header);
740                                 header = NULL;
741                         }
742                 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
743                         next = gtk_tree_model_get_path (model, &tmp_iter);
744                         
745                         /* Ensure that we are not selecting the same */
746                         if (gtk_tree_path_compare (path, next) != 0) {
747                                 gtk_tree_model_get (model, &tmp_iter, 
748                                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
749                                                     &header, -1);                               
750                                 if (header) {
751                                         if (msg_is_visible (header, is_outbox)) {
752                                                 *row_reference = gtk_tree_row_reference_new (model, next);
753                                                 retval = TRUE;
754                                                 finished = TRUE;
755                                         }
756                                         g_object_unref (header);
757                                         header = NULL;
758                                 }
759                         } else {
760                                 /* If we ended up in the same message
761                                    then there is no valid next
762                                    message */
763                                 finished = TRUE;
764                         }
765                         gtk_tree_path_free (next);
766                 } else {
767                         /* If there are no more messages and we don't
768                            want to start again in the first one then
769                            there is no valid next message */
770                         finished = TRUE;
771                 }
772         } while (!finished);
773
774         /* Free */
775         gtk_tree_path_free (path);
776
777         return retval;
778 }
779
780 /* TODO: This should be in _init(), with the parameters as properties. */
781 static void
782 modest_msg_view_window_construct (ModestMsgViewWindow *self, 
783                                   const gchar *modest_account_name,
784                                   const gchar *msg_uid)
785 {
786         GObject *obj = NULL;
787         ModestMsgViewWindowPrivate *priv = NULL;
788         ModestWindowPrivate *parent_priv = NULL;
789         ModestDimmingRulesGroup *menu_rules_group = NULL;
790         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
791         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
792
793         obj = G_OBJECT (self);
794         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
795         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
796
797         priv->msg_uid = g_strdup (msg_uid);
798
799         /* Menubar */
800         parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
801         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
802         gtk_widget_show (parent_priv->menubar);
803         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
804
805         menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
806         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
807         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
808
809         /* Add common dimming rules */
810         modest_dimming_rules_group_add_rules (menu_rules_group, 
811                                               modest_msg_view_menu_dimming_entries,
812                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
813                                               MODEST_WINDOW (self));
814         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
815                                               modest_msg_view_toolbar_dimming_entries,
816                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
817                                               MODEST_WINDOW (self));
818         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
819                                               modest_msg_view_clipboard_dimming_entries,
820                                               G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
821                                               MODEST_WINDOW (self));
822
823         /* Insert dimming rules group for this window */
824         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
825         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
826         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
827         g_object_unref (menu_rules_group);
828         g_object_unref (toolbar_rules_group);
829         g_object_unref (clipboard_rules_group);
830
831         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
832
833         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);
834         g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
835                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
836         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
837                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
838         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
839                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
840         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
841                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
842         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
843                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
844         g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
845                           G_CALLBACK (on_fetch_image), obj);
846
847         g_signal_connect (G_OBJECT (obj), "key-release-event",
848                           G_CALLBACK (modest_msg_view_window_key_event),
849                           NULL);
850
851         g_signal_connect (G_OBJECT (obj), "key-press-event",
852                           G_CALLBACK (modest_msg_view_window_key_event),
853                           NULL);
854
855         g_signal_connect (G_OBJECT (obj), "move-focus",
856                           G_CALLBACK (on_move_focus), obj);
857
858         /* Mail Operation Queue */
859         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
860                                                        "queue-changed",
861                                                        G_CALLBACK (on_queue_changed),
862                                                        obj);
863
864         /* Account manager */
865         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
866                                                           "account_removed",
867                                                           G_CALLBACK(on_account_removed),
868                                                           obj);
869
870         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
871
872         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
873         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
874         priv->last_search = NULL;
875
876         /* Init the clipboard actions dim status */
877         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
878
879         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
880
881
882 }
883
884 /* FIXME: parameter checks */
885 ModestWindow *
886 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
887                                               const gchar *modest_account_name,
888                                               const gchar *msg_uid,
889                                               GtkTreeModel *model, 
890                                               GtkTreeRowReference *row_reference)
891 {
892         ModestMsgViewWindow *window = NULL;
893         ModestMsgViewWindowPrivate *priv = NULL;
894         TnyFolder *header_folder = NULL;
895         ModestHeaderView *header_view = NULL;
896         ModestWindow *main_window = NULL;
897         ModestWindowMgr *mgr = NULL;
898
899         MODEST_DEBUG_BLOCK (
900                modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
901         );
902
903         mgr = modest_runtime_get_window_mgr ();
904         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
905         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
906
907         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
908
909         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
910
911         /* Remember the message list's TreeModel so we can detect changes 
912          * and change the list selection when necessary: */
913
914         main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
915         if (main_window) {
916                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
917                                                          MODEST_MAIN_WINDOW(main_window),
918                                                          MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
919         }
920         
921         if (header_view != NULL){
922                 header_folder = modest_header_view_get_folder(header_view);
923                 /* This could happen if the header folder was
924                    unseleted before opening this msg window (for
925                    example if the user selects an account in the
926                    folder view of the main window */
927                 if (header_folder) {
928                         priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
929                         priv->header_folder_id = tny_folder_get_id(header_folder);
930                         g_assert(priv->header_folder_id != NULL);
931                         g_object_unref(header_folder);
932                 }
933         }
934
935         /* Setup row references and connect signals */
936         priv->header_model = g_object_ref (model);
937
938         if (row_reference) {
939                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
940                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
941                 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
942         } else {
943                 priv->row_reference = NULL;
944                 priv->next_row_reference = NULL;
945         }
946
947         /* Connect signals */
948         priv->row_changed_handler = 
949                 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
950                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
951                                   window);
952         priv->row_deleted_handler = 
953                 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
954                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
955                                   window);
956         priv->row_inserted_handler = 
957                 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
958                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
959                                   window);
960         priv->rows_reordered_handler = 
961                 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
962                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
963                                  window);
964
965         if (header_view != NULL){
966                 modest_header_view_add_observer(header_view,
967                                 MODEST_HEADER_VIEW_OBSERVER(window));
968         }
969
970         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
971         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
972         /* gtk_widget_show_all (GTK_WIDGET (window)); */
973         modest_msg_view_window_update_priority (window);
974
975         /* Check dimming rules */
976         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
977         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
978         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
979
980         return MODEST_WINDOW(window);
981 }
982
983 ModestWindow *
984 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
985                                               const gchar *modest_account_name,
986                                               const gchar *msg_uid)
987 {
988         ModestMsgViewWindow *window = NULL;
989         ModestMsgViewWindowPrivate *priv = NULL;
990         ModestWindowMgr *mgr = NULL;
991
992         mgr = modest_runtime_get_window_mgr ();
993         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
994         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
995         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
996
997         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
998
999         /* Remember that this is a search result, 
1000          * so we can disable some UI appropriately: */
1001         priv->is_search_result = TRUE;
1002
1003         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1004         
1005         update_window_title (window);
1006         /* gtk_widget_show_all (GTK_WIDGET (window));*/
1007         modest_msg_view_window_update_priority (window);
1008
1009         /* Check dimming rules */
1010         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1011         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1012         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1013
1014         return MODEST_WINDOW(window);
1015 }
1016
1017 ModestWindow *
1018 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
1019                             const gchar *modest_account_name,
1020                             const gchar *msg_uid)
1021 {
1022         GObject *obj = NULL;
1023         ModestMsgViewWindowPrivate *priv;       
1024         ModestWindowMgr *mgr = NULL;
1025
1026         g_return_val_if_fail (msg, NULL);
1027         mgr = modest_runtime_get_window_mgr ();
1028         obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1029         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1030         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
1031                 modest_account_name, msg_uid);
1032
1033         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1034         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1035
1036         /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1037
1038         /* Check dimming rules */
1039         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1040         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1041         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1042
1043         return MODEST_WINDOW(obj);
1044 }
1045
1046 static void
1047 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1048                                        GtkTreePath *arg1,
1049                                        GtkTreeIter *arg2,
1050                                        ModestMsgViewWindow *window)
1051 {
1052         check_dimming_rules_after_change (window);
1053 }
1054
1055 static void 
1056 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1057                                       GtkTreePath *arg1,
1058                                       ModestMsgViewWindow *window)
1059 {
1060         check_dimming_rules_after_change (window);
1061 }
1062         /* The window could have dissapeared */
1063
1064 static void
1065 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1066 {
1067         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1068         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1069 }
1070
1071
1072 /* On insertions we check if the folder still has the message we are
1073  * showing or do not. If do not, we do nothing. Which means we are still
1074  * not attached to any header folder and thus next/prev buttons are
1075  * still dimmed. Once the message that is shown by msg-view is found, the
1076  * new model of header-view will be attached and the references will be set.
1077  * On each further insertions dimming rules will be checked. However
1078  * this requires extra CPU time at least works.
1079  * (An message might be deleted from TnyFolder and thus will not be
1080  * inserted into the model again for example if it is removed by the
1081  * imap server and the header view is refreshed.)
1082  */
1083 static void 
1084 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1085                                         GtkTreePath *tree_path,
1086                                         GtkTreeIter *tree_iter,
1087                                         ModestMsgViewWindow *window)
1088 {
1089         ModestMsgViewWindowPrivate *priv = NULL; 
1090         TnyHeader *header = NULL;
1091
1092         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1093         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1094
1095         g_assert (model == priv->header_model);
1096         
1097         /* Check if the newly inserted message is the same we are actually
1098          * showing. IF not, we should remain detached from the header model
1099          * and thus prev and next toolbar buttons should remain dimmed. */
1100         gtk_tree_model_get (model, tree_iter, 
1101                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1102                             &header, -1);
1103
1104         if (TNY_IS_HEADER (header)) {
1105                 gchar *uid = NULL;
1106
1107                 uid = modest_tny_folder_get_header_unique_id (header);
1108                 if (!g_str_equal(priv->msg_uid, uid)) {
1109                         check_dimming_rules_after_change (window);
1110                         g_free(uid);
1111                         g_object_unref (G_OBJECT(header));
1112                         return;
1113                 }
1114                 g_free(uid);
1115                 g_object_unref(G_OBJECT(header));
1116         }
1117
1118         if (priv->row_reference) {
1119                 gtk_tree_row_reference_free (priv->row_reference); 
1120         }
1121
1122         /* Setup row_reference for the actual msg. */
1123         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1124         if (priv->row_reference == NULL) {
1125                 g_warning("No reference for msg header item.");
1126                 return;
1127         }
1128
1129         /* Now set up next_row_reference. */
1130         if (priv->next_row_reference) {
1131                 gtk_tree_row_reference_free (priv->next_row_reference); 
1132         }
1133
1134         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1135         select_next_valid_row (priv->header_model,
1136                                &(priv->next_row_reference), FALSE, priv->is_outbox);
1137
1138         /* Connect the remaining callbacks to become able to detect
1139          * changes in header-view. */
1140         priv->row_changed_handler = 
1141                 g_signal_connect (priv->header_model, "row-changed",
1142                                   G_CALLBACK (modest_msg_view_window_on_row_changed),
1143                                   window);
1144         priv->row_deleted_handler = 
1145                 g_signal_connect (priv->header_model, "row-deleted",
1146                                   G_CALLBACK (modest_msg_view_window_on_row_deleted),
1147                                   window);
1148         priv->rows_reordered_handler = 
1149                 g_signal_connect (priv->header_model, "rows-reordered",
1150                                   G_CALLBACK (modest_msg_view_window_on_row_reordered),
1151                                   window);
1152
1153         check_dimming_rules_after_change (window);      
1154 }
1155
1156 static void 
1157 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1158                                          GtkTreePath *arg1,
1159                                          GtkTreeIter *arg2,
1160                                          gpointer arg3,
1161                                          ModestMsgViewWindow *window)
1162 {
1163         ModestMsgViewWindowPrivate *priv = NULL; 
1164         gboolean already_changed = FALSE;
1165
1166         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1167
1168         /* If the current row was reordered select the proper next
1169            valid row. The same if the next row reference changes */
1170         if (priv->row_reference && 
1171             gtk_tree_row_reference_valid (priv->row_reference)) {
1172                 GtkTreePath *path;
1173                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1174                 if (gtk_tree_path_compare (path, arg1) == 0) {
1175                         if (priv->next_row_reference) {
1176                                 gtk_tree_row_reference_free (priv->next_row_reference);
1177                         }
1178                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1179                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1180                         already_changed = TRUE;
1181                 }
1182                 gtk_tree_path_free (path);
1183         }
1184         if (!already_changed &&
1185             priv->next_row_reference &&
1186             gtk_tree_row_reference_valid (priv->next_row_reference)) {
1187                 GtkTreePath *path;
1188                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1189                 if (gtk_tree_path_compare (path, arg1) == 0) {
1190                         if (priv->next_row_reference) {
1191                                 gtk_tree_row_reference_free (priv->next_row_reference);
1192                         }
1193                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1194                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1195                 }
1196                 gtk_tree_path_free (path);
1197         }
1198         check_dimming_rules_after_change (window);
1199 }
1200
1201 /* The modest_msg_view_window_update_model_replaced implements update
1202  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1203  * actually belongs to the header-view is the same as the TnyFolder of
1204  * the message of msg-view or not. If they are different, there is
1205  * nothing to do. If they are the same, then the model has replaced and
1206  * the reference in msg-view shall be replaced from the old model to
1207  * the new model. In this case the view will be detached from it's
1208  * header folder. From this point the next/prev buttons are dimmed.
1209  */
1210 static void 
1211 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1212                                               GtkTreeModel *model,
1213                                               const gchar *tny_folder_id)
1214 {
1215         ModestMsgViewWindowPrivate *priv = NULL; 
1216         ModestMsgViewWindow *window = NULL;
1217
1218         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1219         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1220
1221         window = MODEST_MSG_VIEW_WINDOW(observer);
1222         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1223
1224         /* If there is an other folder in the header-view then we do
1225          * not care about it's model (msg list). Else if the
1226          * header-view shows the folder the msg shown by us is in, we
1227          * shall replace our model reference and make some check. */
1228         if(model == NULL || tny_folder_id == NULL || 
1229            (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1230                 return;
1231
1232         /* Model is changed(replaced), so we should forget the old
1233          * one. Because there might be other references and there
1234          * might be some change on the model even if we unreferenced
1235          * it, we need to disconnect our signals here. */
1236         if (priv->header_model) {
1237                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1238                                                   priv->row_changed_handler))
1239                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1240                                                     priv->row_changed_handler);
1241                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1242                                                   priv->row_deleted_handler))
1243                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1244                                                     priv->row_deleted_handler);
1245                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1246                                                   priv->row_inserted_handler))
1247                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1248                                                     priv->row_inserted_handler);
1249                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1250                                                   priv->rows_reordered_handler))
1251                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1252                                                     priv->rows_reordered_handler);
1253
1254                 /* Frees */
1255                 if (priv->row_reference)
1256                         gtk_tree_row_reference_free (priv->row_reference);
1257                 if (priv->next_row_reference)
1258                         gtk_tree_row_reference_free (priv->next_row_reference);
1259                 g_object_unref(priv->header_model);
1260
1261                 /* Initialize */
1262                 priv->row_changed_handler = 0;
1263                 priv->row_deleted_handler = 0;
1264                 priv->row_inserted_handler = 0;
1265                 priv->rows_reordered_handler = 0;
1266                 priv->next_row_reference = NULL;
1267                 priv->row_reference = NULL;
1268                 priv->header_model = NULL;
1269         }
1270
1271         priv->header_model = g_object_ref (model);
1272
1273         /* Also we must connect to the new model for row insertions.
1274          * Only for insertions now. We will need other ones only after
1275          * the msg is show by msg-view is added to the new model. */
1276         priv->row_inserted_handler =
1277                 g_signal_connect (priv->header_model, "row-inserted",
1278                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1279                                   window);
1280
1281         modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1282         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1283 }
1284
1285 gboolean 
1286 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1287 {
1288         ModestMsgViewWindowPrivate *priv= NULL; 
1289
1290         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1291         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1292
1293         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1294 }
1295
1296 TnyHeader*
1297 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1298 {
1299         ModestMsgViewWindowPrivate *priv= NULL; 
1300         TnyMsg *msg = NULL;
1301         TnyHeader *header = NULL;
1302         GtkTreePath *path = NULL;
1303         GtkTreeIter iter;
1304
1305         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1306         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1307
1308         /* If the message was not obtained from a treemodel,
1309          * for instance if it was opened directly by the search UI:
1310          */
1311         if (priv->header_model == NULL || 
1312             priv->row_reference == NULL ||
1313             !gtk_tree_row_reference_valid (priv->row_reference)) {
1314                 msg = modest_msg_view_window_get_message (self);
1315                 if (msg) {
1316                         header = tny_msg_get_header (msg);
1317                         g_object_unref (msg);
1318                 }
1319                 return header;
1320         }
1321
1322         /* Get iter of the currently selected message in the header view: */
1323         path = gtk_tree_row_reference_get_path (priv->row_reference);
1324         g_return_val_if_fail (path != NULL, NULL);
1325         gtk_tree_model_get_iter (priv->header_model, 
1326                                  &iter, 
1327                                  path);
1328
1329         /* Get current message header */
1330         gtk_tree_model_get (priv->header_model, &iter, 
1331                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1332                             &header, -1);
1333
1334         gtk_tree_path_free (path);
1335         return header;
1336 }
1337
1338 TnyMsg*
1339 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1340 {
1341         ModestMsgViewWindowPrivate *priv;
1342         
1343         g_return_val_if_fail (self, NULL);
1344         
1345         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1346         
1347         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1348 }
1349
1350 const gchar*
1351 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1352 {
1353         ModestMsgViewWindowPrivate *priv;
1354
1355         g_return_val_if_fail (self, NULL);
1356         
1357         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1358
1359         return (const gchar*) priv->msg_uid;
1360 }
1361
1362 static void 
1363 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1364                                             gpointer data)
1365 {
1366         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1367         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1368         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1369         gboolean is_active;
1370         GtkAction *action;
1371
1372         is_active = gtk_toggle_action_get_active (toggle);
1373
1374         if (is_active) {
1375                 gtk_widget_show (priv->find_toolbar);
1376                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1377         } else {
1378                 gtk_widget_hide (priv->find_toolbar);
1379                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1380         }
1381
1382         /* update the toggle buttons status */
1383         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1384         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1385 }
1386
1387 static void
1388 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1389                                            ModestMsgViewWindow *obj)
1390 {
1391         GtkToggleAction *toggle;
1392         ModestWindowPrivate *parent_priv;
1393         ModestMsgViewWindowPrivate *priv;
1394
1395         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1396         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1397         
1398         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1399         gtk_toggle_action_set_active (toggle, FALSE);
1400         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1401 }
1402
1403 static void
1404 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1405                                            ModestMsgViewWindow *obj)
1406 {
1407         gchar *current_search;
1408         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1409
1410         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1411                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1412                 return;
1413         }
1414
1415         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1416
1417         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1418                 g_free (current_search);
1419                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1420                 return;
1421         }
1422
1423         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1424                 gboolean result;
1425                 g_free (priv->last_search);
1426                 priv->last_search = g_strdup (current_search);
1427                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1428                                                      priv->last_search);
1429                 if (!result) {
1430                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1431                         g_free (priv->last_search);
1432                         priv->last_search = NULL;
1433                 } else {
1434                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1435                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1436                 }
1437         } else {
1438                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1439                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1440                         g_free (priv->last_search);
1441                         priv->last_search = NULL;
1442                 } else {
1443                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1444                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1445                 }
1446         }
1447         
1448         g_free (current_search);
1449                 
1450 }
1451
1452 static gdouble
1453 modest_msg_view_window_get_zoom (ModestWindow *window)
1454 {
1455         ModestMsgViewWindowPrivate *priv;
1456      
1457         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1458
1459         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1460         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1461 }
1462
1463 static gboolean
1464 modest_msg_view_window_key_event (GtkWidget *window,
1465                                   GdkEventKey *event,
1466                                   gpointer userdata)
1467 {
1468         GtkWidget *focus;
1469
1470         focus = gtk_window_get_focus (GTK_WINDOW (window));
1471
1472         /* for the find toolbar case */
1473         if (focus && GTK_IS_ENTRY (focus)) {
1474                 if (event->keyval == GDK_BackSpace) {
1475                         GdkEvent *copy;
1476                         copy = gdk_event_copy ((GdkEvent *) event);
1477                         gtk_widget_event (focus, copy);
1478                         gdk_event_free (copy);
1479                         return TRUE;
1480                 } else 
1481                         return FALSE;
1482         }
1483         if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1484             event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1485             event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1486             event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1487             event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1488             event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1489                 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1490                 /* gboolean return_value; */
1491
1492                 if (event->type == GDK_KEY_PRESS) {
1493                         GtkScrollType scroll_type;
1494                         
1495                         switch (event->keyval) {
1496                         case GDK_Up: 
1497                         case GDK_KP_Up:
1498                                 scroll_type = GTK_SCROLL_STEP_UP; break;
1499                         case GDK_Down: 
1500                         case GDK_KP_Down:
1501                                 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1502                         case GDK_Page_Up:
1503                         case GDK_KP_Page_Up:
1504                                 scroll_type = GTK_SCROLL_PAGE_UP; break;
1505                         case GDK_Page_Down:
1506                         case GDK_KP_Page_Down:
1507                                 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1508                         case GDK_Home:
1509                         case GDK_KP_Home:
1510                                 scroll_type = GTK_SCROLL_START; break;
1511                         case GDK_End:
1512                         case GDK_KP_End:
1513                                 scroll_type = GTK_SCROLL_END; break;
1514                         default: scroll_type = GTK_SCROLL_NONE;
1515                         }
1516                         
1517                         /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",  */
1518                         /*                     scroll_type, FALSE, &return_value); */
1519                         return FALSE;
1520                 } else {
1521                         return FALSE;
1522                 }
1523         } else {
1524                 return FALSE;
1525         }
1526 }
1527
1528 gboolean
1529 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1530 {
1531         GtkTreePath *path;
1532         ModestMsgViewWindowPrivate *priv;
1533         GtkTreeIter tmp_iter;
1534         gboolean is_last_selected;
1535
1536         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1537         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1538
1539         /*if no model (so no rows at all), then virtually we are the last*/
1540         if (!priv->header_model || !priv->row_reference)
1541                 return TRUE;
1542
1543         if (!gtk_tree_row_reference_valid (priv->row_reference))
1544                 return TRUE;
1545
1546         path = gtk_tree_row_reference_get_path (priv->row_reference);
1547         if (path == NULL)
1548                 return TRUE;
1549
1550         is_last_selected = TRUE;
1551         while (is_last_selected) {
1552                 TnyHeader *header;
1553                 gtk_tree_path_next (path);
1554                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1555                         break;
1556                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1557                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1558                                 &header, -1);
1559                 if (header) {
1560                         if (msg_is_visible (header, priv->is_outbox))
1561                                 is_last_selected = FALSE;
1562                         g_object_unref(G_OBJECT(header));
1563                 }
1564         }
1565         gtk_tree_path_free (path);
1566         return is_last_selected;
1567 }
1568
1569 gboolean
1570 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1571 {
1572         ModestMsgViewWindowPrivate *priv;
1573
1574         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1575         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1576
1577         return priv->header_model != NULL;
1578 }
1579
1580 gboolean
1581 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1582 {
1583         ModestMsgViewWindowPrivate *priv;
1584
1585         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1586         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1587
1588         return priv->is_search_result;
1589 }
1590
1591 static gboolean
1592 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1593 {
1594         if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1595                 return FALSE;
1596         if (!check_outbox) {
1597                 return TRUE;
1598         } else {
1599                 ModestTnySendQueueStatus status;
1600                 status = modest_tny_all_send_queues_get_msg_status (header);
1601                 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1602                         (status != MODEST_TNY_SEND_QUEUE_SENDING));
1603         }
1604 }
1605
1606 gboolean
1607 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1608 {
1609         GtkTreePath *path;
1610         ModestMsgViewWindowPrivate *priv;
1611         gboolean is_first_selected;
1612         GtkTreeIter tmp_iter;
1613
1614         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1615         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1616
1617         /*if no model (so no rows at all), then virtually we are the first*/
1618         if (!priv->header_model || !priv->row_reference)
1619                 return TRUE;
1620
1621         if (!gtk_tree_row_reference_valid (priv->row_reference))
1622                 return TRUE;
1623
1624         path = gtk_tree_row_reference_get_path (priv->row_reference);
1625         if (!path)
1626                 return TRUE;
1627
1628         is_first_selected = TRUE;
1629         while (is_first_selected) {
1630                 TnyHeader *header;
1631                 if(!gtk_tree_path_prev (path))
1632                         break;
1633                 /* Here the 'if' is needless for logic, but let make sure
1634                  * iter is valid for gtk_tree_model_get. */
1635                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1636                         break;
1637                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1638                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1639                                 &header, -1);
1640                 if (header) {
1641                         if (msg_is_visible (header, priv->is_outbox))
1642                                 is_first_selected = FALSE;
1643                         g_object_unref(G_OBJECT(header));
1644                 }
1645         }
1646         gtk_tree_path_free (path);
1647         return is_first_selected;
1648 }
1649
1650 typedef struct {
1651         TnyHeader *header;
1652         GtkTreeRowReference *row_reference;
1653 } MsgReaderInfo;
1654
1655 static void
1656 message_reader_performer (gboolean canceled, 
1657                           GError *err,
1658                           GtkWindow *parent_window, 
1659                           TnyAccount *account, 
1660                           gpointer user_data)
1661 {
1662         ModestMailOperation *mail_op = NULL;
1663         MsgReaderInfo *info;
1664
1665         info = (MsgReaderInfo *) user_data;
1666         if (canceled || err) {
1667                 goto frees;
1668         }
1669
1670         /* Register the header - it'll be unregistered in the callback */
1671         modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1672
1673         /* New mail operation */
1674         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1675                                                                  modest_ui_actions_disk_operations_error_handler, 
1676                                                                  NULL, NULL);
1677                                 
1678         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1679         modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1680         g_object_unref (mail_op);
1681
1682         /* Update dimming rules */
1683         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1684         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1685
1686  frees:
1687         /* Frees. The row_reference will be freed by the view_msg_cb callback */
1688         g_object_unref (info->header);
1689         g_slice_free (MsgReaderInfo, info);
1690 }
1691
1692
1693 /**
1694  * Reads the message whose summary item is @header. It takes care of
1695  * several things, among others:
1696  *
1697  * If the message was not previously downloaded then ask the user
1698  * before downloading. If there is no connection launch the connection
1699  * dialog. Update toolbar dimming rules.
1700  *
1701  * Returns: TRUE if the mail operation was started, otherwise if the
1702  * user do not want to download the message, or if the user do not
1703  * want to connect, then the operation is not issued
1704  **/
1705 static gboolean
1706 message_reader (ModestMsgViewWindow *window,
1707                 ModestMsgViewWindowPrivate *priv,
1708                 TnyHeader *header,
1709                 GtkTreeRowReference *row_reference)
1710 {
1711         ModestWindowMgr *mgr;
1712         TnyAccount *account;
1713         TnyFolder *folder;
1714         MsgReaderInfo *info;
1715
1716         g_return_val_if_fail (row_reference != NULL, FALSE);
1717
1718         mgr = modest_runtime_get_window_mgr ();
1719         /* Msg download completed */
1720         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1721                 /* Ask the user if he wants to download the message if
1722                    we're not online */
1723                 if (!tny_device_is_online (modest_runtime_get_device())) {
1724                         GtkResponseType response;
1725
1726                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1727                                                                             _("mcen_nc_get_msg"));
1728                         if (response == GTK_RESPONSE_CANCEL)
1729                                 return FALSE;
1730                 
1731                         folder = tny_header_get_folder (header);
1732                         info = g_slice_new (MsgReaderInfo);
1733                         info->header = g_object_ref (header);
1734                         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1735
1736                         /* Offer the connection dialog if necessary */
1737                         modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
1738                                                                        TRUE,
1739                                                                        TNY_FOLDER_STORE (folder),
1740                                                                        message_reader_performer, 
1741                                                                        info);
1742                         g_object_unref (folder);
1743                         return TRUE;
1744                 }
1745         }
1746         
1747         folder = tny_header_get_folder (header);
1748         account = tny_folder_get_account (folder);
1749         info = g_slice_new (MsgReaderInfo);
1750         info->header = g_object_ref (header);
1751         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1752         
1753         message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1754         g_object_unref (account);
1755         g_object_unref (folder);
1756
1757         return TRUE;
1758 }
1759
1760 gboolean        
1761 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1762 {
1763         ModestMsgViewWindowPrivate *priv;
1764         GtkTreePath *path= NULL;
1765         GtkTreeIter tmp_iter;
1766         TnyHeader *header;
1767         gboolean retval = TRUE;
1768         GtkTreeRowReference *row_reference = NULL;
1769
1770         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1771         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1772
1773         if (!priv->row_reference)
1774                 return FALSE;
1775
1776         /* Update the next row reference if it's not valid. This could
1777            happen if for example the header which it was pointing to,
1778            was deleted. The best place to do it is in the row-deleted
1779            handler but the tinymail model do not work like the glib
1780            tree models and reports the deletion when the row is still
1781            there */
1782         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1783                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1784                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1785                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1786                 }
1787         }
1788         if (priv->next_row_reference)
1789                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1790         if (path == NULL)
1791                 return FALSE;
1792
1793         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1794
1795         gtk_tree_model_get_iter (priv->header_model,
1796                                  &tmp_iter,
1797                                  path);
1798         gtk_tree_path_free (path);
1799
1800         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1801                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1802                             &header, -1);
1803         
1804         /* Read the message & show it */
1805         if (!message_reader (window, priv, header, row_reference)) {
1806                 retval = FALSE;
1807         }
1808         gtk_tree_row_reference_free (row_reference);
1809
1810         /* Free */
1811         g_object_unref (header);
1812
1813         return retval;
1814 }
1815
1816 gboolean        
1817 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1818 {
1819         ModestMsgViewWindowPrivate *priv = NULL;
1820         GtkTreePath *path;
1821         gboolean finished = FALSE;
1822         gboolean retval = FALSE;
1823
1824         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1825         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1826
1827         /* Return inmediatly if there is no header model */
1828         if (!priv->header_model || !priv->row_reference)
1829                 return FALSE;
1830
1831         path = gtk_tree_row_reference_get_path (priv->row_reference);
1832         while (!finished && gtk_tree_path_prev (path)) {
1833                 TnyHeader *header;
1834                 GtkTreeIter iter;
1835
1836                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1837                 gtk_tree_model_get (priv->header_model, &iter, 
1838                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1839                                     &header, -1);
1840                 finished = TRUE;
1841                 if (header) {
1842                         if (msg_is_visible (header, priv->is_outbox)) {
1843                                 GtkTreeRowReference *row_reference;
1844                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1845                                 /* Read the message & show it */
1846                                 retval = message_reader (window, priv, header, row_reference);
1847                                 gtk_tree_row_reference_free (row_reference);
1848                         } else {
1849                                 finished = FALSE;
1850                         }
1851                         g_object_unref (header);
1852                 }
1853         }
1854
1855         gtk_tree_path_free (path);
1856         return retval;
1857 }
1858
1859 static void
1860 view_msg_cb (ModestMailOperation *mail_op, 
1861              TnyHeader *header, 
1862              gboolean canceled,
1863              TnyMsg *msg, 
1864              GError *error,
1865              gpointer user_data)
1866 {
1867         ModestMsgViewWindow *self = NULL;
1868         ModestMsgViewWindowPrivate *priv = NULL;
1869         GtkTreeRowReference *row_reference = NULL;
1870
1871         /* Unregister the header (it was registered before creating the mail operation) */
1872         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1873
1874         row_reference = (GtkTreeRowReference *) user_data;
1875         if (canceled) {
1876                 gtk_tree_row_reference_free (row_reference);
1877                 return;
1878         }
1879         
1880         /* If there was any error */
1881         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1882                 gtk_tree_row_reference_free (row_reference);                    
1883                 return;
1884         }
1885
1886         /* Get the window */ 
1887         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1888         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1889         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1890
1891         /* Update the row reference */
1892         if (priv->row_reference != NULL) {
1893                 gtk_tree_row_reference_free (priv->row_reference);
1894                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1895                 if (priv->next_row_reference != NULL) {
1896                         gtk_tree_row_reference_free (priv->next_row_reference);
1897                 }
1898                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1899                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1900         }
1901
1902         /* Mark header as read */
1903         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1904                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1905
1906         /* Set new message */
1907         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1908                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1909                 modest_msg_view_window_update_priority (self);
1910                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1911                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1912         }
1913
1914         /* Set the new message uid of the window  */
1915         if (priv->msg_uid) {
1916                 g_free (priv->msg_uid);
1917                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1918         }
1919
1920         /* Notify the observers */
1921         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1922                        0, priv->header_model, priv->row_reference);
1923
1924         /* Frees */
1925         g_object_unref (self);
1926         gtk_tree_row_reference_free (row_reference);            
1927 }
1928
1929 TnyFolderType
1930 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1931 {
1932         ModestMsgViewWindowPrivate *priv;
1933         TnyMsg *msg;
1934         TnyFolderType folder_type;
1935
1936         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1937
1938         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1939
1940         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1941         if (msg) {
1942                 TnyFolder *folder;
1943
1944                 folder = tny_msg_get_folder (msg);
1945                 if (folder) {
1946                         folder_type = modest_tny_folder_guess_folder_type (folder);
1947                         g_object_unref (folder);
1948                 }
1949                 g_object_unref (msg);
1950         }
1951
1952         return folder_type;
1953 }
1954
1955
1956 static void
1957 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1958 {
1959         ModestMsgViewWindowPrivate *priv;
1960         TnyHeader *header = NULL;
1961         TnyHeaderFlags flags = 0;
1962
1963         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1964
1965         if (priv->header_model && priv->row_reference) {
1966                 GtkTreeIter iter;
1967                 GtkTreePath *path = NULL;
1968
1969                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1970                 g_return_if_fail (path != NULL);
1971                 gtk_tree_model_get_iter (priv->header_model, 
1972                                          &iter, 
1973                                          gtk_tree_row_reference_get_path (priv->row_reference));
1974
1975                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1976                                     &header, -1);
1977                 gtk_tree_path_free (path);
1978         } else {
1979                 TnyMsg *msg;
1980                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1981                 if (msg) {
1982                         header = tny_msg_get_header (msg);
1983                         g_object_unref (msg);
1984                 }
1985         }
1986
1987         if (header) {
1988                 flags = tny_header_get_flags (header);
1989                 g_object_unref(G_OBJECT(header));
1990         }
1991
1992         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1993
1994 }
1995
1996 static void
1997 toolbar_resize (ModestMsgViewWindow *self)
1998 {
1999         ModestMsgViewWindowPrivate *priv = NULL;
2000         ModestWindowPrivate *parent_priv = NULL;
2001         GtkWidget *widget;
2002         gint static_button_size;
2003         ModestWindowMgr *mgr;
2004
2005         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2006         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2007         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2008
2009         mgr = modest_runtime_get_window_mgr ();
2010         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2011
2012         if (parent_priv->toolbar) {
2013                 /* left size buttons */
2014                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2015                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2016                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2017                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2018                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2019                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2020                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2021                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2022                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2023                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2024                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2025                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2026                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2027                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2028                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2029                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2030                 
2031                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2032                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2033                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2034                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2035                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2036                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2037                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2038                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2039         }
2040                 
2041 }
2042
2043 static void
2044 modest_msg_view_window_show_toolbar (ModestWindow *self,
2045                                      gboolean show_toolbar)
2046 {
2047         ModestMsgViewWindowPrivate *priv = NULL;
2048         ModestWindowPrivate *parent_priv;
2049         GtkWidget *reply_button = NULL, *menu = NULL;
2050         GtkWidget *placeholder = NULL;
2051         gint insert_index;
2052
2053         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2054         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2055
2056         /* Set optimized view status */
2057         priv->optimized_view = !show_toolbar;
2058
2059         if (!parent_priv->toolbar) {
2060                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2061                                                                   "/ToolBar");
2062                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2063
2064                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2065                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2066                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2067                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2068                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2069
2070                 /* Add ProgressBar (Transfer toolbar) */ 
2071                 priv->progress_bar = modest_progress_bar_new ();
2072                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2073                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2074                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2075                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2076                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2077
2078                 /* Connect cancel 'clicked' signal to abort progress mode */
2079                 g_signal_connect(priv->cancel_toolitem, "clicked",
2080                                  G_CALLBACK(cancel_progressbar),
2081                                  self);
2082
2083                 /* Add it to the observers list */
2084                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2085
2086                 /* Add to window */
2087                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2088                                            GTK_TOOLBAR (parent_priv->toolbar));
2089
2090                 /* Set reply button tap and hold menu */
2091                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2092                                                           "/ToolBar/ToolbarMessageReply");
2093                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2094                                                   "/ToolbarReplyCSM");
2095                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2096         }
2097
2098         if (show_toolbar) {
2099                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2100                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2101                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2102
2103                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2104                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2105                         set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2106                 else
2107                         set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2108
2109         } else {
2110                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2111                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2112         }
2113 }
2114
2115 static void 
2116 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2117                                                GdkEvent *event,
2118                                                ModestMsgViewWindow *window)
2119 {
2120         if (!GTK_WIDGET_VISIBLE (window))
2121                 return;
2122
2123         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2124 }
2125
2126 gboolean 
2127 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2128 {
2129         ModestMsgViewWindowPrivate *priv;
2130         
2131         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2132         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2133
2134         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2135 }
2136
2137 static void
2138 cancel_progressbar (GtkToolButton *toolbutton,
2139                     ModestMsgViewWindow *self)
2140 {
2141         GSList *tmp;
2142         ModestMsgViewWindowPrivate *priv;
2143         
2144         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2145
2146         /* Get operation observers and cancel its current operation */
2147         tmp = priv->progress_widgets;
2148         while (tmp) {
2149                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2150                 tmp=g_slist_next(tmp);
2151         }
2152 }
2153 static gboolean
2154 observers_empty (ModestMsgViewWindow *self)
2155 {
2156         GSList *tmp = NULL;
2157         ModestMsgViewWindowPrivate *priv;
2158         gboolean is_empty = TRUE;
2159         guint pending_ops = 0;
2160  
2161         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2162         tmp = priv->progress_widgets;
2163
2164         /* Check all observers */
2165         while (tmp && is_empty)  {
2166                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2167                 is_empty = pending_ops == 0;
2168                 
2169                 tmp = g_slist_next(tmp);
2170         }
2171         
2172         return is_empty;
2173 }
2174
2175 static void
2176 on_account_removed (TnyAccountStore *account_store, 
2177                     TnyAccount *account,
2178                     gpointer user_data)
2179 {
2180         /* Do nothing if it's a transport account, because we only
2181            show the messages of a store account */
2182         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2183                 const gchar *parent_acc = NULL;
2184                 const gchar *our_acc = NULL;
2185
2186                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2187                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2188
2189                 /* Close this window if I'm showing a message of the removed account */
2190                 if (strcmp (parent_acc, our_acc) == 0)
2191                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2192         }
2193 }
2194
2195 static void 
2196 on_mail_operation_started (ModestMailOperation *mail_op,
2197                            gpointer user_data)
2198 {
2199         ModestMsgViewWindow *self;
2200         ModestMailOperationTypeOperation op_type;
2201         GSList *tmp;
2202         ModestMsgViewWindowPrivate *priv;
2203         GObject *source = NULL;
2204
2205         self = MODEST_MSG_VIEW_WINDOW (user_data);
2206         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2207         op_type = modest_mail_operation_get_type_operation (mail_op);
2208         tmp = priv->progress_widgets;
2209         source = modest_mail_operation_get_source(mail_op);
2210         if (G_OBJECT (self) == source) {
2211                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2212                         set_toolbar_transfer_mode(self);
2213                         while (tmp) {
2214                                 modest_progress_object_add_operation (
2215                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2216                                                 mail_op);
2217                                 tmp = g_slist_next (tmp);
2218                         }
2219                 }
2220         }
2221         g_object_unref (source);
2222 }
2223
2224 static void 
2225 on_mail_operation_finished (ModestMailOperation *mail_op,
2226                             gpointer user_data)
2227 {
2228         ModestMsgViewWindow *self;
2229         ModestMailOperationTypeOperation op_type;
2230         GSList *tmp;
2231         ModestMsgViewWindowPrivate *priv;
2232         
2233         self = MODEST_MSG_VIEW_WINDOW (user_data);
2234         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2235         op_type = modest_mail_operation_get_type_operation (mail_op);
2236         tmp = priv->progress_widgets;
2237         
2238         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2239                 while (tmp) {
2240                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2241                                                                  mail_op);
2242                         tmp = g_slist_next (tmp);
2243                 }
2244
2245                 /* If no more operations are being observed, NORMAL mode is enabled again */
2246                 if (observers_empty (self)) {
2247                         set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2248                 }
2249
2250                 /* Update dimming rules. We have to do this right here
2251                    and not in view_msg_cb because at that point the
2252                    transfer mode is still enabled so the dimming rule
2253                    won't let the user delete the message that has been
2254                    readed for example */
2255                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2256                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2257         }
2258 }
2259
2260 static void
2261 on_queue_changed (ModestMailOperationQueue *queue,
2262                   ModestMailOperation *mail_op,
2263                   ModestMailOperationQueueNotification type,
2264                   ModestMsgViewWindow *self)
2265 {       
2266         ModestMsgViewWindowPrivate *priv;
2267
2268         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2269
2270         /* If this operations was created by another window, do nothing */
2271         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2272             return;
2273
2274         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2275                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2276                                                                G_OBJECT (mail_op),
2277                                                                "operation-started",
2278                                                                G_CALLBACK (on_mail_operation_started),
2279                                                                self);
2280                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2281                                                                G_OBJECT (mail_op),
2282                                                                "operation-finished",
2283                                                                G_CALLBACK (on_mail_operation_finished),
2284                                                                self);
2285         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2286                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2287                                                                   G_OBJECT (mail_op),
2288                                                                   "operation-started");
2289                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2290                                                                   G_OBJECT (mail_op),
2291                                                                   "operation-finished");
2292         }
2293 }
2294
2295 TnyList *
2296 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2297 {
2298         ModestMsgViewWindowPrivate *priv;
2299         TnyList *selected_attachments = NULL;
2300         
2301         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2302         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2303
2304         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2305         
2306         return selected_attachments;
2307 }
2308
2309 typedef struct {
2310         gchar *filepath;
2311         GtkWidget *banner;
2312         guint banner_idle_id;
2313 } DecodeAsyncHelper;
2314
2315 static gboolean
2316 decode_async_banner_idle (gpointer user_data)
2317 {
2318         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2319
2320         helper->banner_idle_id = 0;
2321         helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2322         g_object_ref (helper->banner);
2323
2324         return FALSE;
2325 }
2326
2327 static void
2328 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2329                                    gboolean cancelled, 
2330                                    TnyStream *stream, 
2331                                    GError *err, 
2332                                    gpointer user_data)
2333 {
2334         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2335
2336         if (helper->banner_idle_id > 0) {
2337                 g_source_remove (helper->banner_idle_id);
2338                 helper->banner_idle_id = 0;
2339         }
2340         if (helper->banner) {
2341                 gtk_widget_destroy (helper->banner);
2342         }
2343         if (cancelled || err) {
2344                 modest_platform_information_banner (NULL, NULL, 
2345                                                     _("mail_ib_file_operation_failed"));
2346                 goto free;
2347         }
2348
2349         /* make the file read-only */
2350         g_chmod(helper->filepath, 0444);
2351         
2352         /* Activate the file */
2353         modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2354
2355  free:
2356         /* Frees */
2357         g_free (helper->filepath);
2358         g_object_unref (helper->banner);
2359         g_slice_free (DecodeAsyncHelper, helper);
2360 }
2361
2362 void
2363 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, 
2364                                         TnyMimePart *mime_part)
2365 {
2366         ModestMsgViewWindowPrivate *priv;
2367         const gchar *msg_uid;
2368         gchar *attachment_uid = NULL;
2369         gint attachment_index = 0;
2370         TnyList *attachments;
2371
2372         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2373         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2374         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2375
2376         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2377         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2378         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2379         g_object_unref (attachments);
2380         
2381         if (msg_uid && attachment_index >= 0) {
2382                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2383         }
2384
2385         if (mime_part == NULL) {
2386                 gboolean error = FALSE;
2387                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2388                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2389                         error = TRUE;
2390                 } else if (tny_list_get_length (selected_attachments) > 1) {
2391                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2392                         error = TRUE;
2393                 } else {
2394                         TnyIterator *iter;
2395                         iter = tny_list_create_iterator (selected_attachments);
2396                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2397                         g_object_unref (iter);
2398                 }
2399                 g_object_unref (selected_attachments);
2400
2401                 if (error)
2402                         return;
2403         } else {
2404                 g_object_ref (mime_part);
2405         }
2406
2407         if (tny_mime_part_is_purged (mime_part)) {
2408                 g_object_unref (mime_part);
2409                 return;
2410         }
2411
2412         if (!modest_tny_mime_part_is_msg (mime_part)) {
2413                 gchar *filepath = NULL;
2414                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2415                 gboolean show_error_banner = FALSE;
2416                 TnyFsStream *temp_stream = NULL;
2417                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2418                                                                &filepath);
2419                 
2420                 if (temp_stream != NULL) {
2421                         DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2422                         helper->filepath = g_strdup (filepath);
2423                         helper->banner = NULL;
2424                         helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2425                         tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream), 
2426                                                               on_decode_to_stream_async_handler, 
2427                                                               NULL, 
2428                                                               helper);
2429                         g_object_unref (temp_stream);
2430                         /* NOTE: files in the temporary area will be automatically
2431                          * cleaned after some time if they are no longer in use */
2432                 } else {
2433                         if (filepath) {
2434                                 const gchar *content_type;
2435                                 /* the file may already exist but it isn't writable,
2436                                  * let's try to open it anyway */
2437                                 content_type = tny_mime_part_get_content_type (mime_part);
2438                                 modest_platform_activate_file (filepath, content_type);
2439                         } else {
2440                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2441                                 show_error_banner = TRUE;
2442                         }
2443                 }
2444                 if (filepath)
2445                         g_free (filepath);
2446                 if (show_error_banner)
2447                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2448         } else {
2449                 /* message attachment */
2450                 TnyHeader *header = NULL;
2451                 ModestWindowMgr *mgr;
2452                 ModestWindow *msg_win = NULL;
2453                 gboolean found;
2454
2455                 header = tny_msg_get_header (TNY_MSG (mime_part));
2456                 mgr = modest_runtime_get_window_mgr ();
2457                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2458
2459                 if (found) {
2460                         /* if it's found, but there is no msg_win, it's probably in the process of being created;
2461                          * thus, we don't do anything */
2462                         g_warning ("window for is already being created");
2463                 } else {
2464                         /* it's not found, so create a new window for it */
2465                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2466                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2467                         if (!account)
2468                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2469                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2470                         modest_window_set_zoom (MODEST_WINDOW (msg_win),
2471                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2472                         modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2473                         gtk_widget_show_all (GTK_WIDGET (msg_win));
2474                 }
2475         }
2476         g_object_unref (mime_part);
2477 }
2478
2479 typedef struct
2480 {
2481         gchar *filename;
2482         TnyMimePart *part;
2483 } SaveMimePartPair;
2484
2485 typedef struct
2486 {
2487         GList *pairs;
2488         GtkWidget *banner;
2489         GnomeVFSResult result;
2490 } SaveMimePartInfo;
2491
2492 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2493 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2494 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2495 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2496
2497 static void 
2498 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2499 {
2500         
2501         GList *node;
2502         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2503                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2504                 g_free (pair->filename);
2505                 g_object_unref (pair->part);
2506                 g_slice_free (SaveMimePartPair, pair);
2507         }
2508         g_list_free (info->pairs);
2509         info->pairs = NULL;
2510         if (with_struct) {
2511                 gtk_widget_destroy (info->banner);
2512                 g_slice_free (SaveMimePartInfo, info);
2513         }
2514 }
2515
2516 static gboolean
2517 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2518 {
2519         if (info->pairs != NULL) {
2520                 save_mime_part_to_file (info);
2521         } else {
2522                 /* This is a GDK lock because we are an idle callback and
2523                  * hildon_banner_show_information is or does Gtk+ code */
2524
2525                 gdk_threads_enter (); /* CHECKED */
2526                 save_mime_part_info_free (info, TRUE);
2527                 if (info->result == GNOME_VFS_OK) {
2528                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2529                 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2530                         hildon_banner_show_information (NULL, NULL, dgettext("ke-recv", 
2531                                                                              "cerm_device_memory_full"));
2532                 } else {
2533                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2534                 }
2535                 gdk_threads_leave (); /* CHECKED */
2536         }
2537
2538         return FALSE;
2539 }
2540
2541 static gpointer
2542 save_mime_part_to_file (SaveMimePartInfo *info)
2543 {
2544         GnomeVFSHandle *handle;
2545         TnyStream *stream;
2546         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2547
2548         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2549         if (info->result == GNOME_VFS_OK) {
2550                 GError *error = NULL;
2551                 stream = tny_vfs_stream_new (handle);
2552                 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2553                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2554                         
2555                         info->result = GNOME_VFS_ERROR_IO;
2556                 }
2557                 g_object_unref (G_OBJECT (stream));
2558                 g_object_unref (pair->part);
2559                 g_slice_free (SaveMimePartPair, pair);
2560                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2561         } else {
2562                 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2563                 save_mime_part_info_free (info, FALSE);
2564         }
2565
2566         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2567         return NULL;
2568 }
2569
2570 static void
2571 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2572 {
2573         gboolean is_ok = TRUE;
2574         gint replaced_files = 0;
2575         const GList *files = info->pairs;
2576         const GList *iter;
2577
2578         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2579                 SaveMimePartPair *pair = iter->data;
2580                 if (modest_utils_file_exists (pair->filename)) {
2581                         replaced_files++;
2582                 }
2583         }
2584         if (replaced_files) {
2585                 GtkWidget *confirm_overwrite_dialog;
2586                 const gchar *message = (replaced_files == 1) ?
2587                         _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2588                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2589                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2590                         is_ok = FALSE;
2591                 }
2592                 gtk_widget_destroy (confirm_overwrite_dialog);
2593         }
2594
2595         if (!is_ok) {
2596                 save_mime_part_info_free (info, TRUE);
2597         } else {
2598                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2599                                                                   _CS("sfil_ib_saving"));
2600                 info->banner = banner;
2601                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2602         }
2603
2604 }
2605
2606 static void
2607 save_attachments_response (GtkDialog *dialog,
2608                            gint       arg1,
2609                            gpointer   user_data)  
2610 {
2611         TnyList *mime_parts;
2612         gchar *chooser_uri;
2613         GList *files_to_save = NULL;
2614
2615         mime_parts = TNY_LIST (user_data);
2616
2617         if (arg1 != GTK_RESPONSE_OK)
2618                 goto end;
2619
2620         chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2621
2622         if (!modest_utils_folder_writable (chooser_uri)) {
2623                 hildon_banner_show_information 
2624                         (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2625         } else {
2626                 TnyIterator *iter;
2627
2628                 iter = tny_list_create_iterator (mime_parts);
2629                 while (!tny_iterator_is_done (iter)) {
2630                         TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2631
2632                         if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2633                             !tny_mime_part_is_purged (mime_part) &&
2634                             (tny_mime_part_get_filename (mime_part) != NULL)) {
2635                                 SaveMimePartPair *pair;
2636                                         
2637                                 pair = g_slice_new0 (SaveMimePartPair);
2638
2639                                 if (tny_list_get_length (mime_parts) > 1) {
2640                                         gchar *escaped = 
2641                                                 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2642                                         pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2643                                         g_free (escaped);
2644                                 } else {
2645                                         pair->filename = g_strdup (chooser_uri);
2646                                 }
2647                                 pair->part = mime_part;
2648                                 files_to_save = g_list_prepend (files_to_save, pair);
2649                         }
2650                         tny_iterator_next (iter);
2651                 }
2652                 g_object_unref (iter);
2653         }
2654         g_free (chooser_uri);
2655
2656         if (files_to_save != NULL) {
2657                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2658                 info->pairs = files_to_save;
2659                 info->result = TRUE;
2660                 save_mime_parts_to_file_with_checks (info);
2661         }
2662
2663  end:
2664         /* Free and close the dialog */
2665         g_object_unref (mime_parts);
2666         gtk_widget_destroy (GTK_WIDGET (dialog));
2667 }
2668
2669 void
2670 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2671 {
2672         ModestMsgViewWindowPrivate *priv;
2673         GtkWidget *save_dialog = NULL;
2674         gchar *folder = NULL;
2675         gchar *filename = NULL;
2676         gchar *save_multiple_str = NULL;
2677
2678         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2679         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2680
2681         if (mime_parts == NULL) {
2682                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2683                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2684                         return;
2685         } else {
2686                 g_object_ref (mime_parts);
2687         }
2688
2689         /* prepare dialog */
2690         if (tny_list_get_length (mime_parts) == 1) {
2691                 TnyIterator *iter;
2692                 /* only one attachment selected */
2693                 iter = tny_list_create_iterator (mime_parts);
2694                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2695                 g_object_unref (iter);
2696                 if (!modest_tny_mime_part_is_msg (mime_part) && 
2697                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2698                     !tny_mime_part_is_purged (mime_part)) {
2699                         filename = g_strdup (tny_mime_part_get_filename (mime_part));
2700                 } else {
2701                         /* TODO: show any error? */
2702                         g_warning ("Tried to save a non-file attachment");
2703                         g_object_unref (mime_parts);
2704                         return;
2705                 }
2706                 g_object_unref (mime_part);
2707         } else {
2708                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2709                                                      tny_list_get_length (mime_parts));
2710         }
2711         
2712         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2713                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2714
2715         /* set folder */
2716         folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2717         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2718         g_free (folder);
2719
2720         /* set filename */
2721         if (filename) {
2722                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2723                                                    filename);
2724                 g_free (filename);
2725         }
2726
2727         /* if multiple, set multiple string */
2728         if (save_multiple_str) {
2729                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2730                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2731         }
2732
2733         /* We must run this asynchronously, because the hildon dialog
2734            performs a gtk_dialog_run by itself which leads to gdk
2735            deadlocks */
2736         g_signal_connect (save_dialog, "response", 
2737                           G_CALLBACK (save_attachments_response), mime_parts);
2738
2739         gtk_widget_show_all (save_dialog);
2740 }
2741
2742 static gboolean
2743 show_remove_attachment_information (gpointer userdata)
2744 {
2745         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2746         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2747
2748         /* We're outside the main lock */
2749         gdk_threads_enter ();
2750
2751         if (priv->remove_attachment_banner != NULL) {
2752                 gtk_widget_destroy (priv->remove_attachment_banner);
2753                 g_object_unref (priv->remove_attachment_banner);
2754         }
2755
2756         priv->remove_attachment_banner = g_object_ref (
2757                 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2758
2759         gdk_threads_leave ();
2760
2761         return FALSE;
2762 }
2763
2764 void
2765 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2766 {
2767         ModestMsgViewWindowPrivate *priv;
2768         TnyList *mime_parts = NULL;
2769         gchar *confirmation_message;
2770         gint response;
2771         gint n_attachments;
2772         TnyMsg *msg;
2773         TnyIterator *iter;
2774
2775         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2776         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2777
2778         if (get_all)
2779                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2780         else
2781                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2782                 
2783         /* Remove already purged messages from mime parts list */
2784         iter = tny_list_create_iterator (mime_parts);
2785         while (!tny_iterator_is_done (iter)) {
2786                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2787                 tny_iterator_next (iter);
2788                 if (tny_mime_part_is_purged (part)) {
2789                         tny_list_remove (mime_parts, (GObject *) part);
2790                 }
2791                 g_object_unref (part);
2792         }
2793         g_object_unref (iter);
2794
2795         if (tny_list_get_length (mime_parts) == 0) {
2796                 g_object_unref (mime_parts);
2797                 return;
2798         }
2799
2800         n_attachments = tny_list_get_length (mime_parts);
2801         if (n_attachments == 1) {
2802                 gchar *filename;
2803                 TnyMimePart *part;
2804
2805                 iter = tny_list_create_iterator (mime_parts);
2806                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2807                 g_object_unref (iter);
2808                 if (modest_tny_mime_part_is_msg (part)) {
2809                         TnyHeader *header;
2810                         header = tny_msg_get_header (TNY_MSG (part));
2811                         filename = tny_header_dup_subject (header);
2812                         g_object_unref (header);
2813                         if (filename == NULL)
2814                                 filename = g_strdup (_("mail_va_no_subject"));
2815                 } else {
2816                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2817                 }
2818                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2819                 g_free (filename);
2820                 g_object_unref (part);
2821         } else {
2822                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2823                                                                  "mcen_nc_purge_files_text", 
2824                                                                  n_attachments), n_attachments);
2825         }
2826         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2827                                                             confirmation_message);
2828         g_free (confirmation_message);
2829
2830         if (response != GTK_RESPONSE_OK) {
2831                 g_object_unref (mime_parts);
2832                 return;
2833         }
2834
2835         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2836         
2837         iter = tny_list_create_iterator (mime_parts);
2838         while (!tny_iterator_is_done (iter)) {
2839                 TnyMimePart *part;
2840
2841                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2842                 tny_mime_part_set_purged (TNY_MIME_PART (part));
2843                 g_object_unref (part);
2844                 tny_iterator_next (iter);
2845         }
2846         g_object_unref (iter);
2847
2848         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2849         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2850         tny_msg_rewrite_cache (msg);
2851         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2852         g_object_unref (msg);
2853
2854         g_object_unref (mime_parts);
2855
2856         if (priv->purge_timeout > 0) {
2857                 g_source_remove (priv->purge_timeout);
2858                 priv->purge_timeout = 0;
2859         }
2860
2861         if (priv->remove_attachment_banner) {
2862                 gtk_widget_destroy (priv->remove_attachment_banner);
2863                 g_object_unref (priv->remove_attachment_banner);
2864                 priv->remove_attachment_banner = NULL;
2865         }
2866
2867
2868 }
2869
2870
2871 static void
2872 update_window_title (ModestMsgViewWindow *window)
2873 {
2874         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2875         TnyMsg *msg = NULL;
2876         TnyHeader *header = NULL;
2877         gchar *subject = NULL;
2878         
2879         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2880
2881         if (msg != NULL) {
2882                 header = tny_msg_get_header (msg);
2883                 subject = tny_header_dup_subject (header);
2884                 g_object_unref (header);
2885                 g_object_unref (msg);
2886         }
2887
2888         if ((subject == NULL)||(subject[0] == '\0')) {
2889                 g_free (subject);
2890                 subject = g_strdup (_("mail_va_no_subject"));
2891         }
2892
2893         gtk_window_set_title (GTK_WINDOW (window), subject);
2894 }
2895
2896
2897 static void on_move_focus (GtkWidget *widget,
2898                            GtkDirectionType direction,
2899                            gpointer userdata)
2900 {
2901         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2902 }
2903
2904 static TnyStream *
2905 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2906 {
2907         GnomeVFSResult result;
2908         GnomeVFSHandle *handle = NULL;
2909         GnomeVFSFileInfo *info = NULL;
2910         TnyStream *stream;
2911
2912         result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2913         if (result != GNOME_VFS_OK) {
2914                 *expected_size = 0;
2915                 return NULL;
2916         }
2917         
2918         info = gnome_vfs_file_info_new ();
2919         result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2920         if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2921                 /* We put a "safe" default size for going to cache */
2922                 *expected_size = (300*1024);
2923         } else {
2924                 *expected_size = info->size;
2925         }
2926         gnome_vfs_file_info_unref (info);
2927
2928         stream = tny_vfs_stream_new (handle);
2929
2930         return stream;
2931
2932 }
2933
2934 typedef struct {
2935         gchar *uri;
2936         gchar *cache_id;
2937         TnyStream *output_stream;
2938         GtkWidget *msg_view;
2939 } FetchImageData;
2940
2941 gboolean
2942 on_fetch_image_idle_refresh_view (gpointer userdata)
2943 {
2944
2945         FetchImageData *fidata = (FetchImageData *) userdata;
2946         g_message ("REFRESH VIEW");
2947         if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
2948                 g_message ("QUEUING DRAW");
2949                 gtk_widget_queue_draw (fidata->msg_view);
2950         }
2951         g_object_unref (fidata->msg_view);
2952         g_slice_free (FetchImageData, fidata);
2953         return FALSE;
2954 }
2955
2956 static gpointer
2957 on_fetch_image_thread (gpointer userdata)
2958 {
2959         FetchImageData *fidata = (FetchImageData *) userdata;
2960         TnyStreamCache *cache;
2961         TnyStream *cache_stream;
2962
2963         cache = modest_runtime_get_images_cache ();
2964         cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
2965         g_free (fidata->cache_id);
2966         g_free (fidata->uri);
2967
2968         if (cache_stream != NULL) {
2969                 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
2970                 tny_stream_close (cache_stream);
2971                 g_object_unref (cache_stream);
2972         }
2973
2974         tny_stream_close (fidata->output_stream);
2975         g_object_unref (fidata->output_stream);
2976
2977
2978         gdk_threads_enter ();
2979         g_idle_add (on_fetch_image_idle_refresh_view, fidata);
2980         gdk_threads_leave ();
2981
2982         return NULL;
2983 }
2984
2985 static gboolean
2986 on_fetch_image (ModestMsgView *msgview,
2987                 const gchar *uri,
2988                 TnyStream *stream,
2989                 ModestMsgViewWindow *window)
2990 {
2991         const gchar *current_account;
2992         ModestMsgViewWindowPrivate *priv;
2993         FetchImageData *fidata;
2994
2995         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2996
2997         current_account = modest_window_get_active_account (MODEST_WINDOW (window));
2998
2999         fidata = g_slice_new0 (FetchImageData);
3000         fidata->msg_view = g_object_ref (msgview);
3001         fidata->uri = g_strdup (uri);
3002         fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3003         fidata->output_stream = g_object_ref (stream);
3004
3005         if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3006                 g_object_unref (fidata->output_stream);
3007                 g_free (fidata->cache_id);
3008                 g_free (fidata->uri);
3009                 g_object_unref (fidata->msg_view);
3010                 g_slice_free (FetchImageData, fidata);
3011                 tny_stream_close (stream);
3012                 return FALSE;
3013         }
3014
3015         return TRUE;;
3016 }
3017
3018 void
3019 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3020 {
3021         ModestMsgViewWindowPrivate *priv;
3022         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3023         GSList *recipients = NULL;
3024         TnyMsg *msg = NULL;
3025         gboolean contacts_to_add = FALSE;
3026
3027         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3028         if (msg == NULL) return;
3029         recipients = modest_tny_msg_get_all_recipients_list (msg);
3030
3031         if (recipients != NULL) {
3032                 GtkWidget *picker_dialog;
3033                 GtkWidget *selector;
3034                 GSList *node;
3035                 gchar *selected;
3036
3037                 selector = hildon_touch_selector_new_text ();
3038                 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3039                         if (!modest_address_book_has_address ((const gchar *) node->data)) {
3040                                 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), 
3041                                                                    (const gchar *) node->data);
3042                                 contacts_to_add = TRUE;
3043                         }
3044                 }
3045
3046                 if (contacts_to_add) {
3047
3048                         picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3049                         gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3050
3051                         hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog), 
3052                                                            HILDON_TOUCH_SELECTOR (selector));
3053                         
3054                         gtk_dialog_run (GTK_DIALOG (picker_dialog));
3055                         selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3056                         gtk_widget_destroy (picker_dialog);
3057
3058                         if (selected)
3059                                 modest_address_book_add_address (selected);
3060                         g_free (selected);
3061
3062                 } else {
3063
3064                         g_object_unref (selector);
3065
3066                 }
3067         }
3068         
3069         if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3070         g_object_unref (msg);
3071 }