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