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