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