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