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