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