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