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