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