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