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