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