* Fixes NB#86800, NB#86757, merged from trunk r5098
[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-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.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.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
59 #include <math.h>
60 #include <errno.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
63
64 #define DEFAULT_FOLDER "MyDocs/.documents"
65
66 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
67 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
68 static void  modest_header_view_observer_init(
69                 ModestHeaderViewObserverIface *iface_class);
70 static void  modest_msg_view_window_finalize     (GObject *obj);
71 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
72                                                          gpointer data);
73 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74                                                         ModestMsgViewWindow *obj);
75 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76                                                         ModestMsgViewWindow *obj);
77
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
80                                              gdouble zoom);
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
85                                                   GdkEventKey *event,
86                                                   gpointer userdata);
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, 
88                                                            GdkEventWindowState *event, 
89                                                            gpointer userdata);
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
91
92 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
93                                                    gboolean show_toolbar);
94
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
96                                                            GdkEvent *event,
97                                                            ModestMsgViewWindow *window);
98
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
100                                                    GtkTreePath *arg1,
101                                                    GtkTreeIter *arg2,
102                                                    ModestMsgViewWindow *window);
103
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
105                                                    GtkTreePath *arg1,
106                                                    ModestMsgViewWindow *window);
107
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109                                                     GtkTreePath *tree_path,
110                                                     GtkTreeIter *tree_iter,
111                                                     ModestMsgViewWindow *window);
112
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
114                                                      GtkTreePath *arg1,
115                                                      GtkTreeIter *arg2,
116                                                      gpointer arg3,
117                                                      ModestMsgViewWindow *window);
118
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
120                                                           GtkTreeModel *model,
121                                                           const gchar *tny_folder_id);
122
123 static void cancel_progressbar  (GtkToolButton *toolbutton,
124                                  ModestMsgViewWindow *self);
125
126 static void on_queue_changed    (ModestMailOperationQueue *queue,
127                                  ModestMailOperation *mail_op,
128                                  ModestMailOperationQueueNotification type,
129                                  ModestMsgViewWindow *self);
130
131 static void on_account_removed  (TnyAccountStore *account_store, 
132                                  TnyAccount *account,
133                                  gpointer user_data);
134
135 static void on_move_focus (GtkWidget *widget,
136                            GtkDirectionType direction,
137                            gpointer userdata);
138
139 static void view_msg_cb         (ModestMailOperation *mail_op, 
140                                  TnyHeader *header, 
141                                  gboolean canceled,
142                                  TnyMsg *msg, 
143                                  GError *error,
144                                  gpointer user_data);
145
146 static void set_toolbar_mode    (ModestMsgViewWindow *self, 
147                                  ModestToolBarModes mode);
148
149 static void update_window_title (ModestMsgViewWindow *window);
150
151 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
152 static void init_window (ModestMsgViewWindow *obj);
153
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
155
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
157
158 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
159                                                      GtkScrollType scroll_type,
160                                                      gboolean horizontal,
161                                                      gpointer userdata);
162
163 /* list my signals */
164 enum {
165         MSG_CHANGED_SIGNAL,
166         SCROLL_CHILD_SIGNAL,
167         LAST_SIGNAL
168 };
169
170 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
171         { "FindInMessage",    MODEST_TOOLBAR_ICON_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
172         { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
173 };
174
175 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
176         { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
177         { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
178         { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
179         { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
180         { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
181         { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
182 };
183
184 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
185 struct _ModestMsgViewWindowPrivate {
186
187         GtkWidget   *msg_view;
188         GtkWidget   *main_scroll;
189         GtkWidget   *find_toolbar;
190         gchar       *last_search;
191
192         /* Progress observers */
193         GtkWidget        *progress_bar;
194         GSList           *progress_widgets;
195
196         /* Tollbar items */
197         GtkWidget   *progress_toolitem;
198         GtkWidget   *cancel_toolitem;
199         GtkWidget   *prev_toolitem;
200         GtkWidget   *next_toolitem;
201         ModestToolBarModes current_toolbar_mode;
202
203         /* Optimized view enabled */
204         gboolean optimized_view;
205
206         /* Whether this was created via the *_new_for_search_result() function. */
207         gboolean is_search_result;
208
209         /* Whether the message is in outbox */
210         gboolean is_outbox;
211         
212         /* A reference to the @model of the header view 
213          * to allow selecting previous/next messages,
214          * if the message is currently selected in the header view.
215          */
216         const gchar *header_folder_id;
217         GtkTreeModel *header_model;
218         GtkTreeRowReference *row_reference;
219         GtkTreeRowReference *next_row_reference;
220
221         gulong clipboard_change_handler;
222         gulong queue_change_handler;
223         gulong account_removed_handler;
224         gulong row_changed_handler;
225         gulong row_deleted_handler;
226         gulong row_inserted_handler;
227         gulong rows_reordered_handler;
228
229         guint purge_timeout;
230         GtkWidget *remove_attachment_banner;
231
232         guint progress_bar_timeout;
233
234         gchar *msg_uid;
235         
236         GSList *sighandlers;
237 };
238
239 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
240                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
241                                                     ModestMsgViewWindowPrivate))
242 /* globals */
243 static GtkWindowClass *parent_class = NULL;
244
245 /* uncomment the following if you have defined any signals */
246 static guint signals[LAST_SIGNAL] = {0};
247
248 GType
249 modest_msg_view_window_get_type (void)
250 {
251         static GType my_type = 0;
252         if (!my_type) {
253                 static const GTypeInfo my_info = {
254                         sizeof(ModestMsgViewWindowClass),
255                         NULL,           /* base init */
256                         NULL,           /* base finalize */
257                         (GClassInitFunc) modest_msg_view_window_class_init,
258                         NULL,           /* class finalize */
259                         NULL,           /* class data */
260                         sizeof(ModestMsgViewWindow),
261                         1,              /* n_preallocs */
262                         (GInstanceInitFunc) modest_msg_view_window_init,
263                         NULL
264                 };
265                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
266                                                   "ModestMsgViewWindow",
267                                                   &my_info, 0);
268
269                 static const GInterfaceInfo modest_header_view_observer_info = 
270                 {
271                         (GInterfaceInitFunc) modest_header_view_observer_init,
272                         NULL,         /* interface_finalize */
273                         NULL          /* interface_data */
274                 };
275
276                 g_type_add_interface_static (my_type,
277                                 MODEST_TYPE_HEADER_VIEW_OBSERVER,
278                                 &modest_header_view_observer_info);
279         }
280         return my_type;
281 }
282
283 static void
284 save_state (ModestWindow *self)
285 {
286         modest_widget_memory_save (modest_runtime_get_conf (),
287                                    G_OBJECT(self), 
288                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
289 }
290
291
292 static void
293 restore_settings (ModestMsgViewWindow *self)
294 {
295         ModestConf *conf;
296         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
297         GtkAction *action;
298
299         conf = modest_runtime_get_conf ();
300         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
301                                             "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
302         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
303                                       modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
304         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
305                                             "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
306         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
307                                       modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
308         modest_widget_memory_restore (conf,
309                                       G_OBJECT(self), 
310                                       MODEST_CONF_MSG_VIEW_WINDOW_KEY);
311 }
312
313 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
314                                                      GtkScrollType scroll_type,
315                                                      gboolean horizontal,
316                                                      gpointer userdata)
317 {
318         ModestMsgViewWindowPrivate *priv;
319         gboolean return_value;
320
321         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
322         g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
323         return return_value;
324 }
325
326 static void
327 add_scroll_binding (GtkBindingSet *binding_set,
328                     guint keyval,
329                     GtkScrollType scroll)
330 {
331         guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
332         
333         gtk_binding_entry_add_signal (binding_set, keyval, 0,
334                                       "scroll_child", 2,
335                                       GTK_TYPE_SCROLL_TYPE, scroll,
336                                       G_TYPE_BOOLEAN, FALSE);
337         gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
338                                       "scroll_child", 2,
339                                       GTK_TYPE_SCROLL_TYPE, scroll,
340                                       G_TYPE_BOOLEAN, FALSE);
341 }
342
343 static void
344 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
345 {
346         GObjectClass *gobject_class;
347         ModestWindowClass *modest_window_class;
348         GtkBindingSet *binding_set;
349
350         gobject_class = (GObjectClass*) klass;
351         modest_window_class = (ModestWindowClass *) klass;
352
353         parent_class            = g_type_class_peek_parent (klass);
354         gobject_class->finalize = modest_msg_view_window_finalize;
355
356         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
357         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
358         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
359         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
360         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
361         modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
362
363         modest_window_class->save_state_func = save_state;
364
365         klass->scroll_child = modest_msg_view_window_scroll_child;
366
367         signals[MSG_CHANGED_SIGNAL] =
368                 g_signal_new ("msg-changed",
369                               G_TYPE_FROM_CLASS (gobject_class),
370                               G_SIGNAL_RUN_FIRST,
371                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
372                               NULL, NULL,
373                               modest_marshal_VOID__POINTER_POINTER,
374                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
375
376         signals[SCROLL_CHILD_SIGNAL] =
377                 g_signal_new ("scroll-child",
378                               G_TYPE_FROM_CLASS (gobject_class),
379                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
380                               G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
381                               NULL, NULL,
382                               modest_marshal_BOOLEAN__ENUM_BOOLEAN,
383                               G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
384
385         binding_set = gtk_binding_set_by_class (klass);
386         add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
387         add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
388         add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
389         add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
390         add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
391         add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
392
393         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
394
395 }
396
397 static void modest_header_view_observer_init(
398                 ModestHeaderViewObserverIface *iface_class)
399 {
400         iface_class->update_func = modest_msg_view_window_update_model_replaced;
401 }
402
403 static void
404 modest_msg_view_window_init (ModestMsgViewWindow *obj)
405 {
406         ModestMsgViewWindowPrivate *priv;
407         ModestWindowPrivate *parent_priv = NULL;
408         GtkActionGroup *action_group = NULL;
409         GError *error = NULL;
410         GdkPixbuf *window_icon;
411
412         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
413         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
414         parent_priv->ui_manager = gtk_ui_manager_new();
415
416         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
417         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
418
419         /* Add common actions */
420         gtk_action_group_add_actions (action_group,
421                                       modest_action_entries,
422                                       G_N_ELEMENTS (modest_action_entries),
423                                       obj);
424         gtk_action_group_add_toggle_actions (action_group,
425                                              modest_toggle_action_entries,
426                                              G_N_ELEMENTS (modest_toggle_action_entries),
427                                              obj);
428         gtk_action_group_add_toggle_actions (action_group,
429                                              msg_view_toggle_action_entries,
430                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
431                                              obj);
432         gtk_action_group_add_radio_actions (action_group,
433                                             msg_view_zoom_action_entries,
434                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
435                                             100,
436                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
437                                             obj);
438
439         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
440         g_object_unref (action_group);
441
442         /* Load the UI definition */
443         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
444                                          &error);
445         if (error) {
446                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
447                 g_error_free (error);
448                 error = NULL;
449         }
450         /* ****** */
451
452         /* Add accelerators */
453         gtk_window_add_accel_group (GTK_WINDOW (obj), 
454                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
455         
456         priv->is_search_result = FALSE;
457         priv->is_outbox = FALSE;
458
459         priv->msg_view      = NULL;
460         priv->header_model  = NULL;
461         priv->header_folder_id  = NULL;
462         priv->clipboard_change_handler = 0;
463         priv->queue_change_handler = 0;
464         priv->account_removed_handler = 0;
465         priv->row_changed_handler = 0;
466         priv->row_deleted_handler = 0;
467         priv->row_inserted_handler = 0;
468         priv->rows_reordered_handler = 0;
469         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
470
471         priv->optimized_view  = FALSE;
472         priv->progress_bar_timeout = 0;
473         priv->purge_timeout = 0;
474         priv->remove_attachment_banner = NULL;
475         priv->msg_uid = NULL;
476         
477         priv->sighandlers = NULL;
478         
479         /* Init window */
480         init_window (MODEST_MSG_VIEW_WINDOW(obj));
481         
482         /* Set window icon */
483         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG); 
484         if (window_icon) {
485                 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
486                 g_object_unref (window_icon);
487         }       
488         
489         hildon_program_add_window (hildon_program_get_instance(),
490                                    HILDON_WINDOW(obj));
491
492         modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
493                                             GTK_WINDOW(obj),"applications_email_viewer");
494 }
495
496
497 static gboolean
498 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
499 {
500         ModestMsgViewWindowPrivate *priv = NULL;
501         
502         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
503
504         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
505
506         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
507         
508         if (priv->progress_bar_timeout > 0) {
509                 g_source_remove (priv->progress_bar_timeout);
510                 priv->progress_bar_timeout = 0;
511         }
512         
513         return FALSE;
514 }
515
516 static void 
517 set_toolbar_mode (ModestMsgViewWindow *self, 
518                   ModestToolBarModes mode)
519 {
520         ModestWindowPrivate *parent_priv;
521         ModestMsgViewWindowPrivate *priv;
522 /*      GtkWidget *widget = NULL; */
523
524         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
525
526         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
527         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
528                         
529         /* Sets current toolbar mode */
530         priv->current_toolbar_mode = mode;
531
532         /* Update toolbar dimming state */
533         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
534
535         switch (mode) {
536         case TOOLBAR_MODE_NORMAL:               
537                 if (priv->progress_toolitem) {
538                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
539                         gtk_widget_hide (priv->progress_toolitem);
540                 }
541
542                 if (priv->progress_bar)
543                         gtk_widget_hide (priv->progress_bar);
544                         
545                 if (priv->cancel_toolitem)
546                         gtk_widget_hide (priv->cancel_toolitem);
547
548                 if (priv->prev_toolitem)
549                         gtk_widget_show (priv->prev_toolitem);
550                 
551                 if (priv->next_toolitem)
552                         gtk_widget_show (priv->next_toolitem);
553                         
554                 /* Hide toolbar if optimized view is enabled */
555                 if (priv->optimized_view) {
556                         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
557                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
558                 }
559
560                 break;
561         case TOOLBAR_MODE_TRANSFER:
562                 if (priv->prev_toolitem)
563                         gtk_widget_hide (priv->prev_toolitem);
564                 
565                 if (priv->next_toolitem)
566                         gtk_widget_hide (priv->next_toolitem);
567                 
568                 if (priv->progress_bar)
569                         gtk_widget_show (priv->progress_bar);
570
571                 if (priv->progress_toolitem) {
572                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
573                         gtk_widget_show (priv->progress_toolitem);
574                 }
575                         
576                 if (priv->cancel_toolitem)
577                         gtk_widget_show (priv->cancel_toolitem);
578
579                 /* Show toolbar if it's hiden (optimized view ) */
580                 if (priv->optimized_view) {
581                         gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
582                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
583                 }
584
585                 break;
586         default:
587                 g_return_if_reached ();
588         }
589
590 }
591
592
593 static void
594 init_window (ModestMsgViewWindow *obj)
595 {
596         GtkWidget *main_vbox;
597         ModestMsgViewWindowPrivate *priv;
598
599         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
600
601         priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
602         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
603         main_vbox = gtk_vbox_new  (FALSE, 6);
604
605 #ifdef MODEST_USE_MOZEMBED
606         priv->main_scroll = priv->msg_view;
607         gtk_widget_set_size_request (priv->msg_view, -1, 1600);
608 #else
609         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
610         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
611 #endif
612         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
613         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
614         modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
615
616         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
617         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
618
619         priv->find_toolbar = hildon_find_toolbar_new (NULL);
620         hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
621         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
622         
623         gtk_widget_show_all (GTK_WIDGET(main_vbox));
624 }
625
626 static void
627 modest_msg_view_window_disconnect_signals (ModestWindow *self)
628 {
629         ModestMsgViewWindowPrivate *priv;
630         ModestHeaderView *header_view = NULL;
631         ModestWindow *main_window = NULL;
632         
633         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
634
635         if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
636             g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
637                                            priv->clipboard_change_handler)) 
638                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), 
639                                              priv->clipboard_change_handler);
640
641         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
642                                            priv->queue_change_handler))
643                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
644                                              priv->queue_change_handler);
645
646         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), 
647                                            priv->account_removed_handler))
648                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), 
649                                              priv->account_removed_handler);
650
651         if (priv->header_model) {
652                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
653                                                   priv->row_changed_handler))
654                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
655                                                     priv->row_changed_handler);
656                 
657                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
658                                                   priv->row_deleted_handler))
659                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
660                                              priv->row_deleted_handler);
661                 
662                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
663                                                   priv->row_inserted_handler))
664                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
665                                                     priv->row_inserted_handler);
666                 
667                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
668                                                   priv->rows_reordered_handler))
669                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
670                                                     priv->rows_reordered_handler);
671         }
672
673         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
674         priv->sighandlers = NULL;
675         
676         main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
677                                                          FALSE); /* don't create */
678         if (!main_window)
679                 return;
680         
681         header_view = MODEST_HEADER_VIEW(
682                         modest_main_window_get_child_widget(
683                                 MODEST_MAIN_WINDOW(main_window),
684                                 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
685         if (header_view == NULL)
686                 return;
687         
688         modest_header_view_remove_observer(header_view,
689                         MODEST_HEADER_VIEW_OBSERVER(self));
690 }       
691
692 static void
693 modest_msg_view_window_finalize (GObject *obj)
694 {
695         ModestMsgViewWindowPrivate *priv;
696
697         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
698
699         /* Sanity check: shouldn't be needed, the window mgr should
700            call this function before */
701         modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
702
703         if (priv->header_model != NULL) {
704                 g_object_unref (priv->header_model);
705                 priv->header_model = NULL;
706         }
707
708         if (priv->progress_bar_timeout > 0) {
709                 g_source_remove (priv->progress_bar_timeout);
710                 priv->progress_bar_timeout = 0;
711         }
712
713         if (priv->remove_attachment_banner) {
714                 gtk_widget_destroy (priv->remove_attachment_banner);
715                 g_object_unref (priv->remove_attachment_banner);
716                 priv->remove_attachment_banner = NULL;
717         }
718
719         if (priv->purge_timeout > 0) {
720                 g_source_remove (priv->purge_timeout);
721                 priv->purge_timeout = 0;
722         }
723
724         if (priv->row_reference) {
725                 gtk_tree_row_reference_free (priv->row_reference);
726                 priv->row_reference = NULL;
727         }
728
729         if (priv->next_row_reference) {
730                 gtk_tree_row_reference_free (priv->next_row_reference);
731                 priv->next_row_reference = NULL;
732         }
733
734         if (priv->msg_uid) {
735                 g_free (priv->msg_uid);
736                 priv->msg_uid = NULL;
737         }
738
739         G_OBJECT_CLASS(parent_class)->finalize (obj);
740 }
741
742 static gboolean
743 select_next_valid_row (GtkTreeModel *model,
744                        GtkTreeRowReference **row_reference,
745                        gboolean cycle)
746 {
747         GtkTreeIter tmp_iter;
748         GtkTreePath *path;
749         GtkTreePath *next = NULL;
750         gboolean retval = FALSE;
751
752         g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
753
754         path = gtk_tree_row_reference_get_path (*row_reference);
755         gtk_tree_model_get_iter (model, &tmp_iter, path);
756         gtk_tree_row_reference_free (*row_reference);
757         *row_reference = NULL;
758
759         if (gtk_tree_model_iter_next (model, &tmp_iter)) {
760                 next = gtk_tree_model_get_path (model, &tmp_iter);
761                 *row_reference = gtk_tree_row_reference_new (model, next);
762                 retval = TRUE;
763         } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
764                 next = gtk_tree_model_get_path (model, &tmp_iter);
765
766                 /* Ensure that we are not selecting the same */
767                 if (gtk_tree_path_compare (path, next) != 0) {
768                         *row_reference = gtk_tree_row_reference_new (model, next);
769                         retval = TRUE;
770                 }
771         }
772
773         /* Free */
774         gtk_tree_path_free (path);
775         if (next)
776                 gtk_tree_path_free (next);
777
778         return retval;
779 }
780
781 /* TODO: This should be in _init(), with the parameters as properties. */
782 static void
783 modest_msg_view_window_construct (ModestMsgViewWindow *self, 
784                                   const gchar *modest_account_name,
785                                   const gchar *msg_uid)
786 {
787         GObject *obj = NULL;
788         ModestMsgViewWindowPrivate *priv = NULL;
789         ModestWindowPrivate *parent_priv = NULL;
790         ModestDimmingRulesGroup *menu_rules_group = NULL;
791         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
792         ModestDimmingRulesGroup *clipboard_rules_group = NULL;
793
794         obj = G_OBJECT (self);
795         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
796         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
797
798         priv->msg_uid = g_strdup (msg_uid);
799
800         /* Menubar */
801         parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
802         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
803         gtk_widget_show (parent_priv->menubar);
804         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
805
806         menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
807         toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
808         clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
809
810         /* Add common dimming rules */
811         modest_dimming_rules_group_add_rules (menu_rules_group, 
812                                               modest_msg_view_menu_dimming_entries,
813                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
814                                               MODEST_WINDOW (self));
815         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
816                                               modest_msg_view_toolbar_dimming_entries,
817                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
818                                               MODEST_WINDOW (self));
819         modest_dimming_rules_group_add_rules (clipboard_rules_group, 
820                                               modest_msg_view_clipboard_dimming_entries,
821                                               G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
822                                               MODEST_WINDOW (self));
823
824         /* Insert dimming rules group for this window */
825         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
826         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
827         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
828         g_object_unref (menu_rules_group);
829         g_object_unref (toolbar_rules_group);
830         g_object_unref (clipboard_rules_group);
831
832         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
833         
834         /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
835
836         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);
837         g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
838                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
839         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
840                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
841         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
842                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
843         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
844                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
845         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
846                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
847
848         g_signal_connect (G_OBJECT (obj), "key-release-event",
849                           G_CALLBACK (modest_msg_view_window_key_event),
850                           NULL);
851
852         g_signal_connect (G_OBJECT (obj), "key-press-event",
853                           G_CALLBACK (modest_msg_view_window_key_event),
854                           NULL);
855
856         g_signal_connect (G_OBJECT (obj), "window-state-event",
857                           G_CALLBACK (modest_msg_view_window_window_state_event),
858                           NULL);
859
860         g_signal_connect (G_OBJECT (obj), "move-focus",
861                           G_CALLBACK (on_move_focus), obj);
862
863         /* Mail Operation Queue */
864         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
865                                                        "queue-changed",
866                                                        G_CALLBACK (on_queue_changed),
867                                                        obj);
868
869         /* Account manager */
870         priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
871                                                           "account_removed",
872                                                           G_CALLBACK(on_account_removed),
873                                                           obj);
874
875         modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
876
877         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
878         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
879         priv->last_search = NULL;
880
881         /* Init the clipboard actions dim status */
882         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
883
884         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
885
886
887 }
888
889 /* FIXME: parameter checks */
890 ModestWindow *
891 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
892                                               const gchar *modest_account_name,
893                                               const gchar *msg_uid,
894                                               GtkTreeModel *model, 
895                                               GtkTreeRowReference *row_reference)
896 {
897         ModestMsgViewWindow *window = NULL;
898         ModestMsgViewWindowPrivate *priv = NULL;
899         TnyFolder *header_folder = NULL;
900         ModestHeaderView *header_view = NULL;
901         ModestWindow *main_window = NULL;
902         ModestWindowMgr *mgr = NULL;
903
904         MODEST_DEBUG_BLOCK (
905                modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
906         );
907
908         mgr = modest_runtime_get_window_mgr ();
909         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
910         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
911
912         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
913
914         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
915
916         /* Remember the message list's TreeModel so we can detect changes 
917          * and change the list selection when necessary: */
918
919         main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
920         if (main_window) {
921                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
922                                                          MODEST_MAIN_WINDOW(main_window),
923                                                          MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
924         }
925         
926         if (header_view != NULL){
927                 header_folder = modest_header_view_get_folder(header_view);
928                 /* This could happen if the header folder was
929                    unseleted before opening this msg window (for
930                    example if the user selects an account in the
931                    folder view of the main window */
932                 if (header_folder) {
933                         priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
934                         priv->header_folder_id = tny_folder_get_id(header_folder);
935                         g_assert(priv->header_folder_id != NULL);
936                         g_object_unref(header_folder);
937                 }
938         }
939
940         /* Setup row references and connect signals */
941         priv->header_model = g_object_ref (model);
942
943         if (row_reference) {
944                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
945                 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
946                 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
947         } else {
948                 priv->row_reference = NULL;
949                 priv->next_row_reference = NULL;
950         }
951
952         /* Connect signals */
953         priv->row_changed_handler = 
954                 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
955                                   G_CALLBACK(modest_msg_view_window_on_row_changed),
956                                   window);
957         priv->row_deleted_handler = 
958                 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
959                                   G_CALLBACK(modest_msg_view_window_on_row_deleted),
960                                   window);
961         priv->row_inserted_handler = 
962                 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
963                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
964                                   window);
965         priv->rows_reordered_handler = 
966                 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
967                                  G_CALLBACK(modest_msg_view_window_on_row_reordered),
968                                  window);
969
970         if (header_view != NULL){
971                 modest_header_view_add_observer(header_view,
972                                 MODEST_HEADER_VIEW_OBSERVER(window));
973         }
974
975         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
976         update_window_title (MODEST_MSG_VIEW_WINDOW (window));
977         gtk_widget_show_all (GTK_WIDGET (window));
978         modest_msg_view_window_update_priority (window);
979
980         /* Check dimming rules */
981         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
982         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
983         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
984
985         return MODEST_WINDOW(window);
986 }
987
988 ModestWindow *
989 modest_msg_view_window_new_for_search_result (TnyMsg *msg, 
990                                               const gchar *modest_account_name,
991                                               const gchar *msg_uid)
992 {
993         ModestMsgViewWindow *window = NULL;
994         ModestMsgViewWindowPrivate *priv = NULL;
995         ModestWindowMgr *mgr = NULL;
996
997         mgr = modest_runtime_get_window_mgr ();
998         window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
999         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1000         modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1001
1002         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1003
1004         /* Remember that this is a search result, 
1005          * so we can disable some UI appropriately: */
1006         priv->is_search_result = TRUE;
1007
1008         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1009         
1010         update_window_title (window);
1011         gtk_widget_show_all (GTK_WIDGET (window));
1012         modest_msg_view_window_update_priority (window);
1013
1014         /* Check dimming rules */
1015         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1016         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1017         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1018
1019         return MODEST_WINDOW(window);
1020 }
1021
1022 ModestWindow *
1023 modest_msg_view_window_new_for_attachment (TnyMsg *msg, 
1024                             const gchar *modest_account_name,
1025                             const gchar *msg_uid)
1026 {
1027         GObject *obj = NULL;
1028         ModestMsgViewWindowPrivate *priv;       
1029         ModestWindowMgr *mgr = NULL;
1030
1031         g_return_val_if_fail (msg, NULL);
1032         mgr = modest_runtime_get_window_mgr ();
1033         obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1034         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1035         modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), 
1036                 modest_account_name, msg_uid);
1037
1038         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1039         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1040
1041         gtk_widget_show_all (GTK_WIDGET (obj));
1042
1043         /* Check dimming rules */
1044         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1045         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1046         modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1047
1048         return MODEST_WINDOW(obj);
1049 }
1050
1051 static void
1052 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1053                                        GtkTreePath *arg1,
1054                                        GtkTreeIter *arg2,
1055                                        ModestMsgViewWindow *window)
1056 {
1057         check_dimming_rules_after_change (window);
1058 }
1059
1060 static void 
1061 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1062                                       GtkTreePath *arg1,
1063                                       ModestMsgViewWindow *window)
1064 {
1065         check_dimming_rules_after_change (window);
1066 }
1067
1068 static gboolean
1069 check_dimming_rules_after_change_in_idle (gpointer data)
1070 {
1071         /* The window could have dissapeared */
1072         if (MODEST_IS_WINDOW (data)) {
1073                 ModestWindow *win = MODEST_WINDOW (data);
1074                 gdk_threads_enter ();
1075                 modest_ui_actions_check_menu_dimming_rules (win);
1076                 modest_ui_actions_check_toolbar_dimming_rules (win);
1077                 gdk_threads_leave ();
1078         }
1079
1080         return FALSE;
1081 }
1082
1083 static void
1084 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1085 {
1086         static guint dimming_delayer = 0;
1087
1088         if (dimming_delayer > 0)
1089                 g_source_remove (dimming_delayer);
1090
1091         /* We're expecting a lot of changes at the same time so don't
1092            need to check dimming rules for every change that
1093            happens */
1094         dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1095 }
1096
1097
1098 /* On insertions we check if the folder still has the message we are
1099  * showing or do not. If do not, we do nothing. Which means we are still
1100  * not attached to any header folder and thus next/prev buttons are
1101  * still dimmed. Once the message that is shown by msg-view is found, the
1102  * new model of header-view will be attached and the references will be set.
1103  * On each further insertions dimming rules will be checked. However
1104  * this requires extra CPU time at least works.
1105  * (An message might be deleted from TnyFolder and thus will not be
1106  * inserted into the model again for example if it is removed by the
1107  * imap server and the header view is refreshed.)
1108  */
1109 static void 
1110 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1111                                         GtkTreePath *tree_path,
1112                                         GtkTreeIter *tree_iter,
1113                                         ModestMsgViewWindow *window)
1114 {
1115         ModestMsgViewWindowPrivate *priv = NULL; 
1116         TnyHeader *header = NULL;
1117
1118         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1119         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1120
1121         g_assert (model == priv->header_model);
1122         
1123         /* Check if the newly inserted message is the same we are actually
1124          * showing. IF not, we should remain detached from the header model
1125          * and thus prev and next toolbar buttons should remain dimmed. */
1126         gtk_tree_model_get (model, tree_iter, 
1127                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1128                             &header, -1);
1129
1130         if (TNY_IS_HEADER (header)) {
1131                 gchar *uid = NULL;
1132
1133                 uid = modest_tny_folder_get_header_unique_id (header);
1134                 if (!g_str_equal(priv->msg_uid, uid)) {
1135                         check_dimming_rules_after_change (window);
1136                         g_free(uid);
1137                         g_object_unref (G_OBJECT(header));
1138                         return;
1139                 }
1140                 g_free(uid);
1141                 g_object_unref(G_OBJECT(header));
1142         }
1143
1144         if (priv->row_reference) {
1145                 gtk_tree_row_reference_free (priv->row_reference); 
1146         }
1147
1148         /* Setup row_reference for the actual msg. */
1149         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1150         if (priv->row_reference == NULL) {
1151                 g_warning("No reference for msg header item.");
1152                 return;
1153         }
1154
1155         /* Now set up next_row_reference. */
1156         if (priv->next_row_reference) {
1157                 gtk_tree_row_reference_free (priv->next_row_reference); 
1158         }
1159
1160         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1161         select_next_valid_row (priv->header_model,
1162                                &(priv->next_row_reference), FALSE);
1163
1164         /* Connect the remaining callbacks to become able to detect
1165          * changes in header-view. */
1166         priv->row_changed_handler = 
1167                 g_signal_connect (priv->header_model, "row-changed",
1168                                   G_CALLBACK (modest_msg_view_window_on_row_changed),
1169                                   window);
1170         priv->row_deleted_handler = 
1171                 g_signal_connect (priv->header_model, "row-deleted",
1172                                   G_CALLBACK (modest_msg_view_window_on_row_deleted),
1173                                   window);
1174         priv->rows_reordered_handler = 
1175                 g_signal_connect (priv->header_model, "rows-reordered",
1176                                   G_CALLBACK (modest_msg_view_window_on_row_reordered),
1177                                   window);
1178
1179         check_dimming_rules_after_change (window);      
1180 }
1181
1182 static void 
1183 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1184                                          GtkTreePath *arg1,
1185                                          GtkTreeIter *arg2,
1186                                          gpointer arg3,
1187                                          ModestMsgViewWindow *window)
1188 {
1189         ModestMsgViewWindowPrivate *priv = NULL; 
1190         gboolean already_changed = FALSE;
1191
1192         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1193
1194         /* If the current row was reordered select the proper next
1195            valid row. The same if the next row reference changes */
1196         if (priv->row_reference && 
1197             gtk_tree_row_reference_valid (priv->row_reference)) {
1198                 GtkTreePath *path;
1199                 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200                 if (gtk_tree_path_compare (path, arg1) == 0) {
1201                         if (priv->next_row_reference) {
1202                                 gtk_tree_row_reference_free (priv->next_row_reference);
1203                         }
1204                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE);
1206                         already_changed = TRUE;
1207                 }
1208                 gtk_tree_path_free (path);
1209         }
1210         if (!already_changed &&
1211             priv->next_row_reference &&
1212             gtk_tree_row_reference_valid (priv->next_row_reference)) {
1213                 GtkTreePath *path;
1214                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1215                 if (gtk_tree_path_compare (path, arg1) == 0) {
1216                         if (priv->next_row_reference) {
1217                                 gtk_tree_row_reference_free (priv->next_row_reference);
1218                         }
1219                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220                         select_next_valid_row (header_model, &(priv->next_row_reference), FALSE);
1221                 }
1222                 gtk_tree_path_free (path);
1223         }
1224         check_dimming_rules_after_change (window);
1225 }
1226
1227 /* The modest_msg_view_window_update_model_replaced implements update
1228  * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1229  * actually belongs to the header-view is the same as the TnyFolder of
1230  * the message of msg-view or not. If they are different, there is
1231  * nothing to do. If they are the same, then the model has replaced and
1232  * the reference in msg-view shall be replaced from the old model to
1233  * the new model. In this case the view will be detached from it's
1234  * header folder. From this point the next/prev buttons are dimmed.
1235  */
1236 static void 
1237 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1238                                               GtkTreeModel *model,
1239                                               const gchar *tny_folder_id)
1240 {
1241         ModestMsgViewWindowPrivate *priv = NULL; 
1242         ModestMsgViewWindow *window = NULL;
1243
1244         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1245         g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1246
1247         window = MODEST_MSG_VIEW_WINDOW(observer);
1248         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1249
1250         /* If there is an other folder in the header-view then we do
1251          * not care about it's model (msg list). Else if the
1252          * header-view shows the folder the msg shown by us is in, we
1253          * shall replace our model reference and make some check. */
1254         if(model == NULL || tny_folder_id == NULL || 
1255            (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1256                 return;
1257
1258         /* Model is changed(replaced), so we should forget the old
1259          * one. Because there might be other references and there
1260          * might be some change on the model even if we unreferenced
1261          * it, we need to disconnect our signals here. */
1262         if (priv->header_model) {
1263                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1264                                                   priv->row_changed_handler))
1265                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1266                                                     priv->row_changed_handler);
1267                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1268                                                   priv->row_deleted_handler))
1269                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1270                                                     priv->row_deleted_handler);
1271                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1272                                                   priv->row_inserted_handler))
1273                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1274                                                     priv->row_inserted_handler);
1275                 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), 
1276                                                   priv->rows_reordered_handler))
1277                         g_signal_handler_disconnect(G_OBJECT (priv->header_model), 
1278                                                     priv->rows_reordered_handler);
1279
1280                 /* Frees */
1281                 if (priv->row_reference)
1282                         gtk_tree_row_reference_free (priv->row_reference);
1283                 if (priv->next_row_reference)
1284                         gtk_tree_row_reference_free (priv->next_row_reference);
1285                 g_object_unref(priv->header_model);
1286
1287                 /* Initialize */
1288                 priv->row_changed_handler = 0;
1289                 priv->row_deleted_handler = 0;
1290                 priv->row_inserted_handler = 0;
1291                 priv->rows_reordered_handler = 0;
1292                 priv->next_row_reference = NULL;
1293                 priv->row_reference = NULL;
1294                 priv->header_model = NULL;
1295         }
1296
1297         priv->header_model = g_object_ref (model);
1298
1299         /* Also we must connect to the new model for row insertions.
1300          * Only for insertions now. We will need other ones only after
1301          * the msg is show by msg-view is added to the new model. */
1302         priv->row_inserted_handler =
1303                 g_signal_connect (priv->header_model, "row-inserted",
1304                                   G_CALLBACK(modest_msg_view_window_on_row_inserted),
1305                                   window);
1306
1307         modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1308         modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1309 }
1310
1311 gboolean 
1312 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
1313 {
1314         ModestMsgViewWindowPrivate *priv= NULL; 
1315
1316         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1317         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1318
1319         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1320 }
1321
1322 TnyHeader*
1323 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1324 {
1325         ModestMsgViewWindowPrivate *priv= NULL; 
1326         TnyMsg *msg = NULL;
1327         TnyHeader *header = NULL;
1328         GtkTreePath *path = NULL;
1329         GtkTreeIter iter;
1330
1331         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1332         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1333
1334         /* If the message was not obtained from a treemodel,
1335          * for instance if it was opened directly by the search UI:
1336          */
1337         if (priv->header_model == NULL || 
1338             priv->row_reference == NULL ||
1339             !gtk_tree_row_reference_valid (priv->row_reference)) {
1340                 msg = modest_msg_view_window_get_message (self);
1341                 if (msg) {
1342                         header = tny_msg_get_header (msg);
1343                         g_object_unref (msg);
1344                 }
1345                 return header;
1346         }
1347
1348         /* Get iter of the currently selected message in the header view: */
1349         path = gtk_tree_row_reference_get_path (priv->row_reference);
1350         g_return_val_if_fail (path != NULL, NULL);
1351         gtk_tree_model_get_iter (priv->header_model, 
1352                                  &iter, 
1353                                  path);
1354
1355         /* Get current message header */
1356         gtk_tree_model_get (priv->header_model, &iter, 
1357                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1358                             &header, -1);
1359
1360         gtk_tree_path_free (path);
1361         return header;
1362 }
1363
1364 TnyMsg*
1365 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1366 {
1367         ModestMsgViewWindowPrivate *priv;
1368         
1369         g_return_val_if_fail (self, NULL);
1370         
1371         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1372         
1373         return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1374 }
1375
1376 const gchar*
1377 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1378 {
1379         ModestMsgViewWindowPrivate *priv;
1380
1381         g_return_val_if_fail (self, NULL);
1382         
1383         priv  = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1384
1385         return (const gchar*) priv->msg_uid;
1386 }
1387
1388 static void 
1389 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1390                                             gpointer data)
1391 {
1392         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1393         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394         ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1395         gboolean is_active;
1396         GtkAction *action;
1397
1398         is_active = gtk_toggle_action_get_active (toggle);
1399
1400         if (is_active) {
1401                 gtk_widget_show (priv->find_toolbar);
1402                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1403         } else {
1404                 gtk_widget_hide (priv->find_toolbar);
1405         }
1406
1407         /* update the toggle buttons status */
1408         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1409         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1410         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1411         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1412         
1413 }
1414
1415 static void
1416 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1417                                            ModestMsgViewWindow *obj)
1418 {
1419         GtkToggleAction *toggle;
1420         ModestWindowPrivate *parent_priv;
1421         ModestMsgViewWindowPrivate *priv;
1422
1423         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1425         
1426         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1427         gtk_toggle_action_set_active (toggle, FALSE);
1428         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1429 }
1430
1431 static void
1432 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1433                                            ModestMsgViewWindow *obj)
1434 {
1435         gchar *current_search;
1436         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1437
1438         if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1439                 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1440                 return;
1441         }
1442
1443         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
1444
1445         if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1446                 g_free (current_search);
1447                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1448                 return;
1449         }
1450
1451         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1452                 gboolean result;
1453                 g_free (priv->last_search);
1454                 priv->last_search = g_strdup (current_search);
1455                 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1456                                                      priv->last_search);
1457                 if (!result) {
1458                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1459                         g_free (priv->last_search);
1460                         priv->last_search = NULL;
1461                 } else {
1462                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1463                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1464                 }
1465         } else {
1466                 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1467                         hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1468                         g_free (priv->last_search);
1469                         priv->last_search = NULL;
1470                 } else {
1471                         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1472                         hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1473                 }
1474         }
1475         
1476         g_free (current_search);
1477                 
1478 }
1479
1480 static void
1481 modest_msg_view_window_set_zoom (ModestWindow *window,
1482                                  gdouble zoom)
1483 {
1484         ModestMsgViewWindowPrivate *priv;
1485         ModestWindowPrivate *parent_priv;
1486         GtkAction *action = NULL;
1487         gint int_zoom = (gint) rint (zoom*100.0+0.1);
1488      
1489         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1490
1491         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1492         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1493         modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1494
1495         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
1496                                             "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1497
1498         gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1499 }
1500
1501 static gdouble
1502 modest_msg_view_window_get_zoom (ModestWindow *window)
1503 {
1504         ModestMsgViewWindowPrivate *priv;
1505      
1506         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1507
1508         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1509         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1510 }
1511
1512 static gboolean
1513 modest_msg_view_window_zoom_plus (ModestWindow *window)
1514 {
1515         ModestWindowPrivate *parent_priv;
1516         GtkRadioAction *zoom_radio_action;
1517         GSList *group, *node;
1518
1519         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1520         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1521                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1522
1523         group = gtk_radio_action_get_group (zoom_radio_action);
1524
1525         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1526                 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1527                 return FALSE;
1528         }
1529
1530         for (node = group; node != NULL; node = g_slist_next (node)) {
1531                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1532                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1533                         return TRUE;
1534                 }
1535         }
1536         return FALSE;
1537 }
1538
1539 static gboolean
1540 modest_msg_view_window_zoom_minus (ModestWindow *window)
1541 {
1542         ModestWindowPrivate *parent_priv;
1543         GtkRadioAction *zoom_radio_action;
1544         GSList *group, *node;
1545
1546         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1547         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
1548                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1549
1550         group = gtk_radio_action_get_group (zoom_radio_action);
1551
1552         for (node = group; node != NULL; node = g_slist_next (node)) {
1553                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1554                         if (node->next != NULL) {
1555                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1556                                 return TRUE;
1557                         } else {
1558                           hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1559                                 return FALSE;
1560                         }
1561                         break;
1562                 }
1563         }
1564         return FALSE;
1565 }
1566
1567 static gboolean
1568 modest_msg_view_window_key_event (GtkWidget *window,
1569                                   GdkEventKey *event,
1570                                   gpointer userdata)
1571 {
1572         GtkWidget *focus;
1573
1574         focus = gtk_window_get_focus (GTK_WINDOW (window));
1575
1576         /* for the find toolbar case */
1577         if (focus && GTK_IS_ENTRY (focus)) {
1578                 if (event->keyval == GDK_BackSpace) {
1579                         GdkEvent *copy;
1580                         copy = gdk_event_copy ((GdkEvent *) event);
1581                         gtk_widget_event (focus, copy);
1582                         gdk_event_free (copy);
1583                         return TRUE;
1584                 } else 
1585                         return FALSE;
1586         }
1587         if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1588             event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1589             event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1590             event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1591             event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1592             event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1593                 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1594                 /* gboolean return_value; */
1595
1596                 if (event->type == GDK_KEY_PRESS) {
1597                         GtkScrollType scroll_type;
1598                         
1599                         switch (event->keyval) {
1600                         case GDK_Up: 
1601                         case GDK_KP_Up:
1602                                 scroll_type = GTK_SCROLL_STEP_UP; break;
1603                         case GDK_Down: 
1604                         case GDK_KP_Down:
1605                                 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1606                         case GDK_Page_Up:
1607                         case GDK_KP_Page_Up:
1608                                 scroll_type = GTK_SCROLL_PAGE_UP; break;
1609                         case GDK_Page_Down:
1610                         case GDK_KP_Page_Down:
1611                                 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1612                         case GDK_Home:
1613                         case GDK_KP_Home:
1614                                 scroll_type = GTK_SCROLL_START; break;
1615                         case GDK_End:
1616                         case GDK_KP_End:
1617                                 scroll_type = GTK_SCROLL_END; break;
1618                         default: scroll_type = GTK_SCROLL_NONE;
1619                         }
1620                         
1621                         /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",  */
1622                         /*                     scroll_type, FALSE, &return_value); */
1623                         return FALSE;
1624                 } else {
1625                         return FALSE;
1626                 }
1627         } else {
1628                 return FALSE;
1629         }
1630 }
1631
1632 gboolean
1633 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1634 {
1635         GtkTreePath *path;
1636         ModestMsgViewWindowPrivate *priv;
1637         GtkTreeIter tmp_iter;
1638         gboolean is_last_selected;
1639
1640         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1641         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1642
1643         /*if no model (so no rows at all), then virtually we are the last*/
1644         if (!priv->header_model || !priv->row_reference)
1645                 return TRUE;
1646
1647         path = gtk_tree_row_reference_get_path (priv->row_reference);
1648         if (path == NULL)
1649                 return TRUE;
1650
1651         is_last_selected = TRUE;
1652         while (is_last_selected) {
1653                 TnyHeader *header;
1654                 gtk_tree_path_next (path);
1655                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1656                         break;
1657                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1658                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1659                                 &header, -1);
1660                 if (header) {
1661                         if (msg_is_visible (header, priv->is_outbox))
1662                                 is_last_selected = FALSE;
1663                         g_object_unref(G_OBJECT(header));
1664                 }
1665         }
1666         gtk_tree_path_free (path);
1667         return is_last_selected;
1668 }
1669
1670 gboolean
1671 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1672 {
1673         ModestMsgViewWindowPrivate *priv;
1674
1675         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1676         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1677
1678         return priv->header_model != NULL;
1679 }
1680
1681 gboolean
1682 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1683 {
1684         ModestMsgViewWindowPrivate *priv;
1685
1686         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1687         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1688
1689         return priv->is_search_result;
1690 }
1691
1692 static gboolean
1693 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1694 {
1695         return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1696                 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1697         
1698 }
1699
1700 gboolean
1701 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1702 {
1703         GtkTreePath *path;
1704         ModestMsgViewWindowPrivate *priv;
1705         gboolean is_first_selected;
1706         GtkTreeIter tmp_iter;
1707 /*      gchar * path_string;*/
1708
1709         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1710         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1711
1712         /*if no model (so no rows at all), then virtually we are the first*/
1713         if (!priv->header_model || !priv->row_reference)
1714                 return TRUE;
1715
1716         path = gtk_tree_row_reference_get_path (priv->row_reference);
1717         if (!path)
1718                 return TRUE;
1719
1720 /*      path_string = gtk_tree_path_to_string (path);
1721         is_first_selected = strcmp (path_string, "0");
1722
1723         g_free (path_string);
1724         gtk_tree_path_free (path);
1725
1726         return is_first_selected;*/
1727
1728         is_first_selected = TRUE;
1729         while (is_first_selected) {
1730                 TnyHeader *header;
1731                 if(!gtk_tree_path_prev (path))
1732                         break;
1733                 /* Here the 'if' is needless for logic, but let make sure
1734                  * iter is valid for gtk_tree_model_get. */
1735                 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1736                         break;
1737                 gtk_tree_model_get (priv->header_model, &tmp_iter,
1738                                 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1739                                 &header, -1);
1740                 if (header) {
1741                         if (msg_is_visible (header, priv->is_outbox))
1742                                 is_first_selected = FALSE;
1743                         g_object_unref(G_OBJECT(header));
1744                 }
1745         }
1746         gtk_tree_path_free (path);
1747         return is_first_selected;
1748 }
1749
1750 typedef struct {
1751         TnyHeader *header;
1752         GtkTreeRowReference *row_reference;
1753 } MsgReaderInfo;
1754
1755 static void
1756 message_reader_performer (gboolean canceled, 
1757                           GError *err,
1758                           GtkWindow *parent_window, 
1759                           TnyAccount *account, 
1760                           gpointer user_data)
1761 {
1762         ModestMailOperation *mail_op = NULL;
1763         MsgReaderInfo *info;
1764
1765         info = (MsgReaderInfo *) user_data;
1766         if (canceled || err) {
1767                 goto frees;
1768         }
1769
1770         /* Register the header - it'll be unregistered in the callback */
1771         modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1772
1773         /* New mail operation */
1774         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1775                                                                  modest_ui_actions_disk_operations_error_handler, 
1776                                                                  NULL, NULL);
1777                                 
1778         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1779         modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1780         g_object_unref (mail_op);
1781
1782         /* Update dimming rules */
1783         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1784         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1785
1786  frees:
1787         /* Frees. The row_reference will be freed by the view_msg_cb callback */
1788         g_object_unref (info->header);
1789         g_slice_free (MsgReaderInfo, info);
1790 }
1791
1792
1793 /**
1794  * Reads the message whose summary item is @header. It takes care of
1795  * several things, among others:
1796  *
1797  * If the message was not previously downloaded then ask the user
1798  * before downloading. If there is no connection launch the connection
1799  * dialog. Update toolbar dimming rules.
1800  *
1801  * Returns: TRUE if the mail operation was started, otherwise if the
1802  * user do not want to download the message, or if the user do not
1803  * want to connect, then the operation is not issued
1804  **/
1805 static gboolean
1806 message_reader (ModestMsgViewWindow *window,
1807                 ModestMsgViewWindowPrivate *priv,
1808                 TnyHeader *header,
1809                 GtkTreeRowReference *row_reference)
1810 {
1811         gboolean already_showing = FALSE;
1812         ModestWindow *msg_window = NULL;
1813         ModestWindowMgr *mgr;
1814         TnyAccount *account;
1815         TnyFolder *folder;
1816         MsgReaderInfo *info;
1817
1818         g_return_val_if_fail (row_reference != NULL, FALSE);
1819
1820         mgr = modest_runtime_get_window_mgr ();
1821         already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1822         if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1823                 gboolean retval;
1824                 if (msg_window)
1825                         gtk_window_present (GTK_WINDOW (msg_window));
1826                 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1827                 return TRUE;
1828         }
1829
1830         /* Msg download completed */
1831         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1832                 /* Ask the user if he wants to download the message if
1833                    we're not online */
1834                 if (!tny_device_is_online (modest_runtime_get_device())) {
1835                         GtkResponseType response;
1836
1837                         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1838                                                                             _("mcen_nc_get_msg"));
1839                         if (response == GTK_RESPONSE_CANCEL)
1840                                 return FALSE;
1841                 
1842                         folder = tny_header_get_folder (header);
1843                         info = g_slice_new (MsgReaderInfo);
1844                         info->header = g_object_ref (header);
1845                         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1846
1847                         /* Offer the connection dialog if necessary */
1848                         modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, 
1849                                                                        TRUE,
1850                                                                        TNY_FOLDER_STORE (folder),
1851                                                                        message_reader_performer, 
1852                                                                        info);
1853                         g_object_unref (folder);
1854                         return TRUE;
1855                 }
1856         }
1857         
1858         folder = tny_header_get_folder (header);
1859         account = tny_folder_get_account (folder);
1860         info = g_slice_new (MsgReaderInfo);
1861         info->header = g_object_ref (header);
1862         info->row_reference = gtk_tree_row_reference_copy (row_reference);
1863         
1864         message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1865         g_object_unref (account);
1866         g_object_unref (folder);
1867
1868         return TRUE;
1869 }
1870
1871 gboolean        
1872 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1873 {
1874         ModestMsgViewWindowPrivate *priv;
1875         GtkTreePath *path= NULL;
1876         GtkTreeIter tmp_iter;
1877         TnyHeader *header;
1878         gboolean retval = TRUE;
1879         GtkTreeRowReference *row_reference = NULL;
1880
1881         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1882         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1883
1884         if (!priv->row_reference)
1885                 return FALSE;
1886
1887         /* Update the next row reference if it's not valid. This could
1888            happen if for example the header which it was pointing to,
1889            was deleted. The best place to do it is in the row-deleted
1890            handler but the tinymail model do not work like the glib
1891            tree models and reports the deletion when the row is still
1892            there */
1893         if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1894                 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1895                         priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1896                         select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1897                 }
1898         }
1899         if (priv->next_row_reference)
1900                 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1901         if (path == NULL)
1902                 return FALSE;
1903
1904         row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1905
1906         gtk_tree_model_get_iter (priv->header_model,
1907                                  &tmp_iter,
1908                                  path);
1909         gtk_tree_path_free (path);
1910
1911         gtk_tree_model_get (priv->header_model, &tmp_iter, 
1912                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1913                             &header, -1);
1914         
1915         /* Read the message & show it */
1916         if (!message_reader (window, priv, header, row_reference)) {
1917                 retval = FALSE;
1918         }
1919         gtk_tree_row_reference_free (row_reference);
1920
1921         /* Free */
1922         g_object_unref (header);
1923
1924         return retval;
1925 }
1926
1927 gboolean        
1928 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1929 {
1930         ModestMsgViewWindowPrivate *priv = NULL;
1931         GtkTreePath *path;
1932         gboolean finished = FALSE;
1933         gboolean retval = FALSE;
1934
1935         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1936         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1937
1938         /* Return inmediatly if there is no header model */
1939         if (!priv->header_model || !priv->row_reference)
1940                 return FALSE;
1941
1942         path = gtk_tree_row_reference_get_path (priv->row_reference);
1943         while (!finished && gtk_tree_path_prev (path)) {
1944                 TnyHeader *header;
1945                 GtkTreeIter iter;
1946
1947                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1948                 gtk_tree_model_get (priv->header_model, &iter, 
1949                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1950                                     &header, -1);
1951                 finished = TRUE;
1952                 if (header) {
1953                         if (msg_is_visible (header, priv->is_outbox)) {
1954                                 GtkTreeRowReference *row_reference;
1955                                 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1956                                 /* Read the message & show it */
1957                                 retval = message_reader (window, priv, header, row_reference);
1958                                 gtk_tree_row_reference_free (row_reference);
1959                         } else {
1960                                 finished = FALSE;
1961                         }
1962                         g_object_unref (header);
1963                 }
1964         }
1965
1966         gtk_tree_path_free (path);
1967         return retval;
1968 }
1969
1970 static void
1971 view_msg_cb (ModestMailOperation *mail_op, 
1972              TnyHeader *header, 
1973              gboolean canceled,
1974              TnyMsg *msg, 
1975              GError *error,
1976              gpointer user_data)
1977 {
1978         ModestMsgViewWindow *self = NULL;
1979         ModestMsgViewWindowPrivate *priv = NULL;
1980         GtkTreeRowReference *row_reference = NULL;
1981
1982         /* Unregister the header (it was registered before creating the mail operation) */
1983         modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1984
1985         row_reference = (GtkTreeRowReference *) user_data;
1986         if (canceled) {
1987                 gtk_tree_row_reference_free (row_reference);
1988                 return;
1989         }
1990         
1991         /* If there was any error */
1992         if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1993                 gtk_tree_row_reference_free (row_reference);                    
1994                 return;
1995         }
1996
1997         /* Get the window */ 
1998         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1999         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2000         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2001
2002         /* Update the row reference */
2003         if (priv->row_reference != NULL) {
2004                 gtk_tree_row_reference_free (priv->row_reference);
2005                 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2006                 if (priv->next_row_reference != NULL) {
2007                         gtk_tree_row_reference_free (priv->next_row_reference);
2008                 }
2009                 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2010                 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
2011         }
2012
2013         /* Mark header as read */
2014         if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2015                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2016
2017         /* Set new message */
2018         if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2019                 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2020                 modest_msg_view_window_update_priority (self);
2021                 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2022                 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2023         }
2024
2025         /* Set the new message uid of the window  */
2026         if (priv->msg_uid) {
2027                 g_free (priv->msg_uid);
2028                 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2029         }
2030
2031         /* Notify the observers */
2032         g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], 
2033                        0, priv->header_model, priv->row_reference);
2034
2035         /* Frees */
2036         g_object_unref (self);
2037         gtk_tree_row_reference_free (row_reference);            
2038 }
2039
2040 TnyFolderType
2041 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2042 {
2043         ModestMsgViewWindowPrivate *priv;
2044         TnyMsg *msg;
2045         TnyFolderType folder_type;
2046
2047         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2048
2049         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2050
2051         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2052         if (msg) {
2053                 TnyFolder *folder;
2054
2055                 folder = tny_msg_get_folder (msg);
2056                 if (folder) {
2057                         folder_type = modest_tny_folder_guess_folder_type (folder);
2058                         g_object_unref (folder);
2059                 }
2060                 g_object_unref (msg);
2061         }
2062
2063         return folder_type;
2064 }
2065
2066
2067 static void
2068 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2069 {
2070         ModestMsgViewWindowPrivate *priv;
2071         TnyHeader *header = NULL;
2072         TnyHeaderFlags flags = 0;
2073
2074         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2075
2076         if (priv->header_model && priv->row_reference) {
2077                 GtkTreeIter iter;
2078                 GtkTreePath *path = NULL;
2079
2080                 path = gtk_tree_row_reference_get_path (priv->row_reference);
2081                 g_return_if_fail (path != NULL);
2082                 gtk_tree_model_get_iter (priv->header_model, 
2083                                          &iter, 
2084                                          gtk_tree_row_reference_get_path (priv->row_reference));
2085
2086                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2087                                     &header, -1);
2088                 gtk_tree_path_free (path);
2089         } else {
2090                 TnyMsg *msg;
2091                 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2092                 if (msg) {
2093                         header = tny_msg_get_header (msg);
2094                         g_object_unref (msg);
2095                 }
2096         }
2097
2098         if (header) {
2099                 flags = tny_header_get_flags (header);
2100                 g_object_unref(G_OBJECT(header));
2101         }
2102
2103         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2104
2105 }
2106
2107 static void
2108 toolbar_resize (ModestMsgViewWindow *self)
2109 {
2110         ModestMsgViewWindowPrivate *priv = NULL;
2111         ModestWindowPrivate *parent_priv = NULL;
2112         GtkWidget *widget;
2113         gint static_button_size;
2114         ModestWindowMgr *mgr;
2115
2116         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2117         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2118         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2119
2120         mgr = modest_runtime_get_window_mgr ();
2121         static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2122
2123         if (parent_priv->toolbar) {
2124                 /* left size buttons */
2125                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2126                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2127                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2128                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2129                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2130                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2131                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2132                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2133                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2134                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2135                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2136                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2137                 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2138                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2139                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2140                 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2141                 
2142                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2143                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2144                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2145                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2146                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2147                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2148                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2149                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2150         }
2151                 
2152 }
2153
2154 static gboolean
2155 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2156 {
2157         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2158                 ModestWindowPrivate *parent_priv;
2159                 ModestWindowMgr *mgr;
2160                 gboolean is_fullscreen;
2161                 GtkAction *fs_toggle_action;
2162                 gboolean active;
2163
2164                 mgr = modest_runtime_get_window_mgr ();
2165                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2166
2167                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2168                 
2169                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2170                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2171                 if (is_fullscreen != active) {
2172                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2173                 }
2174                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2175         }
2176
2177         return FALSE;
2178
2179 }
2180
2181 static void
2182 modest_msg_view_window_show_toolbar (ModestWindow *self,
2183                                      gboolean show_toolbar)
2184 {
2185         ModestMsgViewWindowPrivate *priv = NULL;
2186         ModestWindowPrivate *parent_priv;
2187         GtkWidget *reply_button = NULL, *menu = NULL;
2188         GtkWidget *placeholder = NULL;
2189         gint insert_index;
2190         const gchar *action_name;
2191         GtkAction *action;
2192         
2193         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2194         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2195
2196         /* Set optimized view status */
2197         priv->optimized_view = !show_toolbar;
2198
2199         if (!parent_priv->toolbar) {
2200                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2201                                                                   "/ToolBar");
2202                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2203
2204                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2205                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2206                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2207                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2208                 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2209
2210                 /* Add ProgressBar (Transfer toolbar) */ 
2211                 priv->progress_bar = modest_progress_bar_new ();
2212                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2213                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2214                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2215                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2216                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2217                 
2218                 /* Connect cancel 'clicked' signal to abort progress mode */
2219                 g_signal_connect(priv->cancel_toolitem, "clicked",
2220                                  G_CALLBACK(cancel_progressbar),
2221                                  self);
2222                 
2223                 /* Add it to the observers list */
2224                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2225
2226                 /* Add to window */
2227                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
2228                                            GTK_TOOLBAR (parent_priv->toolbar));
2229
2230                 /* Set reply button tap and hold menu */        
2231                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2232                                                           "/ToolBar/ToolbarMessageReply");
2233                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
2234                                                   "/ToolbarReplyCSM");
2235                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2236         }
2237
2238         if (show_toolbar) {
2239                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
2240                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2241                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2242
2243                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2244                 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) 
2245                         set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2246                 else
2247                         set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2248
2249         } else {
2250                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2251                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2252         }
2253
2254         /* Update also the actions (to update the toggles in the
2255            menus), we have to do it manually because some other window
2256            of the same time could have changed it (remember that the
2257            toolbar fullscreen mode is shared by all the windows of the
2258            same type */
2259         if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2260                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2261         else
2262                 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2263
2264         action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2265         modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2266                                                             show_toolbar);
2267 }
2268
2269 static void 
2270 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2271                                                GdkEvent *event,
2272                                                ModestMsgViewWindow *window)
2273 {
2274         if (!GTK_WIDGET_VISIBLE (window))
2275                 return;
2276
2277         modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2278 }
2279
2280 gboolean 
2281 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2282 {
2283         ModestMsgViewWindowPrivate *priv;
2284         
2285         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); 
2286         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2287
2288         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2289 }
2290
2291 static void
2292 cancel_progressbar (GtkToolButton *toolbutton,
2293                     ModestMsgViewWindow *self)
2294 {
2295         GSList *tmp;
2296         ModestMsgViewWindowPrivate *priv;
2297         
2298         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2299
2300         /* Get operation observers and cancel its current operation */
2301         tmp = priv->progress_widgets;
2302         while (tmp) {
2303                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2304                 tmp=g_slist_next(tmp);
2305         }
2306 }
2307 static gboolean
2308 observers_empty (ModestMsgViewWindow *self)
2309 {
2310         GSList *tmp = NULL;
2311         ModestMsgViewWindowPrivate *priv;
2312         gboolean is_empty = TRUE;
2313         guint pending_ops = 0;
2314  
2315         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2316         tmp = priv->progress_widgets;
2317
2318         /* Check all observers */
2319         while (tmp && is_empty)  {
2320                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2321                 is_empty = pending_ops == 0;
2322                 
2323                 tmp = g_slist_next(tmp);
2324         }
2325         
2326         return is_empty;
2327 }
2328
2329 static void
2330 on_account_removed (TnyAccountStore *account_store, 
2331                     TnyAccount *account,
2332                     gpointer user_data)
2333 {
2334         /* Do nothing if it's a transport account, because we only
2335            show the messages of a store account */
2336         if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2337                 const gchar *parent_acc = NULL;
2338                 const gchar *our_acc = NULL;
2339
2340                 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2341                 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2342
2343                 /* Close this window if I'm showing a message of the removed account */
2344                 if (strcmp (parent_acc, our_acc) == 0)
2345                         modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2346         }
2347 }
2348
2349 static void 
2350 on_mail_operation_started (ModestMailOperation *mail_op,
2351                            gpointer user_data)
2352 {
2353         ModestMsgViewWindow *self;
2354         ModestMailOperationTypeOperation op_type;
2355         GSList *tmp;
2356         ModestMsgViewWindowPrivate *priv;
2357         GObject *source = NULL;
2358
2359         self = MODEST_MSG_VIEW_WINDOW (user_data);
2360         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2361         op_type = modest_mail_operation_get_type_operation (mail_op);
2362         tmp = priv->progress_widgets;
2363         source = modest_mail_operation_get_source(mail_op);
2364         if (G_OBJECT (self) == source) {
2365                 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2366                         set_toolbar_transfer_mode(self);
2367                         while (tmp) {
2368                                 modest_progress_object_add_operation (
2369                                                 MODEST_PROGRESS_OBJECT (tmp->data),
2370                                                 mail_op);
2371                                 tmp = g_slist_next (tmp);
2372                         }
2373                 }
2374         }
2375         g_object_unref (source);
2376 }
2377
2378 static void 
2379 on_mail_operation_finished (ModestMailOperation *mail_op,
2380                             gpointer user_data)
2381 {
2382         ModestMsgViewWindow *self;
2383         ModestMailOperationTypeOperation op_type;
2384         GSList *tmp;
2385         ModestMsgViewWindowPrivate *priv;
2386         
2387         self = MODEST_MSG_VIEW_WINDOW (user_data);
2388         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2389         op_type = modest_mail_operation_get_type_operation (mail_op);
2390         tmp = priv->progress_widgets;
2391         
2392         if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2393                 while (tmp) {
2394                         modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2395                                                                  mail_op);
2396                         tmp = g_slist_next (tmp);
2397                 }
2398
2399                 /* If no more operations are being observed, NORMAL mode is enabled again */
2400                 if (observers_empty (self)) {
2401                         set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2402                 }
2403
2404                 /* Update dimming rules. We have to do this right here
2405                    and not in view_msg_cb because at that point the
2406                    transfer mode is still enabled so the dimming rule
2407                    won't let the user delete the message that has been
2408                    readed for example */
2409                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2410                 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2411         }
2412 }
2413
2414 static void
2415 on_queue_changed (ModestMailOperationQueue *queue,
2416                   ModestMailOperation *mail_op,
2417                   ModestMailOperationQueueNotification type,
2418                   ModestMsgViewWindow *self)
2419 {       
2420         ModestMsgViewWindowPrivate *priv;
2421
2422         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2423
2424         /* If this operations was created by another window, do nothing */
2425         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
2426             return;
2427
2428         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2429                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2430                                                                G_OBJECT (mail_op),
2431                                                                "operation-started",
2432                                                                G_CALLBACK (on_mail_operation_started),
2433                                                                self);
2434                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2435                                                                G_OBJECT (mail_op),
2436                                                                "operation-finished",
2437                                                                G_CALLBACK (on_mail_operation_finished),
2438                                                                self);
2439         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2440                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2441                                                                   G_OBJECT (mail_op),
2442                                                                   "operation-started");
2443                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2444                                                                   G_OBJECT (mail_op),
2445                                                                   "operation-finished");
2446         }
2447 }
2448
2449 TnyList *
2450 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
2451 {
2452         ModestMsgViewWindowPrivate *priv;
2453         TnyList *selected_attachments = NULL;
2454         
2455         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2456         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2457
2458         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2459         
2460         return selected_attachments;
2461 }
2462
2463 typedef struct {
2464         gchar *filepath;
2465         GtkWidget *banner;
2466         guint banner_idle_id;
2467 } DecodeAsyncHelper;
2468
2469 static gboolean
2470 decode_async_banner_idle (gpointer user_data)
2471 {
2472         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2473
2474         helper->banner_idle_id = 0;
2475         helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2476         g_object_ref (helper->banner);
2477
2478         return FALSE;
2479 }
2480
2481 static void
2482 on_decode_to_stream_async_handler (TnyMimePart *mime_part, 
2483                                    gboolean cancelled, 
2484                                    TnyStream *stream, 
2485                                    GError *err, 
2486                                    gpointer user_data)
2487 {
2488         DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2489
2490         if (helper->banner_idle_id > 0) {
2491                 g_source_remove (helper->banner_idle_id);
2492                 helper->banner_idle_id = 0;
2493         }
2494         if (helper->banner) {
2495                 gtk_widget_destroy (helper->banner);
2496         }
2497         if (cancelled || err) {
2498                 modest_platform_information_banner (NULL, NULL, 
2499                                                     _("mail_ib_file_operation_failed"));
2500                 goto free;
2501         }
2502
2503         /* make the file read-only */
2504         g_chmod(helper->filepath, 0444);
2505         
2506         /* Activate the file */
2507         modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2508
2509  free:
2510         /* Frees */
2511         g_free (helper->filepath);
2512         g_object_unref (helper->banner);
2513         g_slice_free (DecodeAsyncHelper, helper);
2514 }
2515
2516 void
2517 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2518 {
2519         ModestMsgViewWindowPrivate *priv;
2520         const gchar *msg_uid;
2521         gchar *attachment_uid = NULL;
2522         gint attachment_index = 0;
2523         TnyList *attachments;
2524
2525         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2526         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2527         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2528
2529         msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2530         attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2531         attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2532         g_object_unref (attachments);
2533         
2534         if (msg_uid && attachment_index >= 0) {
2535                 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2536         }
2537
2538         if (mime_part == NULL) {
2539                 gboolean error = FALSE;
2540                 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2541                 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2542                         error = TRUE;
2543                 } else if (tny_list_get_length (selected_attachments) > 1) {
2544                         hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2545                         error = TRUE;
2546                 } else {
2547                         TnyIterator *iter;
2548                         iter = tny_list_create_iterator (selected_attachments);
2549                         mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2550                         g_object_unref (iter);
2551                 }
2552                 g_object_unref (selected_attachments);
2553
2554                 if (error)
2555                         return;
2556         } else {
2557                 g_object_ref (mime_part);
2558         }
2559
2560         if (tny_mime_part_is_purged (mime_part)) {
2561                 g_object_unref (mime_part);
2562                 return;
2563         }
2564
2565         if (!modest_tny_mime_part_is_msg (mime_part)) {
2566                 gchar *filepath = NULL;
2567                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2568                 gboolean show_error_banner = FALSE;
2569                 TnyFsStream *temp_stream = NULL;
2570                 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2571                                                                &filepath);
2572                 
2573                 if (temp_stream != NULL) {
2574                         DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2575                         helper->filepath = g_strdup (filepath);
2576                         helper->banner = NULL;
2577                         helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2578                         tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream), 
2579                                                               on_decode_to_stream_async_handler, 
2580                                                               NULL, 
2581                                                               helper);
2582                         g_object_unref (temp_stream);
2583                         /* NOTE: files in the temporary area will be automatically
2584                          * cleaned after some time if they are no longer in use */
2585                 } else {
2586                         if (filepath != NULL) {
2587                                 const gchar *content_type;
2588                                 /* the file may already exist but it isn't writable,
2589                                  * let's try to open it anyway */
2590                                 content_type = tny_mime_part_get_content_type (mime_part);
2591                                 modest_platform_activate_file (filepath, content_type);
2592                                 g_free (filepath);
2593                         } else {
2594                                 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2595                                 show_error_banner = TRUE;
2596                         }
2597                 }
2598                 if (show_error_banner)
2599                         modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2600         } else {
2601                 /* message attachment */
2602                 TnyHeader *header = NULL;
2603                 ModestWindowMgr *mgr;
2604                 ModestWindow *msg_win = NULL;
2605                 gboolean found;
2606                 
2607                 header = tny_msg_get_header (TNY_MSG (mime_part));
2608                 mgr = modest_runtime_get_window_mgr ();         
2609                 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2610
2611                 if (found) {
2612                         if (msg_win)                            /* there is already a window for this uid; top it */
2613                                 gtk_window_present (GTK_WINDOW(msg_win));
2614                         else 
2615                                 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2616                                  * thus, we don't do anything */
2617                                 g_warning ("window for is already being created");
2618                 } else { 
2619                         /* it's not found, so create a new window for it */
2620                         modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2621                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2622                         if (!account)
2623                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2624                         msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2625                         modest_window_set_zoom (MODEST_WINDOW (msg_win), 
2626                                                 modest_window_get_zoom (MODEST_WINDOW (window)));
2627                         modest_window_mgr_register_window (mgr, msg_win);
2628                         gtk_widget_show_all (GTK_WIDGET (msg_win));
2629                 }
2630         }
2631         g_object_unref (mime_part);
2632 }
2633
2634 typedef struct
2635 {
2636         gchar *filename;
2637         TnyMimePart *part;
2638 } SaveMimePartPair;
2639
2640 typedef struct
2641 {
2642         GList *pairs;
2643         GtkWidget *banner;
2644         GnomeVFSResult result;
2645 } SaveMimePartInfo;
2646
2647 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2648 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2649 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2650 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2651
2652 static void 
2653 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2654 {
2655         
2656         GList *node;
2657         for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2658                 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2659                 g_free (pair->filename);
2660                 g_object_unref (pair->part);
2661                 g_slice_free (SaveMimePartPair, pair);
2662         }
2663         g_list_free (info->pairs);
2664         info->pairs = NULL;
2665         if (with_struct) {
2666                 gtk_widget_destroy (info->banner);
2667                 g_slice_free (SaveMimePartInfo, info);
2668         }
2669 }
2670
2671 static gboolean
2672 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2673 {
2674         if (info->pairs != NULL) {
2675                 save_mime_part_to_file (info);
2676         } else {
2677                 /* This is a GDK lock because we are an idle callback and
2678                  * hildon_banner_show_information is or does Gtk+ code */
2679
2680                 gdk_threads_enter (); /* CHECKED */
2681                 save_mime_part_info_free (info, TRUE);
2682                 if (info->result == GNOME_VFS_OK) {
2683                         hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2684                 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2685                         hildon_banner_show_information (NULL, NULL, dgettext("ke-recv", 
2686                                                                              "cerm_device_memory_full"));
2687                 } else {
2688                         hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2689                 }
2690                 gdk_threads_leave (); /* CHECKED */
2691         }
2692
2693         return FALSE;
2694 }
2695
2696 static gpointer
2697 save_mime_part_to_file (SaveMimePartInfo *info)
2698 {
2699         GnomeVFSHandle *handle;
2700         TnyStream *stream;
2701         SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2702
2703         info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2704         if (info->result == GNOME_VFS_OK) {
2705                 GError *error = NULL;
2706                 stream = tny_vfs_stream_new (handle);
2707                 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2708                         g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2709                         
2710                         info->result = GNOME_VFS_ERROR_IO;
2711                 }
2712                 g_object_unref (G_OBJECT (stream));
2713                 g_object_unref (pair->part);
2714                 g_slice_free (SaveMimePartPair, pair);
2715                 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2716         } else {
2717                 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2718                 save_mime_part_info_free (info, FALSE);
2719         }
2720
2721         g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2722         return NULL;
2723 }
2724
2725 static void
2726 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2727 {
2728         gboolean is_ok = TRUE;
2729         gint replaced_files = 0;
2730         const GList *files = info->pairs;
2731         const GList *iter;
2732
2733         for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2734                 SaveMimePartPair *pair = iter->data;
2735                 if (modest_utils_file_exists (pair->filename)) {
2736                         replaced_files++;
2737                 }
2738         }
2739         if (replaced_files) {
2740                 GtkWidget *confirm_overwrite_dialog;
2741                 const gchar *message = (replaced_files == 1) ?
2742                         _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2743                 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2744                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2745                         is_ok = FALSE;
2746                 }
2747                 gtk_widget_destroy (confirm_overwrite_dialog);
2748         }
2749
2750         if (!is_ok) {
2751                 save_mime_part_info_free (info, TRUE);
2752         } else {
2753                 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, 
2754                                                                   _CS("sfil_ib_saving"));
2755                 info->banner = banner;
2756                 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2757         }
2758
2759 }
2760
2761 void
2762 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2763 {
2764         ModestMsgViewWindowPrivate *priv;
2765         GList *files_to_save = NULL;
2766         GtkWidget *save_dialog = NULL;
2767         gchar *folder = NULL;
2768         const gchar *filename = NULL;
2769         gchar *save_multiple_str = NULL;
2770
2771         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2772         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2773
2774         if (mime_parts == NULL) {
2775                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2776                 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2777                         return;
2778         } else {
2779                 g_object_ref (mime_parts);
2780         }
2781
2782         /* prepare dialog */
2783         if (tny_list_get_length (mime_parts) == 1) {
2784                 TnyIterator *iter;
2785                 /* only one attachment selected */
2786                 iter = tny_list_create_iterator (mime_parts);
2787                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2788                 g_object_unref (iter);
2789                 if (!modest_tny_mime_part_is_msg (mime_part) && 
2790                     modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2791                     !tny_mime_part_is_purged (mime_part)) {
2792                         filename = tny_mime_part_get_filename (mime_part);
2793                 } else {
2794                         /* TODO: show any error? */
2795                         g_warning ("Tried to save a non-file attachment");
2796                         g_object_unref (mime_parts);
2797                         return;
2798                 }
2799                 g_object_unref (mime_part);
2800         } else {
2801                 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), 
2802                                                      tny_list_get_length (mime_parts));
2803         }
2804         
2805         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), 
2806                                                       GTK_FILE_CHOOSER_ACTION_SAVE);
2807
2808         /* set folder */
2809         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2810         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2811         g_free (folder);
2812
2813         /* set filename */
2814         if (filename != NULL)
2815                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), 
2816                                                    filename);
2817
2818         /* if multiple, set multiple string */
2819         if (save_multiple_str) {
2820                 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2821                 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2822         }
2823                 
2824         /* show dialog */
2825         if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2826                 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2827
2828                 if (!modest_utils_folder_writable (chooser_uri)) {
2829                         hildon_banner_show_information 
2830                                 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2831                 } else {
2832                         TnyIterator *iter;
2833
2834                         iter = tny_list_create_iterator (mime_parts);
2835                         while (!tny_iterator_is_done (iter)) {
2836                                 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2837
2838                                 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2839                                     !tny_mime_part_is_purged (mime_part) &&
2840                                     (tny_mime_part_get_filename (mime_part) != NULL)) {
2841                                         SaveMimePartPair *pair;
2842                                         
2843                                         pair = g_slice_new0 (SaveMimePartPair);
2844                                         if (save_multiple_str) {
2845                                                 gchar *escaped = gnome_vfs_escape_slashes (
2846                                                         tny_mime_part_get_filename (mime_part));
2847                                                 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2848                                                 g_free (escaped);
2849                                         } else {
2850                                                 pair->filename = g_strdup (chooser_uri);
2851                                         }
2852                                         pair->part = mime_part;
2853                                         files_to_save = g_list_prepend (files_to_save, pair);
2854                                 }
2855                                 tny_iterator_next (iter);
2856                         }
2857                         g_object_unref (iter);
2858                 }
2859                 g_free (chooser_uri);
2860         }
2861
2862         gtk_widget_destroy (save_dialog);
2863
2864         g_object_unref (mime_parts);
2865
2866         if (files_to_save != NULL) {
2867                 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2868                 info->pairs = files_to_save;
2869                 info->result = TRUE;
2870                 save_mime_parts_to_file_with_checks (info);
2871         }
2872 }
2873
2874 static gboolean
2875 show_remove_attachment_information (gpointer userdata)
2876 {
2877         ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2878         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2879
2880         /* We're outside the main lock */
2881         gdk_threads_enter ();
2882
2883         if (priv->remove_attachment_banner != NULL) {
2884                 gtk_widget_destroy (priv->remove_attachment_banner);
2885                 g_object_unref (priv->remove_attachment_banner);
2886         }
2887
2888         priv->remove_attachment_banner = g_object_ref (
2889                 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2890
2891         gdk_threads_leave ();
2892
2893         return FALSE;
2894 }
2895
2896 void
2897 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2898 {
2899         ModestMsgViewWindowPrivate *priv;
2900         TnyList *mime_parts = NULL;
2901         gchar *confirmation_message;
2902         gint response;
2903         gint n_attachments;
2904         TnyMsg *msg;
2905         TnyIterator *iter;
2906
2907         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2908         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2909
2910         if (get_all)
2911                 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2912         else
2913                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2914                 
2915         /* Remove already purged messages from mime parts list */
2916         iter = tny_list_create_iterator (mime_parts);
2917         while (!tny_iterator_is_done (iter)) {
2918                 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2919                 tny_iterator_next (iter);
2920                 if (tny_mime_part_is_purged (part)) {
2921                         tny_list_remove (mime_parts, (GObject *) part);
2922                 }
2923                 g_object_unref (part);
2924         }
2925         g_object_unref (iter);
2926
2927         if (tny_list_get_length (mime_parts) == 0) {
2928                 g_object_unref (mime_parts);
2929                 return;
2930         }
2931
2932         n_attachments = tny_list_get_length (mime_parts);
2933         if (n_attachments == 1) {
2934                 gchar *filename;
2935                 TnyMimePart *part;
2936
2937                 iter = tny_list_create_iterator (mime_parts);
2938                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2939                 g_object_unref (iter);
2940                 if (modest_tny_mime_part_is_msg (part)) {
2941                         TnyHeader *header;
2942                         header = tny_msg_get_header (TNY_MSG (part));
2943                         filename = tny_header_dup_subject (header);
2944                         g_object_unref (header);
2945                         if (filename == NULL)
2946                                 filename = g_strdup (_("mail_va_no_subject"));
2947                 } else {
2948                         filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2949                 }
2950                 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2951                 g_free (filename);
2952                 g_object_unref (part);
2953         } else {
2954                 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", 
2955                                                                  "mcen_nc_purge_files_text", 
2956                                                                  n_attachments), n_attachments);
2957         }
2958         response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2959                                                             confirmation_message);
2960         g_free (confirmation_message);
2961
2962         if (response != GTK_RESPONSE_OK) {
2963                 g_object_unref (mime_parts);
2964                 return;
2965         }
2966
2967         priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2968         
2969         iter = tny_list_create_iterator (mime_parts);
2970         while (!tny_iterator_is_done (iter)) {
2971                 TnyMimePart *part;
2972
2973                 part = (TnyMimePart *) tny_iterator_get_current (iter);
2974                 tny_mime_part_set_purged (TNY_MIME_PART (part));
2975                 g_object_unref (part);
2976                 tny_iterator_next (iter);
2977         }
2978         g_object_unref (iter);
2979
2980         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2981         tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2982         tny_msg_rewrite_cache (msg);
2983         tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2984         g_object_unref (msg);
2985
2986         g_object_unref (mime_parts);
2987
2988         if (priv->purge_timeout > 0) {
2989                 g_source_remove (priv->purge_timeout);
2990                 priv->purge_timeout = 0;
2991         }
2992
2993         if (priv->remove_attachment_banner) {
2994                 gtk_widget_destroy (priv->remove_attachment_banner);
2995                 g_object_unref (priv->remove_attachment_banner);
2996                 priv->remove_attachment_banner = NULL;
2997         }
2998
2999
3000 }
3001
3002
3003 static void
3004 update_window_title (ModestMsgViewWindow *window)
3005 {
3006         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3007         TnyMsg *msg = NULL;
3008         TnyHeader *header = NULL;
3009         gchar *subject = NULL;
3010         
3011         msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3012
3013         if (msg != NULL) {
3014                 header = tny_msg_get_header (msg);
3015                 subject = tny_header_dup_subject (header);
3016                 g_object_unref (header);
3017                 g_object_unref (msg);
3018         }
3019
3020         if ((subject == NULL)||(subject[0] == '\0')) {
3021                 g_free (subject);
3022                 subject = g_strdup (_("mail_va_no_subject"));
3023         }
3024
3025         gtk_window_set_title (GTK_WINDOW (window), subject);
3026 }
3027
3028
3029 static void on_move_focus (GtkWidget *widget,
3030                            GtkDirectionType direction,
3031                            gpointer userdata)
3032 {
3033         g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3034 }
3035