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