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