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