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