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