* src/maemo/modest-msg-view-window.c:
[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         gboolean already_showing = FALSE;
1104         ModestWindow *msg_window = NULL;
1105         ModestWindowMgr *mgr;
1106
1107         g_return_val_if_fail (path != NULL, FALSE);
1108
1109         mgr = modest_runtime_get_window_mgr ();
1110         already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1111         if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1112                 gboolean retval;
1113                 if (msg_window)
1114                         gtk_window_present (GTK_WINDOW (msg_window));
1115                 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1116                 return TRUE;
1117         }
1118
1119         /* Msg download completed */
1120         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1121                 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1122         } else {
1123                 TnyFolder *folder;
1124                 GtkResponseType response;
1125
1126                 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1127
1128                 /* Ask the user if he wants to download the message */
1129                 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1130                                                                     _("mcen_nc_get_msg"));
1131                 if (response == GTK_RESPONSE_CANCEL)
1132                         return FALSE;
1133                 
1134                 /* Offer the connection dialog if necessary */
1135                 /* FIXME: should this stuff go directly to the mail
1136                    operation instead of spread it all over the
1137                    code? */
1138                 folder = tny_header_get_folder (header);
1139                 if (folder) {
1140                         if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1141                                 g_object_unref (folder);
1142                                 return FALSE;
1143                         }
1144                         g_object_unref (folder);
1145                 }
1146         }
1147
1148         /* New mail operation */
1149         mail_op = modest_mail_operation_new_with_error_handling (op_type, 
1150                                                                  G_OBJECT(window),
1151                                                                  modest_ui_actions_get_msgs_full_error_handler, 
1152                                                                  NULL);
1153                                 
1154         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1155         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1156         g_object_unref (mail_op);
1157
1158         /* Update toolbar dimming rules */
1159         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1160
1161         return TRUE;
1162 }
1163
1164 gboolean        
1165 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1166 {
1167         ModestMsgViewWindowPrivate *priv;
1168         GtkTreePath *path= NULL;
1169         GtkTreeIter tmp_iter;
1170         TnyHeader *header;
1171         gboolean retval = TRUE;
1172
1173         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1174         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1175
1176         /* Update the next row reference if it's not valid. This could
1177            happen if for example the header which it was pointing to,
1178            was deleted. The best place to do it is in the row-deleted
1179            handler but the tinymail model do not work like the glib
1180            tree models and reports the deletion when the row is still
1181            there */
1182         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1183                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1184                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1185                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1186                 }
1187         }
1188         if (priv->next_row_reference)
1189                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1190         if (path == NULL)
1191                 return FALSE;
1192
1193         gtk_tree_model_get_iter (priv->header_model,
1194                                  &tmp_iter,
1195                                  path);
1196
1197         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1198                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1199                             &header, -1);
1200         
1201         /* Read the message & show it */
1202         if (!message_reader (window, priv, header, path)) {
1203                 retval = FALSE;
1204                 gtk_tree_path_free (path);
1205         }
1206
1207         /* Free */
1208         g_object_unref (header);
1209
1210         return retval;          
1211 }
1212
1213 gboolean 
1214 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1215 {
1216         ModestMsgViewWindowPrivate *priv = NULL;
1217         TnyHeader *header = NULL;
1218         GtkTreeIter iter;
1219         GtkTreePath *path;
1220
1221         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1222         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1223
1224         /* Check that the model is not empty */
1225         if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1226                 return FALSE;
1227
1228         /* Get the header */
1229         gtk_tree_model_get (priv->header_model, 
1230                             &iter, 
1231                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1232                             &header, -1);
1233         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1234         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1235                 g_object_unref (header);
1236                 return modest_msg_view_window_select_next_message (self);
1237         }
1238         
1239         path = gtk_tree_model_get_path (priv->header_model, &iter);
1240         
1241         /* Read the message & show it */
1242         message_reader (self, priv, header, path);
1243         
1244         /* Free */
1245         g_object_unref (header);
1246
1247         return TRUE;
1248 }
1249  
1250 gboolean        
1251 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1252 {
1253         ModestMsgViewWindowPrivate *priv = NULL;
1254         GtkTreePath *path;
1255
1256         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1257         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1258
1259         /* Return inmediatly if there is no header model */
1260         if (!priv->header_model)
1261                 return FALSE;
1262
1263         path = gtk_tree_row_reference_get_path (priv->row_reference);
1264         while (gtk_tree_path_prev (path)) {
1265                 TnyHeader *header;
1266                 GtkTreeIter iter;
1267
1268                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1269                 gtk_tree_model_get (priv->header_model, &iter, 
1270                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1271                                     &header, -1);
1272                 if (!header)
1273                         break;
1274                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1275                         g_object_unref (header);
1276                         continue;
1277                 }
1278
1279                 /* Read the message & show it */
1280                 if (!message_reader (window, priv, header, path)) {
1281                         g_object_unref (header);
1282                         break;
1283                 }
1284
1285                 g_object_unref (header);
1286
1287                 return TRUE;
1288         }
1289
1290         gtk_tree_path_free (path);
1291         return FALSE;
1292 }
1293
1294 static void
1295 view_msg_cb (ModestMailOperation *mail_op, 
1296              TnyHeader *header, 
1297              TnyMsg *msg, 
1298              gpointer user_data)
1299 {
1300         ModestMsgViewWindow *self = NULL;
1301         ModestMsgViewWindowPrivate *priv = NULL;
1302         GtkTreePath *path;
1303
1304         /* If there was any error */
1305         path = (GtkTreePath *) user_data;
1306         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1307                 gtk_tree_path_free (path);                      
1308                 return;
1309         }
1310
1311         /* Get the window */ 
1312         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1313         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1314         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1315
1316         /* Update the row reference */
1317         gtk_tree_row_reference_free (priv->row_reference);
1318         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1319         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1320         select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1321         gtk_tree_path_free (path);
1322
1323         /* Mark header as read */
1324         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1325                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1326
1327         /* Set new message */
1328         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1329         modest_msg_view_window_update_priority (self);
1330         update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1331         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1332
1333         /* Set the new message uid of the window  */
1334         if (priv->msg_uid) {
1335                 g_free (priv->msg_uid);
1336                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1337         }
1338
1339         /* Notify the observers */
1340         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
1341                        0, priv->header_model, priv->row_reference);
1342
1343         /* Free new references */
1344         g_object_unref (self);
1345 }
1346
1347 TnyFolderType
1348 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1349 {
1350         ModestMsgViewWindowPrivate *priv;
1351         TnyMsg *msg;
1352         TnyFolderType folder_type;
1353
1354         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1355
1356         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1357
1358         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1359         if (msg) {
1360                 TnyFolder *folder;
1361
1362                 folder = tny_msg_get_folder (msg);
1363                 
1364                 if (folder) {
1365                         folder_type = tny_folder_get_folder_type (folder);
1366                         
1367                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1368                                 const gchar *fname = tny_folder_get_name (folder);
1369                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1370                         }
1371
1372                         g_object_unref (folder);
1373                 }
1374                 g_object_unref (msg);
1375         }
1376
1377         return folder_type;
1378 }
1379
1380
1381 static void
1382 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1383 {
1384         ModestMsgViewWindowPrivate *priv;
1385         TnyHeaderFlags flags = 0;
1386
1387         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1388
1389         if (priv->header_model) {
1390                 TnyHeader *header;
1391                 GtkTreeIter iter;
1392                 GtkTreePath *path = NULL;
1393
1394                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1395                 g_return_if_fail (path != NULL);
1396                 gtk_tree_model_get_iter (priv->header_model, 
1397                                          &iter, 
1398                                          gtk_tree_row_reference_get_path (priv->row_reference));
1399
1400                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1401                                     &header, -1);
1402                 flags = tny_header_get_flags (header);
1403                 gtk_tree_path_free (path);
1404         }
1405
1406         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1407
1408 }
1409
1410 static gboolean
1411 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1412 {
1413         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1414                 ModestWindowPrivate *parent_priv;
1415                 ModestWindowMgr *mgr;
1416                 gboolean is_fullscreen;
1417                 GtkAction *fs_toggle_action;
1418                 gboolean active;
1419
1420                 mgr = modest_runtime_get_window_mgr ();
1421                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1422
1423                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1424                 
1425                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1426                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1427                 if (is_fullscreen != active) {
1428                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1429                 }
1430         }
1431
1432         return FALSE;
1433
1434 }
1435
1436 void
1437 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1438 {
1439                 ModestWindowPrivate *parent_priv;
1440                 GtkAction *fs_toggle_action;
1441                 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1442                 
1443                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1444                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1445                                               !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1446 }
1447
1448 static void
1449 set_homogeneous (GtkWidget *widget,
1450                  gpointer data)
1451 {
1452         if (GTK_IS_TOOL_ITEM (widget)) {
1453                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1454                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1455         }
1456 }
1457
1458 static void
1459 modest_msg_view_window_show_toolbar (ModestWindow *self,
1460                                      gboolean show_toolbar)
1461 {
1462         ModestMsgViewWindowPrivate *priv = NULL;
1463         ModestWindowPrivate *parent_priv;
1464         GtkWidget *reply_button = NULL, *menu = NULL;
1465         GtkWidget *placeholder = NULL;
1466         gint insert_index;
1467         
1468         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1469         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1470
1471         /* Set optimized view status */
1472         priv->optimized_view = !show_toolbar;
1473
1474         if (!parent_priv->toolbar) {
1475                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1476                                                                   "/ToolBar");
1477
1478                 /* Set homogeneous toolbar */
1479                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
1480                                        set_homogeneous, NULL);
1481
1482                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1483                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1484                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1485                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1486                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1487                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1488                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1489                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1490
1491                 /* Add ProgressBar (Transfer toolbar) */ 
1492                 priv->progress_bar = modest_progress_bar_widget_new ();
1493                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1494                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1495                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1496                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1497                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1498                 
1499                 /* Connect cancel 'clicked' signal to abort progress mode */
1500                 g_signal_connect(priv->cancel_toolitem, "clicked",
1501                                  G_CALLBACK(cancel_progressbar),
1502                                  self);
1503                 
1504                 /* Add it to the observers list */
1505                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1506
1507                 /* Add to window */
1508                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
1509                                            GTK_TOOLBAR (parent_priv->toolbar));
1510
1511
1512                 /* Set reply button tap and hold menu */        
1513                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1514                                                           "/ToolBar/ToolbarMessageReply");
1515                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1516                                                   "/ToolbarReplyCSM");
1517                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1518         }
1519
1520         if (show_toolbar) {
1521                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
1522                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1523                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1524                 
1525                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1526                 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);                   
1527                 
1528         } else {
1529                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1530                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1531         }
1532 }
1533
1534 static void 
1535 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1536                                                GdkEvent *event,
1537                                                ModestMsgViewWindow *window)
1538 {
1539         ModestWindowPrivate *parent_priv;
1540 /*      GtkAction *action; */
1541         gboolean is_address;
1542         gchar *selection;
1543         GtkWidget *focused;
1544
1545         if (!GTK_WIDGET_VISIBLE (window))
1546                 return;
1547
1548         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1549         selection = gtk_clipboard_wait_for_text (clipboard);
1550
1551         is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL)));
1552         
1553 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1554 /*      gtk_action_set_sensitive (action, is_address); */
1555
1556         focused = gtk_window_get_focus (GTK_WINDOW (window));
1557
1558 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1559 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1560
1561 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1562 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1563
1564         g_free (selection);
1565 /*      modest_msg_view_window_update_dimmed (window); */
1566         
1567 }
1568
1569 gboolean 
1570 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
1571 {
1572         ModestMsgViewWindowPrivate *priv;
1573         
1574         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
1575         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1576
1577         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1578 }
1579
1580 static void
1581 cancel_progressbar (GtkToolButton *toolbutton,
1582                     ModestMsgViewWindow *self)
1583 {
1584         GSList *tmp;
1585         ModestMsgViewWindowPrivate *priv;
1586         
1587         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1588
1589         /* Get operation observers and cancel its current operation */
1590         tmp = priv->progress_widgets;
1591         while (tmp) {
1592                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1593                 tmp=g_slist_next(tmp);
1594         }
1595 }
1596 static gboolean
1597 observers_empty (ModestMsgViewWindow *self)
1598 {
1599         GSList *tmp = NULL;
1600         ModestMsgViewWindowPrivate *priv;
1601         gboolean is_empty = TRUE;
1602         guint pending_ops = 0;
1603  
1604         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1605         tmp = priv->progress_widgets;
1606
1607         /* Check all observers */
1608         while (tmp && is_empty)  {
1609                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
1610                 is_empty = pending_ops == 0;
1611                 
1612                 tmp = g_slist_next(tmp);
1613         }
1614         
1615         return is_empty;
1616 }
1617
1618 static void
1619 on_account_removed (TnyAccountStore *account_store, 
1620                     TnyAccount *account,
1621                     gpointer user_data)
1622 {
1623         /* Do nothing if it's a transport account, because we only
1624            show the messages of a store account */
1625         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
1626                 const gchar *parent_acc = NULL;
1627                 const gchar *our_acc = NULL;
1628
1629                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
1630                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
1631
1632                 /* Close this window if I'm showing a message of the removed account */
1633                 if (strcmp (parent_acc, our_acc) == 0)
1634                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
1635         }
1636 }
1637
1638 static void
1639 on_queue_changed (ModestMailOperationQueue *queue,
1640                   ModestMailOperation *mail_op,
1641                   ModestMailOperationQueueNotification type,
1642                   ModestMsgViewWindow *self)
1643 {
1644         GSList *tmp;
1645         ModestMsgViewWindowPrivate *priv;
1646         ModestMailOperationTypeOperation op_type;
1647         ModestToolBarModes mode;
1648         
1649         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1650         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1651
1652         /* If this operations was created by another window, do nothing */
1653         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
1654             return;
1655
1656         /* Get toolbar mode from operation id*/
1657         op_type = modest_mail_operation_get_type_operation (mail_op);
1658         switch (op_type) {
1659 /*      case MODEST_MAIL_OPERATION_TYPE_SEND: */
1660         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
1661         case MODEST_MAIL_OPERATION_TYPE_OPEN:
1662                 mode = TOOLBAR_MODE_TRANSFER;
1663                 break;
1664         default:
1665                 mode = TOOLBAR_MODE_NORMAL;
1666                 
1667         }
1668                 
1669         /* Add operation observers and change toolbar if neccessary*/
1670         tmp = priv->progress_widgets;
1671         switch (type) {
1672         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
1673                 if (mode == TOOLBAR_MODE_TRANSFER) {
1674                         /* Enable transfer toolbar mode */
1675                         set_toolbar_transfer_mode(self);
1676                         while (tmp) {
1677                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1678                                                                       mail_op);
1679                                 tmp = g_slist_next (tmp);
1680                         }
1681                         
1682                 }
1683                 break;
1684         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
1685                 if (mode == TOOLBAR_MODE_TRANSFER) {
1686                         while (tmp) {
1687                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1688                                                                  mail_op);
1689                                 tmp = g_slist_next (tmp);
1690                                 
1691                         }
1692
1693                         /* If no more operations are being observed, NORMAL mode is enabled again */
1694                         if (observers_empty (self)) {
1695                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
1696                         }
1697                 }
1698                 break;
1699         }
1700 }
1701
1702 GList *
1703 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
1704 {
1705         ModestMsgViewWindowPrivate *priv;
1706         GList *selected_attachments = NULL;
1707         
1708         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
1709         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
1710
1711         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1712         
1713         return selected_attachments;
1714 }
1715
1716 void
1717 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
1718 {
1719         ModestMsgViewWindowPrivate *priv;
1720         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1721         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
1722
1723         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1724
1725         if (mime_part == NULL) {
1726                 gboolean error = FALSE;
1727                 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1728                 if (selected_attachments == NULL) {
1729                         error = TRUE;
1730                 } else if (g_list_length (selected_attachments) > 1) {
1731                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
1732                         error = TRUE;
1733                 } else {
1734                         mime_part = (TnyMimePart *) selected_attachments->data;
1735                         g_object_ref (mime_part);
1736                 }
1737                 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
1738                 g_list_free (selected_attachments);
1739
1740                 if (error)
1741                         return;
1742         } else {
1743                 g_object_ref (mime_part);
1744         }
1745
1746         if (tny_mime_part_is_purged (mime_part)) {
1747                 g_object_unref (mime_part);
1748                 hildon_banner_show_information (NULL, NULL, _("mail_ib_attach_not_local"));
1749                 return;
1750         }
1751
1752         if (!TNY_IS_MSG (mime_part)) {
1753                 gchar *filepath = NULL;
1754                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
1755                 gchar *extension = NULL;
1756                 TnyFsStream *temp_stream = NULL;
1757
1758                 if (att_filename) {
1759                         extension = g_strrstr (att_filename, ".");
1760                         if (extension != NULL)
1761                                 extension++;
1762                 }
1763
1764                 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
1765
1766                 if (temp_stream) {
1767                         const gchar *content_type;
1768                         content_type = tny_mime_part_get_content_type (mime_part);
1769                         tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
1770                         
1771                         modest_platform_activate_file (filepath, content_type);
1772                         g_object_unref (temp_stream);
1773                         g_free (filepath);
1774                         /* TODO: delete temporary file */
1775                 }
1776         } else {
1777                 /* message attachment */
1778                 TnyHeader *header = NULL;
1779                 ModestWindowMgr *mgr;
1780                 ModestWindow *msg_win = NULL;
1781                 gboolean found;
1782                 
1783                 header = tny_msg_get_header (TNY_MSG (mime_part));
1784                 mgr = modest_runtime_get_window_mgr ();         
1785                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
1786
1787                 if (found) {
1788                         if (msg_win)                            /* there is already a window for this uid; top it */
1789                                 gtk_window_present (GTK_WINDOW(msg_win));
1790                         else 
1791                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
1792                                  * thus, we don't do anything */
1793                                 g_warning ("window for is already being created");
1794                 } else { 
1795                         /* it's not found, so create a new window for it */
1796                         modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
1797                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
1798                         if (!account)
1799                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
1800                         msg_win = modest_msg_view_window_new (TNY_MSG (mime_part), account, NULL);
1801                         modest_window_set_zoom (MODEST_WINDOW (msg_win), 
1802                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
1803                         modest_window_mgr_register_window (mgr, msg_win);
1804                         gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
1805                         gtk_widget_show_all (GTK_WIDGET (msg_win));
1806                 }
1807         }
1808         g_object_unref (mime_part);
1809 }
1810
1811 typedef struct
1812 {
1813         gchar *filename;
1814         TnyMimePart *part;
1815 } SaveMimePartPair;
1816
1817 typedef struct
1818 {
1819         GList *pairs;
1820         GtkWidget *banner;
1821         gboolean result;
1822 } SaveMimePartInfo;
1823
1824 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
1825 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
1826 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
1827 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
1828
1829 static void 
1830 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
1831 {
1832         
1833         GList *node;
1834         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
1835                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
1836                 g_free (pair->filename);
1837                 g_object_unref (pair->part);
1838                 g_slice_free (SaveMimePartPair, pair);
1839         }
1840         g_list_free (info->pairs);
1841         info->pairs = NULL;
1842         if (with_struct) {
1843                 gtk_widget_destroy (info->banner);
1844                 g_object_unref (info->banner);
1845                 g_slice_free (SaveMimePartInfo, info);
1846         }
1847 }
1848
1849 static gboolean
1850 idle_save_mime_part_show_result (SaveMimePartInfo *info)
1851 {
1852         if (info->pairs != NULL) {
1853                 gdk_threads_enter ();
1854                 save_mime_parts_to_file_with_checks (info);
1855                 gdk_threads_leave ();
1856         } else {
1857                 gboolean result;
1858                 result = info->result;
1859
1860                 gdk_threads_enter ();
1861                 save_mime_part_info_free (info, TRUE);
1862                 if (result) {
1863                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
1864                 } else {
1865                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
1866                 }
1867                 gdk_threads_leave ();
1868         }
1869
1870         return FALSE;
1871 }
1872
1873 static gpointer
1874 save_mime_part_to_file (SaveMimePartInfo *info)
1875 {
1876         GnomeVFSResult result;
1877         GnomeVFSHandle *handle;
1878         TnyStream *stream;
1879         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
1880
1881         result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
1882         if (result == GNOME_VFS_OK) {
1883                 stream = tny_vfs_stream_new (handle);
1884                 tny_mime_part_decode_to_stream (pair->part, stream);
1885                 g_object_unref (G_OBJECT (stream));
1886                 g_object_unref (pair->part);
1887                 g_slice_free (SaveMimePartPair, pair);
1888                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
1889                 info->result = TRUE;
1890         } else {
1891                 save_mime_part_info_free (info, FALSE);
1892                 info->result = FALSE;
1893         }
1894
1895         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
1896         return NULL;
1897 }
1898
1899 static void
1900 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
1901 {
1902         SaveMimePartPair *pair;
1903         gboolean is_ok = TRUE;
1904
1905         pair = info->pairs->data;
1906         if (modest_maemo_utils_file_exists (pair->filename)) {
1907                 GtkWidget *confirm_overwrite_dialog;
1908                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
1909                                                                          _("emev_nc_replace_files"));
1910                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
1911                         is_ok = FALSE;
1912                 }
1913                 gtk_widget_destroy (confirm_overwrite_dialog);
1914         }
1915
1916         if (!is_ok) {
1917                 save_mime_part_info_free (info, TRUE);
1918         } else {
1919                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
1920         }
1921
1922 }
1923
1924
1925 void
1926 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
1927 {
1928         gboolean clean_list = FALSE;
1929         ModestMsgViewWindowPrivate *priv;
1930         GList *files_to_save = NULL;
1931         GtkWidget *save_dialog = NULL;
1932         gchar *folder = NULL;
1933         gboolean canceled = FALSE;
1934         const gchar *filename = NULL;
1935         gchar *save_multiple_str = NULL;
1936
1937         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1938         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1939
1940         if (mime_parts == NULL) {
1941                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1942                 if (mime_parts == NULL)
1943                         return;
1944                 clean_list = TRUE;
1945         }
1946
1947         /* prepare dialog */
1948         if (mime_parts->next == NULL) {
1949                 /* only one attachment selected */
1950                 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
1951                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
1952                         filename = tny_mime_part_get_filename (mime_part);
1953                 } else {
1954                         g_warning ("Tried to save a non-file attachment");
1955                         canceled = TRUE;
1956                 }
1957         } else {
1958                 save_multiple_str = g_strdup_printf (_("FIXME: %d attachments"), 
1959                                                      g_list_length (mime_parts));
1960         }
1961         
1962         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
1963                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
1964
1965         /* set folder */
1966         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
1967         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
1968         g_free (folder);
1969
1970         /* set filename */
1971         if (filename != NULL)
1972                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
1973                                                    filename);
1974
1975         /* if multiple, set multiple string */
1976         if (save_multiple_str) {
1977                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
1978         }
1979                 
1980         /* show dialog */
1981         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
1982                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
1983
1984                 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
1985                         hildon_banner_show_information 
1986                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
1987                 } else {
1988                         GList *node = NULL;
1989
1990                         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
1991                                 TnyMimePart *mime_part = (TnyMimePart *) node->data;
1992                                 
1993                                 if (tny_mime_part_is_attachment (mime_part)) {
1994                                         SaveMimePartPair *pair;
1995
1996                                         if ((mime_parts->next != NULL) &&
1997                                             (tny_mime_part_get_filename (mime_part) == NULL))
1998                                                 continue;
1999                                         
2000                                         pair = g_slice_new0 (SaveMimePartPair);
2001                                         if (mime_parts->next == NULL) {
2002                                                 pair->filename = g_strdup (chooser_uri);
2003                                         } else {
2004                                                 pair->filename = 
2005                                                         g_build_filename (chooser_uri,
2006                                                                           tny_mime_part_get_filename (mime_part), NULL);
2007                                         }
2008                                         pair->part = g_object_ref (mime_part);
2009                                         files_to_save = g_list_prepend (files_to_save, pair);
2010                                 }
2011                         }
2012                 }
2013                 g_free (chooser_uri);
2014         }
2015
2016         gtk_widget_destroy (save_dialog);
2017
2018         if (clean_list) {
2019                 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2020                 g_list_free (mime_parts);
2021         }
2022
2023         if (files_to_save != NULL) {
2024                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2025                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2026                                                                   _CS("sfil_ib_saving"));
2027                 info->pairs = files_to_save;
2028                 info->banner = banner;
2029                 info->result = TRUE;
2030                 g_object_ref (banner);
2031                 save_mime_parts_to_file_with_checks (info);
2032         }
2033 }
2034
2035 void
2036 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2037 {
2038         ModestMsgViewWindowPrivate *priv;
2039         GList *mime_parts = NULL, *node;
2040         gchar *confirmation_message;
2041         gint response;
2042         gint n_attachments;
2043         TnyMsg *msg;
2044 /*      TnyFolder *folder; */
2045
2046         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2047         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2048
2049         if (get_all)
2050                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2051         else
2052                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2053                 
2054         /* Remove already purged messages from mime parts list */
2055         node = mime_parts;
2056         while (node != NULL) {
2057                 TnyMimePart *part = TNY_MIME_PART (node->data);
2058                 if (tny_mime_part_is_purged (part)) {
2059                         GList *deleted_node = node;
2060                         node = g_list_next (node);
2061                         g_object_unref (part);
2062                         mime_parts = g_list_delete_link (mime_parts, deleted_node);
2063                 } else {
2064                         node = g_list_next (node);
2065                 }
2066         }
2067
2068         if (mime_parts == NULL)
2069                 return;
2070
2071         n_attachments = g_list_length (mime_parts);
2072         if (n_attachments == 1) {
2073                 const gchar *filename;
2074
2075                 if (TNY_IS_MSG (mime_parts->data)) {
2076                         TnyHeader *header;
2077                         header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2078                         filename = tny_header_get_subject (header);
2079                         g_object_unref (header);
2080                         if (filename == NULL)
2081                                 filename = _("mail_va_no_subject");
2082                 } else {
2083                         filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2084                 }
2085                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2086         } else {
2087                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2088                                                                  "mcen_nc_purge_files_text", 
2089                                                                  n_attachments), n_attachments);
2090         }
2091         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2092                                                             confirmation_message);
2093         g_free (confirmation_message);
2094
2095         if (response != GTK_RESPONSE_OK)
2096                 return;
2097
2098 /*      folder = tny_msg_get_folder (msg); */
2099 /*      tny_msg_uncache_attachments (msg); */
2100 /*      tny_folder_refresh (folder, NULL); */
2101 /*      g_object_unref (folder); */
2102         
2103         modest_platform_information_banner (NULL, NULL, _("mcen_ib_removing_attachment"));
2104
2105         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2106                 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2107 /*              modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2108         }
2109
2110         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2111         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2112         tny_msg_rewrite_cache (msg);
2113         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2114
2115         g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2116         g_list_free (mime_parts);
2117
2118
2119 }
2120
2121
2122 static void
2123 update_window_title (ModestMsgViewWindow *window)
2124 {
2125         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2126         TnyMsg *msg = NULL;
2127         TnyHeader *header = NULL;
2128         const gchar *subject = NULL;
2129
2130         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2131         if (msg != NULL) {
2132                 header = tny_msg_get_header (msg);
2133                 subject = tny_header_get_subject (header);
2134                 g_object_unref (msg);
2135         }
2136
2137         if ((subject == NULL)||(subject[0] == '\0'))
2138                 subject = _("mail_va_no_subject");
2139
2140         gtk_window_set_title (GTK_WINDOW (window), subject);
2141 }