9b9e3b9f32bed37d813bb6cc3946ffa2a632e778
[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-maemo-utils.h>
39 #include <modest-tny-msg.h>
40 #include <modest-msg-view-window.h>
41 #include <modest-attachments-view.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar-widget.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include <gtkhtml/gtkhtml-search.h>
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57
58 #define DEFAULT_FOLDER "MyDocs/.documents"
59
60 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
61 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
62 static void  modest_msg_view_window_finalize     (GObject *obj);
63 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
64                                                          gpointer data);
65 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
66                                                         ModestMsgViewWindow *obj);
67 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
68                                                         ModestMsgViewWindow *obj);
69
70 static void  modest_msg_view_window_set_zoom (ModestWindow *window,
71                                               gdouble zoom);
72 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
73 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
74 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
75 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
76                                                           GdkEventKey *event,
77                                                           gpointer userdata);
78 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, 
79                                                            GdkEventWindowState *event, 
80                                                            gpointer userdata);
81 static void modest_msg_view_window_scroll_up (ModestWindow *window);
82 static void modest_msg_view_window_scroll_down (ModestWindow *window);
83 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
84
85 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
86                                                    gboolean show_toolbar);
87
88 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
89                                                            GdkEvent *event,
90                                                            ModestMsgViewWindow *window);
91
92 static void cancel_progressbar  (GtkToolButton *toolbutton,
93                                  ModestMsgViewWindow *self);
94
95 static void on_queue_changed    (ModestMailOperationQueue *queue,
96                                  ModestMailOperation *mail_op,
97                                  ModestMailOperationQueueNotification type,
98                                  ModestMsgViewWindow *self);
99
100 static void on_account_removed  (TnyAccountStore *account_store, 
101                                  TnyAccount *account,
102                                  gpointer user_data);
103
104 static void view_msg_cb         (ModestMailOperation *mail_op, 
105                                  TnyHeader *header, 
106                                  TnyMsg *msg, 
107                                  gpointer user_data);
108
109 static void set_toolbar_mode    (ModestMsgViewWindow *self, 
110                                  ModestToolBarModes mode);
111
112 static void update_window_title (ModestMsgViewWindow *window);
113
114 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
115
116
117 /* list my signals */
118 enum {
119         MSG_CHANGED_SIGNAL,
120         LAST_SIGNAL
121 };
122
123 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
124         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
125         { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
126 };
127
128 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
129         { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
130         { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
131         { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
132         { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
133         { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
134         { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
135 };
136
137 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
138 struct _ModestMsgViewWindowPrivate {
139
140         GtkWidget   *msg_view;
141         GtkWidget   *main_scroll;
142         GtkWidget   *find_toolbar;
143         gchar       *last_search;
144
145         /* Progress observers */
146         GtkWidget        *progress_bar;
147         GSList           *progress_widgets;
148
149         /* Tollbar items */
150         GtkWidget   *progress_toolitem;
151         GtkWidget   *cancel_toolitem;
152         GtkWidget   *prev_toolitem;
153         GtkWidget   *next_toolitem;
154         ModestToolBarModes current_toolbar_mode;
155
156         /* Optimized view enabled */
157         gboolean optimized_view;
158
159         GtkTreeModel *header_model;
160         GtkTreeRowReference *row_reference;
161         GtkTreeRowReference *next_row_reference;
162
163         guint clipboard_change_handler;
164         guint queue_change_handler;
165         guint account_removed_handler;
166
167         guint progress_bar_timeout;
168
169         gchar *msg_uid;
170 };
171
172 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
173                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
174                                                     ModestMsgViewWindowPrivate))
175 /* globals */
176 static GtkWindowClass *parent_class = NULL;
177
178 /* uncomment the following if you have defined any signals */
179 static guint signals[LAST_SIGNAL] = {0};
180
181 GType
182 modest_msg_view_window_get_type (void)
183 {
184         static GType my_type = 0;
185         if (!my_type) {
186                 static const GTypeInfo my_info = {
187                         sizeof(ModestMsgViewWindowClass),
188                         NULL,           /* base init */
189                         NULL,           /* base finalize */
190                         (GClassInitFunc) modest_msg_view_window_class_init,
191                         NULL,           /* class finalize */
192                         NULL,           /* class data */
193                         sizeof(ModestMsgViewWindow),
194                         1,              /* n_preallocs */
195                         (GInstanceInitFunc) modest_msg_view_window_init,
196                         NULL
197                 };
198                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
199                                                   "ModestMsgViewWindow",
200                                                   &my_info, 0);
201         }
202         return my_type;
203 }
204
205 static void
206 save_state (ModestWindow *self)
207 {
208         modest_widget_memory_save (modest_runtime_get_conf (),
209                                    G_OBJECT(self), 
210                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
211 }
212
213
214 static void
215 restore_settings (ModestMsgViewWindow *self)
216 {
217         modest_widget_memory_restore (modest_runtime_get_conf (),
218                                       G_OBJECT(self), 
219                                       MODEST_CONF_MSG_VIEW_WINDOW_KEY);
220 }
221
222 static void
223 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
224 {
225         GObjectClass *gobject_class;
226         ModestWindowClass *modest_window_class;
227         gobject_class = (GObjectClass*) klass;
228         modest_window_class = (ModestWindowClass *) klass;
229
230         parent_class            = g_type_class_peek_parent (klass);
231         gobject_class->finalize = modest_msg_view_window_finalize;
232
233         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
234         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
235         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
236         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
237         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
238
239         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
240
241         modest_window_class->save_state_func = save_state;
242
243         signals[MSG_CHANGED_SIGNAL] =
244                 g_signal_new ("msg-changed",
245                               G_TYPE_FROM_CLASS (gobject_class),
246                               G_SIGNAL_RUN_FIRST,
247                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
248                               NULL, NULL,
249                               modest_marshal_VOID__POINTER_POINTER,
250                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
251 }
252
253 static void
254 modest_msg_view_window_init (ModestMsgViewWindow *obj)
255 {
256         ModestMsgViewWindowPrivate *priv;
257         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
258
259         priv->msg_view      = NULL;
260         priv->header_model  = NULL;
261         priv->clipboard_change_handler = 0;
262         priv->queue_change_handler = 0;
263         priv->account_removed_handler = 0;
264         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
265
266         priv->optimized_view  = FALSE;
267         priv->progress_bar_timeout = 0;
268         priv->msg_uid = NULL;
269 }
270
271
272 static gboolean
273 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
274 {
275         ModestMsgViewWindowPrivate *priv = NULL;
276         
277         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
278
279         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
280
281         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
282         
283         if (priv->progress_bar_timeout > 0) {
284                 g_source_remove (priv->progress_bar_timeout);
285                 priv->progress_bar_timeout = 0;
286         }
287         
288         return FALSE;
289 }
290
291 static void 
292 set_toolbar_mode (ModestMsgViewWindow *self, 
293                   ModestToolBarModes mode)
294 {
295         ModestWindowPrivate *parent_priv;
296         ModestMsgViewWindowPrivate *priv;
297 /*      GtkWidget *widget = NULL; */
298
299         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
300
301         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
302         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
303                         
304         /* Sets current toolbar mode */
305         priv->current_toolbar_mode = mode;
306
307         /* Update toolbar dimming state */
308         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
309
310         switch (mode) {
311         case TOOLBAR_MODE_NORMAL:               
312 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
313 /*              gtk_action_set_sensitive (widget, TRUE); */
314 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
315 /*              gtk_action_set_sensitive (widget, TRUE); */
316 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
317 /*              gtk_action_set_sensitive (widget, TRUE); */
318 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
319 /*              gtk_action_set_sensitive (widget, TRUE); */
320
321                 if (priv->prev_toolitem)
322                         gtk_widget_show (priv->prev_toolitem);
323                 
324                 if (priv->next_toolitem)
325                         gtk_widget_show (priv->next_toolitem);
326                         
327                 if (priv->progress_toolitem)
328                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
329                 if (priv->progress_bar)
330                         gtk_widget_hide (priv->progress_bar);
331                         
332                 if (priv->cancel_toolitem)
333                         gtk_widget_hide (priv->cancel_toolitem);
334
335                 /* Hide toolbar if optimized view is enabled */
336                 if (priv->optimized_view) {
337                         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
338                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
339                 }
340
341                 break;
342         case TOOLBAR_MODE_TRANSFER:
343 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
344 /*              gtk_action_set_sensitive (widget, FALSE); */
345 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
346 /*              gtk_action_set_sensitive (widget, FALSE); */
347 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
348 /*              gtk_action_set_sensitive (widget, FALSE); */
349 /*              widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
350 /*              gtk_action_set_sensitive (widget, FALSE); */
351
352                 if (priv->prev_toolitem)
353                         gtk_widget_hide (priv->prev_toolitem);
354                 
355                 if (priv->next_toolitem)
356                         gtk_widget_hide (priv->next_toolitem);
357                 
358                 if (priv->progress_toolitem)
359                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
360                 if (priv->progress_bar)
361                         gtk_widget_show (priv->progress_bar);
362                         
363                 if (priv->cancel_toolitem)
364                         gtk_widget_show (priv->cancel_toolitem);
365
366                 /* Show toolbar if it's hiden (optimized view ) */
367                 if (priv->optimized_view) {
368                         gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
369                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
370                 }
371
372                 break;
373         default:
374                 g_return_if_reached ();
375         }
376
377 }
378
379
380 static GtkWidget *
381 menubar_to_menu (GtkUIManager *ui_manager)
382 {
383         GtkWidget *main_menu;
384         GtkWidget *menubar;
385         GList *iter;
386
387         /* Create new main menu */
388         main_menu = gtk_menu_new();
389
390         /* Get the menubar from the UI manager */
391         menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
392
393         iter = gtk_container_get_children (GTK_CONTAINER (menubar));
394         while (iter) {
395                 GtkWidget *menu;
396
397                 menu = GTK_WIDGET (iter->data);
398                 gtk_widget_reparent(menu, main_menu);
399
400                 iter = g_list_next (iter);
401         }
402         return main_menu;
403 }
404
405 static void
406 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
407 {
408         GtkWidget *main_vbox;
409         ModestMsgViewWindowPrivate *priv;
410         ModestWindowPrivate *parent_priv;
411         
412         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
413         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
414
415         priv->msg_view = modest_msg_view_new (msg);
416         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
417         main_vbox = gtk_vbox_new  (FALSE, 6);
418
419         /* Menubar */
420         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
421         gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
422         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
423
424         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
425         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
426         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
427         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
428
429         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
430         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
431         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
432
433         priv->find_toolbar = hildon_find_toolbar_new (NULL);
434         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
435         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
436         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
437         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
438         
439         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);
440         gtk_widget_show_all (GTK_WIDGET(main_vbox));
441
442 }       
443
444
445 static void
446 modest_msg_view_window_finalize (GObject *obj)
447 {
448         ModestMsgViewWindowPrivate *priv;
449
450         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
451         if (priv->clipboard_change_handler > 0) {
452                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
453                                              priv->clipboard_change_handler);
454                 priv->clipboard_change_handler = 0;
455         }
456         if (priv->queue_change_handler > 0) {
457                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
458                                              priv->queue_change_handler);
459                 priv->queue_change_handler = 0;
460         }
461         if (priv->account_removed_handler > 0) {
462                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
463                                              priv->account_removed_handler);
464                 priv->account_removed_handler = 0;
465         }
466         if (priv->header_model != NULL) {
467                 g_object_unref (priv->header_model);
468                 priv->header_model = NULL;
469         }
470
471         if (priv->progress_bar_timeout > 0) {
472                 g_source_remove (priv->progress_bar_timeout);
473                 priv->progress_bar_timeout = 0;
474         }
475
476         if (priv->row_reference) {
477                 gtk_tree_row_reference_free (priv->row_reference);
478                 priv->row_reference = NULL;
479         }
480
481         if (priv->next_row_reference) {
482                 gtk_tree_row_reference_free (priv->next_row_reference);
483                 priv->next_row_reference = NULL;
484         }
485
486         if (priv->msg_uid) {
487                 g_free (priv->msg_uid);
488                 priv->msg_uid = NULL;
489         }
490
491         G_OBJECT_CLASS(parent_class)->finalize (obj);
492 }
493
494 static gboolean
495 select_next_valid_row (GtkTreeModel *model,
496                        GtkTreeRowReference **row_reference,
497                        gboolean cycle)
498 {
499         GtkTreeIter tmp_iter;
500         GtkTreePath *path, *next;
501         gboolean retval = FALSE;
502
503         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
504
505         path = gtk_tree_row_reference_get_path (*row_reference);
506         gtk_tree_model_get_iter (model, &tmp_iter, path);
507         gtk_tree_row_reference_free (*row_reference);
508         *row_reference = NULL;
509
510         if (gtk_tree_model_iter_next (model, &tmp_iter)) {
511                 next = gtk_tree_model_get_path (model, &tmp_iter);
512                 *row_reference = gtk_tree_row_reference_new (model, next);
513                 retval = TRUE;
514         } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
515                 next = gtk_tree_model_get_path (model, &tmp_iter);
516
517                 /* Ensure that we are not selecting the same */
518                 if (gtk_tree_path_compare (path, next) != 0) {
519                         *row_reference = gtk_tree_row_reference_new (model, next);
520                         retval = TRUE;
521                 }
522         }
523
524         /* Free */
525         gtk_tree_path_free (path);
526
527         return retval;
528 }
529
530 ModestWindow *
531 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
532                                               const gchar *modest_account_name,
533                                               const gchar *msg_uid,
534                                               GtkTreeModel *model, 
535                                               GtkTreeRowReference *row_reference)
536 {
537         ModestMsgViewWindow *window = NULL;
538         ModestMsgViewWindowPrivate *priv = NULL;
539
540         window = MODEST_MSG_VIEW_WINDOW(modest_msg_view_window_new (msg, modest_account_name, msg_uid));
541         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
542
543         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
544
545         g_object_ref (model);
546         priv->header_model = model;
547         priv->row_reference = gtk_tree_row_reference_copy (row_reference);
548         priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
549         select_next_valid_row (model, &(priv->next_row_reference), TRUE);
550
551         modest_msg_view_window_update_priority (window);
552
553         /* Check toolbar dimming rules */
554         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
555
556         return MODEST_WINDOW(window);
557 }
558
559
560 ModestWindow *
561 modest_msg_view_window_new (TnyMsg *msg, 
562                             const gchar *modest_account_name,
563                             const gchar *msg_uid)
564 {
565         ModestMsgViewWindow *self = NULL;
566         GObject *obj = NULL;
567         ModestMsgViewWindowPrivate *priv = NULL;
568         ModestWindowPrivate *parent_priv = NULL;
569         ModestDimmingRulesGroup *menu_rules_group = NULL;
570         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
571         GtkActionGroup *action_group = NULL;
572         GError *error = NULL;
573         GdkPixbuf *window_icon;
574
575         g_return_val_if_fail (msg, NULL);
576         
577         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
578         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
579         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
580         self = MODEST_MSG_VIEW_WINDOW (obj);
581
582         priv->msg_uid = g_strdup (msg_uid);
583
584         parent_priv->ui_manager = gtk_ui_manager_new();
585         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
586
587         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
588         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
589
590         menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
591         toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
592
593         /* Add common actions */
594         gtk_action_group_add_actions (action_group,
595                                       modest_action_entries,
596                                       G_N_ELEMENTS (modest_action_entries),
597                                       obj);
598         gtk_action_group_add_toggle_actions (action_group,
599                                              modest_toggle_action_entries,
600                                              G_N_ELEMENTS (modest_toggle_action_entries),
601                                              obj);
602         gtk_action_group_add_toggle_actions (action_group,
603                                              msg_view_toggle_action_entries,
604                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
605                                              obj);
606         gtk_action_group_add_radio_actions (action_group,
607                                             msg_view_zoom_action_entries,
608                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
609                                             100,
610                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
611                                             obj);
612
613         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
614         g_object_unref (action_group);
615
616         /* Load the UI definition */
617         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
618                                          &error);
619         if (error) {
620                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
621                 g_error_free (error);
622                 error = NULL;
623         }
624         /* ****** */
625
626         /* Add common dimming rules */
627         modest_dimming_rules_group_add_rules (menu_rules_group, 
628                                               modest_msg_view_menu_dimming_entries,
629                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
630                                               self);
631         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
632                                               modest_msg_view_toolbar_dimming_entries,
633                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
634                                               self);
635
636         /* Insert dimming rules group for this window */
637         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
638         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
639         g_object_unref (menu_rules_group);
640         g_object_unref (toolbar_rules_group);
641
642         /* Add accelerators */
643         gtk_window_add_accel_group (GTK_WINDOW (obj), 
644                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
645         
646         /* Init window */
647         init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
648         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
649         
650         /* Set window icon */
651         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON); 
652         if (window_icon) {
653                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
654                 g_object_unref (window_icon);
655         }
656
657         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
658
659         g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
660                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
661         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
662                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
663         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
664                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
665         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
666                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
667         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
668                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
669
670         g_signal_connect (G_OBJECT (obj), "key-release-event",
671                           G_CALLBACK (modest_msg_view_window_key_release_event),
672                           NULL);
673
674         g_signal_connect (G_OBJECT (obj), "window-state-event",
675                           G_CALLBACK (modest_msg_view_window_window_state_event),
676                           NULL);
677
678         /* Mail Operation Queue */
679         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
680                                                        "queue-changed",
681                                                        G_CALLBACK (on_queue_changed),
682                                                        obj);
683
684         /* Account manager */
685         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
686                                                           "account_removed",
687                                                           G_CALLBACK(on_account_removed),
688                                                           obj);
689
690         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
691
692         priv->last_search = NULL;
693
694         /* Init the clipboard actions dim status */
695         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
696
697         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
698
699         /* Check toolbar dimming rules */
700         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
701
702         return MODEST_WINDOW(obj);
703 }
704
705
706 gboolean 
707 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
708 {
709         ModestMsgViewWindowPrivate *priv= NULL; 
710
711         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
712         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
713
714         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
715 }
716
717 TnyHeader*
718 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
719 {
720         ModestMsgViewWindowPrivate *priv= NULL; 
721         TnyMsg *msg = NULL;
722         TnyHeader *header = NULL;
723         GtkTreePath *path = NULL;
724         GtkTreeIter iter;
725
726         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
727         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
728
729         /* Message is not obtained from a treemodel (Attachment ?) */
730         if (priv->header_model == NULL) {
731                 msg = modest_msg_view_window_get_message (self);
732                 header = tny_msg_get_header (msg);
733                 g_object_unref (msg);
734                 return header;
735         }
736
737         /* Get current message iter */
738         path = gtk_tree_row_reference_get_path (priv->row_reference);
739         g_return_val_if_fail (path != NULL, NULL);
740         gtk_tree_model_get_iter (priv->header_model, 
741                                  &iter, 
742                                  path);
743
744         /* Get current message header */
745         gtk_tree_model_get (priv->header_model, &iter, 
746                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
747                             &header, -1);
748
749         gtk_tree_path_free (path);
750         return header;
751 }
752
753 TnyMsg*
754 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
755 {
756         ModestMsgView *msg_view;
757         ModestMsgViewWindowPrivate *priv;
758
759         g_return_val_if_fail (self, NULL);
760
761         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
762
763         msg_view = MODEST_MSG_VIEW (priv->msg_view);
764
765         return modest_msg_view_get_message (msg_view);
766 }
767
768 const gchar*
769 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
770 {
771         ModestMsgViewWindowPrivate *priv;
772
773         g_return_val_if_fail (self, NULL);
774         
775         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
776
777         return (const gchar*) priv->msg_uid;
778 }
779
780 static void 
781 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
782                                             gpointer data)
783 {
784         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
785         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
786         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
787         gboolean is_active;
788         GtkAction *action;
789
790         is_active = gtk_toggle_action_get_active (toggle);
791
792         if (is_active) {
793                 gtk_widget_show (priv->find_toolbar);
794                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
795         } else {
796                 gtk_widget_hide (priv->find_toolbar);
797         }
798
799         /* update the toggle buttons status */
800         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
801         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
802         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
803         modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
804         
805 }
806
807 static void
808 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
809                                            ModestMsgViewWindow *obj)
810 {
811         GtkToggleAction *toggle;
812         ModestWindowPrivate *parent_priv;
813         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
814         
815         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
816         gtk_toggle_action_set_active (toggle, FALSE);
817 }
818
819 static void
820 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
821                                            ModestMsgViewWindow *obj)
822 {
823         gchar *current_search;
824         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
825
826         if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
827                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
828                 return;
829         }
830
831         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
832
833         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
834                 g_free (current_search);
835                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
836                 return;
837         }
838
839         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
840                 gboolean result;
841                 g_free (priv->last_search);
842                 priv->last_search = g_strdup (current_search);
843                 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
844                                                  priv->last_search);
845                 if (!result) {
846                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
847                         g_free (priv->last_search);
848                         priv->last_search = NULL;
849                 } 
850         } else {
851                 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
852                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
853                         g_free (priv->last_search);
854                         priv->last_search = NULL;
855                 }
856         }
857         
858         g_free (current_search);
859                 
860 }
861
862 static void
863 modest_msg_view_window_set_zoom (ModestWindow *window,
864                                  gdouble zoom)
865 {
866         ModestMsgViewWindowPrivate *priv;
867      
868         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
869
870         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
871         modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
872 }
873
874 static gdouble
875 modest_msg_view_window_get_zoom (ModestWindow *window)
876 {
877         ModestMsgViewWindowPrivate *priv;
878      
879         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
880
881         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
882         return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
883 }
884
885 static gboolean
886 modest_msg_view_window_zoom_plus (ModestWindow *window)
887 {
888         ModestWindowPrivate *parent_priv;
889         GtkRadioAction *zoom_radio_action;
890         GSList *group, *node;
891
892         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
893         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
894                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
895
896         group = gtk_radio_action_get_group (zoom_radio_action);
897
898         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
899                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
900                 return FALSE;
901         }
902
903         for (node = group; node != NULL; node = g_slist_next (node)) {
904                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
905                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
906                         return TRUE;
907                 }
908         }
909         return FALSE;
910 }
911
912 static gboolean
913 modest_msg_view_window_zoom_minus (ModestWindow *window)
914 {
915         ModestWindowPrivate *parent_priv;
916         GtkRadioAction *zoom_radio_action;
917         GSList *group, *node;
918
919         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
920         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
921                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
922
923         group = gtk_radio_action_get_group (zoom_radio_action);
924
925         for (node = group; node != NULL; node = g_slist_next (node)) {
926                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
927                         if (node->next != NULL) {
928                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
929                                 return TRUE;
930                         } else {
931                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
932                                 return FALSE;
933                         }
934                         break;
935                 }
936         }
937         return FALSE;
938 }
939
940 static gboolean
941 modest_msg_view_window_key_release_event (GtkWidget *window,
942                                           GdkEventKey *event,
943                                           gpointer userdata)
944 {
945         if (event->type == GDK_KEY_RELEASE) {
946                 switch (event->keyval) {
947                 case GDK_Up:
948                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
949                         return TRUE;
950                         break;
951                 case GDK_Down:
952                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
953                         return TRUE;
954                         break;
955                 default:
956                         return FALSE;
957                         break;
958                 };
959         } else {
960                 return FALSE;
961         }
962 }
963
964 static void
965 modest_msg_view_window_scroll_up (ModestWindow *window)
966 {
967         ModestMsgViewWindowPrivate *priv;
968         gboolean return_value;
969
970         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
971         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
972 }
973
974 static void
975 modest_msg_view_window_scroll_down (ModestWindow *window)
976 {
977         ModestMsgViewWindowPrivate *priv;
978         gboolean return_value;
979
980         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
981         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
982 }
983
984 gboolean
985 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
986 {
987         GtkTreePath *path;
988         ModestMsgViewWindowPrivate *priv;
989         GtkTreeIter tmp_iter;
990         gboolean has_next = FALSE;
991
992         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
993         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
994
995         if (priv->header_model) {
996                 path = gtk_tree_row_reference_get_path (priv->row_reference);
997                 if (path == NULL) return FALSE;
998                 while (!has_next) {
999                         TnyHeader *header;
1000                         gtk_tree_path_next (path);
1001                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1002                                 break;
1003                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1004                                             &header, -1);
1005                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
1006                                 has_next = TRUE;
1007                                 break;
1008                         }       
1009                 }
1010                 gtk_tree_path_free (path);
1011                 return !has_next;
1012         } else {
1013                 return TRUE;
1014         }
1015         
1016 }
1017
1018 gboolean
1019 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1020 {
1021         ModestMsgViewWindowPrivate *priv;
1022
1023         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1024         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1025
1026         return priv->header_model != NULL;
1027 }
1028
1029 gboolean
1030 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1031 {
1032         GtkTreePath *path;
1033         ModestMsgViewWindowPrivate *priv;
1034         gboolean result;
1035         GtkTreeIter tmp_iter;
1036
1037         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1038         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1039
1040         if (priv->header_model) {
1041                 gchar * path_string;
1042                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1043                 if (!path)
1044                         return TRUE;
1045
1046                 path_string = gtk_tree_path_to_string (path);
1047                 result = (strcmp (path_string, "0")==0);
1048                 if (result) {
1049                         g_free (path_string);
1050                         gtk_tree_path_free (path);
1051                         return result;
1052                 }
1053
1054                 while (result) {
1055                         TnyHeader *header;
1056
1057                         gtk_tree_path_prev (path);
1058                         
1059                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1060                                 break;
1061                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1062                                             &header, -1);
1063                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
1064                                 result = FALSE;
1065                                 break;
1066                         }
1067
1068                         path_string = gtk_tree_path_to_string (path);
1069                         if (strcmp(path_string, "0")==0) {
1070                                 g_free (path_string);
1071                                 break;
1072                         }
1073                         g_free (path_string);
1074                 }
1075                 gtk_tree_path_free (path);
1076                 return result;
1077         } else {
1078                 return TRUE;
1079         }
1080         
1081 }
1082
1083 /**
1084  * Reads the message whose summary item is @header. It takes care of
1085  * several things, among others:
1086  *
1087  * If the message was not previously downloaded then ask the user
1088  * before downloading. If there is no connection launch the connection
1089  * dialog. Update toolbar dimming rules.
1090  *
1091  * Returns: TRUE if the mail operation was started, otherwise if the
1092  * user do not want to download the message, or if the user do not
1093  * want to connect, then the operation is not issued
1094  **/
1095 static gboolean
1096 message_reader (ModestMsgViewWindow *window,
1097                 ModestMsgViewWindowPrivate *priv,
1098                 TnyHeader *header,
1099                 GtkTreePath *path)
1100 {
1101         ModestMailOperation *mail_op = NULL;
1102         ModestMailOperationTypeOperation op_type;
1103
1104         g_return_val_if_fail (path != NULL, FALSE);
1105
1106         /* Msg download completed */
1107         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1108                 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1109         } else {
1110                 TnyFolder *folder;
1111                 GtkResponseType response;
1112
1113                 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1114
1115                 /* Ask the user if he wants to download the message */
1116                 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1117                                                                     _("mcen_nc_get_msg"));
1118                 if (response == GTK_RESPONSE_CANCEL)
1119                         return FALSE;
1120                 
1121                 /* Offer the connection dialog if necessary */
1122                 /* FIXME: should this stuff go directly to the mail
1123                    operation instead of spread it all over the
1124                    code? */
1125                 folder = tny_header_get_folder (header);
1126                 if (folder) {
1127                         if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1128                                 g_object_unref (folder);
1129                                 return FALSE;
1130                         }
1131                         g_object_unref (folder);
1132                 }
1133         }
1134
1135         /* New mail operation */
1136         mail_op = modest_mail_operation_new_with_error_handling (op_type, 
1137                                                                  G_OBJECT(window),
1138                                                                  modest_ui_actions_get_msgs_full_error_handler, 
1139                                                                  NULL);
1140                                 
1141         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1142         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1143         g_object_unref (mail_op);
1144
1145         /* Update toolbar dimming rules */
1146         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1147
1148         return TRUE;
1149 }
1150
1151 gboolean        
1152 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1153 {
1154         ModestMsgViewWindowPrivate *priv;
1155         GtkTreePath *path= NULL;
1156         GtkTreeIter tmp_iter;
1157         TnyHeader *header;
1158         gboolean retval = TRUE;
1159
1160         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1161         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1162
1163         /* Update the next row reference if it's not valid. This could
1164            happen if for example the header which it was pointing to,
1165            was deleted. The best place to do it is in the row-deleted
1166            handler but the tinymail model do not work like the glib
1167            tree models and reports the deletion when the row is still
1168            there */
1169         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1170                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1171                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1172                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1173                 }
1174         }
1175         if (priv->next_row_reference)
1176                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1177         if (path == NULL)
1178                 return FALSE;
1179
1180         gtk_tree_model_get_iter (priv->header_model,
1181                                  &tmp_iter,
1182                                  path);
1183
1184         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1185                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1186                             &header, -1);
1187         
1188         /* Read the message & show it */
1189         if (!message_reader (window, priv, header, path)) {
1190                 retval = FALSE;
1191                 gtk_tree_path_free (path);
1192         }
1193
1194         /* Free */
1195         g_object_unref (header);
1196
1197         return retval;          
1198 }
1199
1200 gboolean 
1201 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1202 {
1203         ModestMsgViewWindowPrivate *priv = NULL;
1204         TnyHeader *header = NULL;
1205         GtkTreeIter iter;
1206         GtkTreePath *path;
1207
1208         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1209         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1210
1211         /* Check that the model is not empty */
1212         if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1213                 return FALSE;
1214
1215         /* Get the header */
1216         gtk_tree_model_get (priv->header_model, 
1217                             &iter, 
1218                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1219                             &header, -1);
1220         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1221         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1222                 g_object_unref (header);
1223                 return modest_msg_view_window_select_next_message (self);
1224         }
1225         
1226         path = gtk_tree_model_get_path (priv->header_model, &iter);
1227         
1228         /* Read the message & show it */
1229         message_reader (self, priv, header, path);
1230         
1231         /* Free */
1232         g_object_unref (header);
1233
1234         return TRUE;
1235 }
1236  
1237 gboolean        
1238 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1239 {
1240         ModestMsgViewWindowPrivate *priv = NULL;
1241         GtkTreePath *path;
1242
1243         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1244         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1245
1246         /* Return inmediatly if there is no header model */
1247         if (!priv->header_model)
1248                 return FALSE;
1249
1250         path = gtk_tree_row_reference_get_path (priv->row_reference);
1251         while (gtk_tree_path_prev (path)) {
1252                 TnyHeader *header;
1253                 GtkTreeIter iter;
1254
1255                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1256                 gtk_tree_model_get (priv->header_model, &iter, 
1257                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1258                                     &header, -1);
1259                 if (!header)
1260                         break;
1261                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1262                         g_object_unref (header);
1263                         continue;
1264                 }
1265
1266                 /* Read the message & show it */
1267                 if (!message_reader (window, priv, header, path)) {
1268                         g_object_unref (header);
1269                         break;
1270                 }
1271
1272                 g_object_unref (header);
1273
1274                 return TRUE;
1275         }
1276
1277         gtk_tree_path_free (path);
1278         return FALSE;
1279 }
1280
1281 static void
1282 view_msg_cb (ModestMailOperation *mail_op, 
1283              TnyHeader *header, 
1284              TnyMsg *msg, 
1285              gpointer user_data)
1286 {
1287         ModestMsgViewWindow *self = NULL;
1288         ModestMsgViewWindowPrivate *priv = NULL;
1289         GtkTreePath *path;
1290
1291         /* If there was any error */
1292         path = (GtkTreePath *) user_data;
1293         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1294                 gtk_tree_path_free (path);                      
1295                 return;
1296         }
1297
1298         /* Get the window */ 
1299         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1300         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1301         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1302
1303         /* Update the row reference */
1304         gtk_tree_row_reference_free (priv->row_reference);
1305         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1306         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1307         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1308         gtk_tree_path_free (path);
1309
1310         /* Mark header as read */
1311         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1312                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1313
1314         /* Set new message */
1315         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1316         modest_msg_view_window_update_priority (self);
1317         update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1318         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1319
1320         /* Set the new message uid of the window  */
1321         if (priv->msg_uid) {
1322                 g_free (priv->msg_uid);
1323                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1324         }
1325
1326         /* Notify the observers */
1327         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1328                        0, priv->header_model, priv->row_reference);
1329
1330         /* Free new references */
1331         g_object_unref (self);
1332 }
1333
1334 TnyFolderType
1335 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1336 {
1337         ModestMsgViewWindowPrivate *priv;
1338         TnyMsg *msg;
1339         TnyFolderType folder_type;
1340
1341         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1342
1343         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1344
1345         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1346         if (msg) {
1347                 TnyFolder *folder;
1348
1349                 folder = tny_msg_get_folder (msg);
1350                 
1351                 if (folder) {
1352                         folder_type = tny_folder_get_folder_type (folder);
1353                         
1354                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1355                                 const gchar *fname = tny_folder_get_name (folder);
1356                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1357                         }
1358
1359                         g_object_unref (folder);
1360                 }
1361                 g_object_unref (msg);
1362         }
1363
1364         return folder_type;
1365 }
1366
1367
1368 static void
1369 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1370 {
1371         ModestMsgViewWindowPrivate *priv;
1372         TnyHeaderFlags flags = 0;
1373
1374         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1375
1376         if (priv->header_model) {
1377                 TnyHeader *header;
1378                 GtkTreeIter iter;
1379                 GtkTreePath *path = NULL;
1380
1381                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1382                 g_return_if_fail (path != NULL);
1383                 gtk_tree_model_get_iter (priv->header_model, 
1384                                          &iter, 
1385                                          gtk_tree_row_reference_get_path (priv->row_reference));
1386
1387                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1388                                     &header, -1);
1389                 flags = tny_header_get_flags (header);
1390                 gtk_tree_path_free (path);
1391         }
1392
1393         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1394
1395 }
1396
1397 static gboolean
1398 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1399 {
1400         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1401                 ModestWindowPrivate *parent_priv;
1402                 ModestWindowMgr *mgr;
1403                 gboolean is_fullscreen;
1404                 GtkAction *fs_toggle_action;
1405                 gboolean active;
1406
1407                 mgr = modest_runtime_get_window_mgr ();
1408                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1409
1410                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1411                 
1412                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1413                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1414                 if (is_fullscreen != active) {
1415                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1416                 }
1417         }
1418
1419         return FALSE;
1420
1421 }
1422
1423 void
1424 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1425 {
1426                 ModestWindowPrivate *parent_priv;
1427                 GtkAction *fs_toggle_action;
1428                 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1429                 
1430                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1431                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1432                                               !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1433 }
1434
1435 static void
1436 set_homogeneous (GtkWidget *widget,
1437                  gpointer data)
1438 {
1439         if (GTK_IS_TOOL_ITEM (widget)) {
1440                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1441                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1442         }
1443 }
1444
1445 static void
1446 modest_msg_view_window_show_toolbar (ModestWindow *self,
1447                                      gboolean show_toolbar)
1448 {
1449         ModestMsgViewWindowPrivate *priv = NULL;
1450         ModestWindowPrivate *parent_priv;
1451         GtkWidget *reply_button = NULL, *menu = NULL;
1452         GtkWidget *placeholder = NULL;
1453         gint insert_index;
1454         
1455         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1456         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1457
1458         /* Set optimized view status */
1459         priv->optimized_view = !show_toolbar;
1460
1461         if (!parent_priv->toolbar) {
1462                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1463                                                                   "/ToolBar");
1464
1465                 /* Set homogeneous toolbar */
1466                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
1467                                        set_homogeneous, NULL);
1468
1469                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1470                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1471                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1472                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1473                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1474                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1475                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1476                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1477
1478                 /* Add ProgressBar (Transfer toolbar) */ 
1479                 priv->progress_bar = modest_progress_bar_widget_new ();
1480                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1481                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1482                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1483                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1484                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1485                 
1486                 /* Connect cancel 'clicked' signal to abort progress mode */
1487                 g_signal_connect(priv->cancel_toolitem, "clicked",
1488                                  G_CALLBACK(cancel_progressbar),
1489                                  self);
1490                 
1491                 /* Add it to the observers list */
1492                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1493
1494                 /* Add to window */
1495                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
1496                                            GTK_TOOLBAR (parent_priv->toolbar));
1497
1498
1499                 /* Set reply button tap and hold menu */        
1500                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1501                                                           "/ToolBar/ToolbarMessageReply");
1502                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1503                                                   "/ToolbarReplyCSM");
1504                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1505         }
1506
1507         if (show_toolbar) {
1508                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
1509                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1510                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1511                 
1512                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1513                 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);                   
1514                 
1515         } else {
1516                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1517                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1518         }
1519 }
1520
1521 static void 
1522 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1523                                                GdkEvent *event,
1524                                                ModestMsgViewWindow *window)
1525 {
1526         ModestWindowPrivate *parent_priv;
1527 /*      GtkAction *action; */
1528         gboolean is_address;
1529         gchar *selection;
1530         GtkWidget *focused;
1531
1532         if (!GTK_WIDGET_VISIBLE (window))
1533                 return;
1534
1535         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1536         selection = gtk_clipboard_wait_for_text (clipboard);
1537
1538         is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1539         
1540 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1541 /*      gtk_action_set_sensitive (action, is_address); */
1542
1543         focused = gtk_window_get_focus (GTK_WINDOW (window));
1544
1545 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1546 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1547
1548 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1549 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1550
1551         g_free (selection);
1552 /*      modest_msg_view_window_update_dimmed (window); */
1553         
1554 }
1555
1556 gboolean 
1557 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1558 {
1559         ModestMsgViewWindowPrivate *priv;
1560         
1561         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
1562         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1563
1564         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1565 }
1566
1567 static void
1568 cancel_progressbar (GtkToolButton *toolbutton,
1569                     ModestMsgViewWindow *self)
1570 {
1571         GSList *tmp;
1572         ModestMsgViewWindowPrivate *priv;
1573         
1574         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1575
1576         /* Get operation observers and cancel its current operation */
1577         tmp = priv->progress_widgets;
1578         while (tmp) {
1579                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1580                 tmp=g_slist_next(tmp);
1581         }
1582 }
1583 static gboolean
1584 observers_empty (ModestMsgViewWindow *self)
1585 {
1586         GSList *tmp = NULL;
1587         ModestMsgViewWindowPrivate *priv;
1588         gboolean is_empty = TRUE;
1589         guint pending_ops = 0;
1590  
1591         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1592         tmp = priv->progress_widgets;
1593
1594         /* Check all observers */
1595         while (tmp && is_empty)  {
1596                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
1597                 is_empty = pending_ops == 0;
1598                 
1599                 tmp = g_slist_next(tmp);
1600         }
1601         
1602         return is_empty;
1603 }
1604
1605 static void
1606 on_account_removed (TnyAccountStore *account_store, 
1607                     TnyAccount *account,
1608                     gpointer user_data)
1609 {
1610         /* Do nothing if it's a transport account, because we only
1611            show the messages of a store account */
1612         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
1613                 const gchar *parent_acc = NULL;
1614                 const gchar *our_acc = NULL;
1615
1616                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
1617                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1618
1619                 /* Close this window if I'm showing a message of the removed account */
1620                 if (strcmp (parent_acc, our_acc) == 0)
1621                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
1622         }
1623 }
1624
1625 static void
1626 on_queue_changed (ModestMailOperationQueue *queue,
1627                   ModestMailOperation *mail_op,
1628                   ModestMailOperationQueueNotification type,
1629                   ModestMsgViewWindow *self)
1630 {
1631         GSList *tmp;
1632         ModestMsgViewWindowPrivate *priv;
1633         ModestMailOperationTypeOperation op_type;
1634         ModestToolBarModes mode;
1635         
1636         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1637         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1638
1639         /* If this operations was created by another window, do nothing */
1640         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
1641             return;
1642
1643         /* Get toolbar mode from operation id*/
1644         op_type = modest_mail_operation_get_type_operation (mail_op);
1645         switch (op_type) {
1646 /*      case MODEST_MAIL_OPERATION_TYPE_SEND: */
1647         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
1648         case MODEST_MAIL_OPERATION_TYPE_OPEN:
1649                 mode = TOOLBAR_MODE_TRANSFER;
1650                 break;
1651         default:
1652                 mode = TOOLBAR_MODE_NORMAL;
1653                 
1654         }
1655                 
1656         /* Add operation observers and change toolbar if neccessary*/
1657         tmp = priv->progress_widgets;
1658         switch (type) {
1659         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
1660                 if (mode == TOOLBAR_MODE_TRANSFER) {
1661                         /* Enable transfer toolbar mode */
1662                         set_toolbar_transfer_mode(self);
1663                         while (tmp) {
1664                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1665                                                                       mail_op);
1666                                 tmp = g_slist_next (tmp);
1667                         }
1668                         
1669                 }
1670                 break;
1671         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
1672                 if (mode == TOOLBAR_MODE_TRANSFER) {
1673                         while (tmp) {
1674                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1675                                                                  mail_op);
1676                                 tmp = g_slist_next (tmp);
1677                                 
1678                         }
1679
1680                         /* If no more operations are being observed, NORMAL mode is enabled again */
1681                         if (observers_empty (self)) {
1682                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
1683                         }
1684                 }
1685                 break;
1686         }
1687 }
1688
1689 GList *
1690 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
1691 {
1692         ModestMsgViewWindowPrivate *priv;
1693         GList *selected_attachments = NULL;
1694         
1695         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
1696         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
1697
1698         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1699         
1700         return selected_attachments;
1701 }
1702
1703 void
1704 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
1705 {
1706         ModestMsgViewWindowPrivate *priv;
1707         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1708         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
1709
1710         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1711
1712         if (mime_part == NULL) {
1713                 gboolean error = FALSE;
1714                 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1715                 if (selected_attachments == NULL) {
1716                         error = TRUE;
1717                 } else if (g_list_length (selected_attachments) > 1) {
1718                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
1719                         error = TRUE;
1720                 } else {
1721                         mime_part = (TnyMimePart *) selected_attachments->data;
1722                         g_object_ref (mime_part);
1723                 }
1724                 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
1725                 g_list_free (selected_attachments);
1726
1727                 if (error)
1728                         return;
1729         } else {
1730                 g_object_ref (mime_part);
1731         }
1732
1733         if (tny_mime_part_is_purged (mime_part)) {
1734                 g_object_unref (mime_part);
1735                 hildon_banner_show_information (NULL, NULL, _("mail_ib_attach_not_local"));
1736                 return;
1737         }
1738
1739         if (!TNY_IS_MSG (mime_part)) {
1740                 gchar *filepath = NULL;
1741                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
1742                 gchar *extension = NULL;
1743                 TnyFsStream *temp_stream = NULL;
1744
1745                 if (att_filename) {
1746                         extension = g_strrstr (att_filename, ".");
1747                         if (extension != NULL)
1748                                 extension++;
1749                 }
1750
1751                 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
1752
1753                 if (temp_stream) {
1754                         const gchar *content_type;
1755                         content_type = tny_mime_part_get_content_type (mime_part);
1756                         tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
1757                         
1758                         modest_platform_activate_file (filepath, content_type);
1759                         g_object_unref (temp_stream);
1760                         g_free (filepath);
1761                         /* TODO: delete temporary file */
1762                 }
1763         } else {
1764                 /* message attachment */
1765                 TnyHeader *header = NULL;
1766                 ModestWindowMgr *mgr;
1767                 ModestWindow *msg_win = NULL;
1768                 gboolean found;
1769                 
1770                 header = tny_msg_get_header (TNY_MSG (mime_part));
1771                 mgr = modest_runtime_get_window_mgr ();         
1772                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
1773
1774                 if (found) {
1775                         if (msg_win)                            /* there is already a window for this uid; top it */
1776                                 gtk_window_present (GTK_WINDOW(msg_win));
1777                         else 
1778                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
1779                                  * thus, we don't do anything */
1780                                 g_warning ("window for is already being created");
1781                 } else { 
1782                         /* it's not found, so create a new window for it */
1783                         modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
1784                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
1785                         if (!account)
1786                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
1787                         msg_win = modest_msg_view_window_new (TNY_MSG (mime_part), account, NULL);
1788                         modest_window_mgr_register_window (mgr, msg_win);
1789                         gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
1790                         gtk_widget_show_all (GTK_WIDGET (msg_win));
1791                 }
1792         }
1793         g_object_unref (mime_part);
1794 }
1795
1796 typedef struct
1797 {
1798         gchar *filename;
1799         TnyMimePart *part;
1800 } SaveMimePartPair;
1801
1802 typedef struct
1803 {
1804         GList *pairs;
1805         GtkWidget *banner;
1806         gboolean result;
1807 } SaveMimePartInfo;
1808
1809 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
1810 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
1811 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
1812 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
1813
1814 static void 
1815 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
1816 {
1817         
1818         GList *node;
1819         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
1820                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
1821                 g_free (pair->filename);
1822                 g_object_unref (pair->part);
1823                 g_slice_free (SaveMimePartPair, pair);
1824         }
1825         g_list_free (info->pairs);
1826         info->pairs = NULL;
1827         if (with_struct) {
1828                 gtk_widget_destroy (info->banner);
1829                 g_object_unref (info->banner);
1830                 g_slice_free (SaveMimePartInfo, info);
1831         }
1832 }
1833
1834 static gboolean
1835 idle_save_mime_part_show_result (SaveMimePartInfo *info)
1836 {
1837         if (info->pairs != NULL) {
1838                 gdk_threads_enter ();
1839                 save_mime_parts_to_file_with_checks (info);
1840                 gdk_threads_leave ();
1841         } else {
1842                 gboolean result;
1843                 result = info->result;
1844
1845                 gdk_threads_enter ();
1846                 save_mime_part_info_free (info, TRUE);
1847                 if (result) {
1848                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
1849                 } else {
1850                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
1851                 }
1852                 gdk_threads_leave ();
1853         }
1854
1855         return FALSE;
1856 }
1857
1858 static gpointer
1859 save_mime_part_to_file (SaveMimePartInfo *info)
1860 {
1861         GnomeVFSResult result;
1862         GnomeVFSHandle *handle;
1863         TnyStream *stream;
1864         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
1865
1866         result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
1867         if (result == GNOME_VFS_OK) {
1868                 stream = tny_vfs_stream_new (handle);
1869                 tny_mime_part_decode_to_stream (pair->part, stream);
1870                 g_object_unref (G_OBJECT (stream));
1871                 g_object_unref (pair->part);
1872                 g_slice_free (SaveMimePartPair, pair);
1873                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
1874                 info->result = TRUE;
1875         } else {
1876                 save_mime_part_info_free (info, FALSE);
1877                 info->result = FALSE;
1878         }
1879
1880         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
1881         return NULL;
1882 }
1883
1884 static void
1885 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
1886 {
1887         SaveMimePartPair *pair;
1888         gboolean is_ok = TRUE;
1889
1890         pair = info->pairs->data;
1891         if (modest_maemo_utils_file_exists (pair->filename)) {
1892                 GtkWidget *confirm_overwrite_dialog;
1893                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
1894                                                                          _("emev_nc_replace_files"));
1895                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
1896                         is_ok = FALSE;
1897                 }
1898                 gtk_widget_destroy (confirm_overwrite_dialog);
1899         }
1900
1901         if (!is_ok) {
1902                 save_mime_part_info_free (info, TRUE);
1903         } else {
1904                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
1905         }
1906
1907 }
1908
1909
1910 void
1911 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
1912 {
1913         gboolean clean_list = FALSE;
1914         ModestMsgViewWindowPrivate *priv;
1915         GList *files_to_save = NULL;
1916         GtkWidget *save_dialog = NULL;
1917         gchar *folder = NULL;
1918         gboolean canceled = FALSE;
1919         const gchar *filename = NULL;
1920         gchar *save_multiple_str = NULL;
1921
1922         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1923         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1924
1925         if (mime_parts == NULL) {
1926                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1927                 if (mime_parts == NULL)
1928                         return;
1929                 clean_list = TRUE;
1930         }
1931
1932         /* prepare dialog */
1933         if (mime_parts->next == NULL) {
1934                 /* only one attachment selected */
1935                 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
1936                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
1937                         filename = tny_mime_part_get_filename (mime_part);
1938                 } else {
1939                         g_warning ("Tried to save a non-file attachment");
1940                         canceled = TRUE;
1941                 }
1942         } else {
1943                 save_multiple_str = g_strdup_printf (_("FIXME: %d attachments"), 
1944                                                      g_list_length (mime_parts));
1945         }
1946         
1947         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
1948                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
1949
1950         /* set folder */
1951         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
1952         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
1953         g_free (folder);
1954
1955         /* set filename */
1956         if (filename != NULL)
1957                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
1958                                                    filename);
1959
1960         /* if multiple, set multiple string */
1961         if (save_multiple_str) {
1962                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
1963         }
1964                 
1965         /* show dialog */
1966         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
1967                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
1968
1969                 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
1970                         hildon_banner_show_information 
1971                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
1972                 } else {
1973                         GList *node = NULL;
1974
1975                         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
1976                                 TnyMimePart *mime_part = (TnyMimePart *) node->data;
1977                                 
1978                                 if (tny_mime_part_is_attachment (mime_part)) {
1979                                         SaveMimePartPair *pair;
1980
1981                                         if ((mime_parts->next != NULL) &&
1982                                             (tny_mime_part_get_filename (mime_part) == NULL))
1983                                                 continue;
1984                                         
1985                                         pair = g_slice_new0 (SaveMimePartPair);
1986                                         if (mime_parts->next == NULL) {
1987                                                 pair->filename = g_strdup (chooser_uri);
1988                                         } else {
1989                                                 pair->filename = 
1990                                                         g_build_filename (chooser_uri,
1991                                                                           tny_mime_part_get_filename (mime_part), NULL);
1992                                         }
1993                                         pair->part = g_object_ref (mime_part);
1994                                         files_to_save = g_list_prepend (files_to_save, pair);
1995                                 }
1996                         }
1997                 }
1998                 g_free (chooser_uri);
1999         }
2000
2001         gtk_widget_destroy (save_dialog);
2002
2003         if (clean_list) {
2004                 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2005                 g_list_free (mime_parts);
2006         }
2007
2008         if (files_to_save != NULL) {
2009                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2010                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2011                                                                   _CS("sfil_ib_saving"));
2012                 info->pairs = files_to_save;
2013                 info->banner = banner;
2014                 info->result = TRUE;
2015                 g_object_ref (banner);
2016                 save_mime_parts_to_file_with_checks (info);
2017         }
2018 }
2019
2020 void
2021 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2022 {
2023         ModestMsgViewWindowPrivate *priv;
2024         GList *mime_parts = NULL, *node;
2025         gchar *confirmation_message;
2026         gint response;
2027         gint n_attachments;
2028         TnyMsg *msg;
2029 /*      TnyFolder *folder; */
2030
2031         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2032         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2033
2034         if (get_all)
2035                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2036         else
2037                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2038                 
2039         /* Remove already purged messages from mime parts list */
2040         node = mime_parts;
2041         while (node != NULL) {
2042                 TnyMimePart *part = TNY_MIME_PART (node->data);
2043                 if (tny_mime_part_is_purged (part)) {
2044                         GList *deleted_node = node;
2045                         node = g_list_next (node);
2046                         g_object_unref (part);
2047                         mime_parts = g_list_delete_link (mime_parts, deleted_node);
2048                 } else {
2049                         node = g_list_next (node);
2050                 }
2051         }
2052
2053         if (mime_parts == NULL)
2054                 return;
2055
2056         n_attachments = g_list_length (mime_parts);
2057         if (n_attachments == 1) {
2058                 const gchar *filename;
2059
2060                 if (TNY_IS_MSG (mime_parts->data)) {
2061                         TnyHeader *header;
2062                         header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2063                         filename = tny_header_get_subject (header);
2064                         g_object_unref (header);
2065                         if (filename == NULL)
2066                                 filename = _("mail_va_no_subject");
2067                 } else {
2068                         filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2069                 }
2070                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2071         } else {
2072                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2073                                                                  "mcen_nc_purge_files_text", 
2074                                                                  n_attachments), n_attachments);
2075         }
2076         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2077                                                             confirmation_message);
2078         g_free (confirmation_message);
2079
2080         if (response != GTK_RESPONSE_OK)
2081                 return;
2082
2083 /*      folder = tny_msg_get_folder (msg); */
2084 /*      tny_msg_uncache_attachments (msg); */
2085 /*      tny_folder_refresh (folder, NULL); */
2086 /*      g_object_unref (folder); */
2087         
2088         modest_platform_information_banner (NULL, NULL, _("mcen_ib_removing_attachment"));
2089
2090         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2091                 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2092 /*              modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2093         }
2094
2095         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2096         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2097         tny_msg_rewrite_cache (msg);
2098         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2099
2100         g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2101         g_list_free (mime_parts);
2102
2103
2104 }
2105
2106
2107 static void
2108 update_window_title (ModestMsgViewWindow *window)
2109 {
2110         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2111         TnyMsg *msg = NULL;
2112         TnyHeader *header = NULL;
2113         const gchar *subject = NULL;
2114
2115         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2116         if (msg != NULL) {
2117                 header = tny_msg_get_header (msg);
2118                 subject = tny_header_get_subject (header);
2119                 g_object_unref (msg);
2120         }
2121
2122         if ((subject == NULL)||(subject[0] == '\0'))
2123                 subject = _("mail_va_no_subject");
2124
2125         gtk_window_set_title (GTK_WINDOW (window), subject);
2126 }