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