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