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