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