* add "Copy" to the CSM; might fix NB#58246, but needs discussion with the UI designer
[modest] / src / maemo / modest-msg-view-window.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <glib/gi18n.h>
30 #include <string.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
33 #include <tny-header.h>
34 #include <tny-vfs-stream.h>
35 #include "modest-platform.h"
36 #include <modest-maemo-utils.h>
37 #include <modest-tny-msg.h>
38 #include <modest-msg-view-window.h>
39 #include <modest-attachments-view.h>
40 #include <modest-main-window-ui.h>
41 #include "modest-msg-view-window-ui-dimming.h"
42 #include <modest-widget-memory.h>
43 #include <modest-runtime.h>
44 #include <modest-window-priv.h>
45 #include <modest-tny-folder.h>
46 #include <modest-text-utils.h>
47 #include <modest-account-mgr-helpers.h>
48 #include "modest-progress-bar-widget.h"
49 #include "modest-defs.h"
50 #include "modest-hildon-includes.h"
51 #include <gtkhtml/gtkhtml-search.h>
52 #include "modest-ui-dimming-manager.h"
53 #include <gdk/gdkkeysyms.h>
54
55 #define DEFAULT_FOLDER "MyDocs/.documents"
56
57 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
58 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
59 static void  modest_msg_view_window_finalize     (GObject *obj);
60 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
61                                                          gpointer data);
62 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
63                                                         ModestMsgViewWindow *obj);
64 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
65                                                         ModestMsgViewWindow *obj);
66
67 static void  modest_msg_view_window_set_zoom (ModestWindow *window,
68                                               gdouble zoom);
69 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
70 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
71 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
72 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
73                                                           GdkEventKey *event,
74                                                           gpointer userdata);
75 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, 
76                                                            GdkEventWindowState *event, 
77                                                            gpointer userdata);
78 static void modest_msg_view_window_scroll_up (ModestWindow *window);
79 static void modest_msg_view_window_scroll_down (ModestWindow *window);
80 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
81
82 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
83                                                    gboolean show_toolbar);
84
85 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
86                                                            GdkEvent *event,
87                                                            ModestMsgViewWindow *window);
88
89 static void cancel_progressbar (GtkToolButton *toolbutton,
90                                 ModestMsgViewWindow *self);
91
92 static void         on_queue_changed                     (ModestMailOperationQueue *queue,
93                                                           ModestMailOperation *mail_op,
94                                                           ModestMailOperationQueueNotification type,
95                                                           ModestMsgViewWindow *self);
96
97 static void view_msg_cb (ModestMailOperation *mail_op, TnyHeader *header, TnyMsg *msg, gpointer user_data);
98
99 static void set_toolbar_mode (ModestMsgViewWindow *self, 
100                               ModestToolBarModes mode);
101
102 static gboolean set_toolbar_transfer_mode     (ModestMsgViewWindow *self); 
103
104
105 static void update_window_title (ModestMsgViewWindow *window);
106
107 /* list my signals */
108 enum {
109         /* MY_SIGNAL_1, */
110         /* MY_SIGNAL_2, */
111         LAST_SIGNAL
112 };
113
114 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
115         { "FindInMessage",    GTK_STOCK_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
116 };
117
118 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
119         { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
120         { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
121         { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
122         { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
123         { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
124         { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
125 };
126
127 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
128 struct _ModestMsgViewWindowPrivate {
129
130         GtkWidget   *msg_view;
131         GtkWidget   *main_scroll;
132         GtkWidget   *find_toolbar;
133         gchar       *last_search;
134
135         /* Progress observers */
136         GtkWidget        *progress_bar;
137         GSList           *progress_widgets;
138
139         /* Tollbar items */
140         GtkWidget   *progress_toolitem;
141         GtkWidget   *cancel_toolitem;
142         GtkWidget   *prev_toolitem;
143         GtkWidget   *next_toolitem;
144         ModestToolBarModes current_toolbar_mode;
145
146         /* Optimized view enabled */
147         gboolean optimized_view;
148
149         GtkTreeModel *header_model;
150         GtkTreeRowReference *row_reference;
151
152         guint clipboard_change_handler;
153         guint queue_change_handler;
154
155         guint progress_bar_timeout;
156
157         gchar *msg_uid;
158 };
159
160 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
161                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
162                                                     ModestMsgViewWindowPrivate))
163 /* globals */
164 static GtkWindowClass *parent_class = NULL;
165
166 /* uncomment the following if you have defined any signals */
167 /* static guint signals[LAST_SIGNAL] = {0}; */
168
169 GType
170 modest_msg_view_window_get_type (void)
171 {
172         static GType my_type = 0;
173         if (!my_type) {
174                 static const GTypeInfo my_info = {
175                         sizeof(ModestMsgViewWindowClass),
176                         NULL,           /* base init */
177                         NULL,           /* base finalize */
178                         (GClassInitFunc) modest_msg_view_window_class_init,
179                         NULL,           /* class finalize */
180                         NULL,           /* class data */
181                         sizeof(ModestMsgViewWindow),
182                         1,              /* n_preallocs */
183                         (GInstanceInitFunc) modest_msg_view_window_init,
184                         NULL
185                 };
186                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
187                                                   "ModestMsgViewWindow",
188                                                   &my_info, 0);
189         }
190         return my_type;
191 }
192
193 static void
194 save_state (ModestWindow *self)
195 {
196         modest_widget_memory_save (modest_runtime_get_conf (),
197                                    G_OBJECT(self), 
198                                    MODEST_CONF_MSG_VIEW_WINDOW_KEY);
199 }
200
201
202 static void
203 restore_settings (ModestMsgViewWindow *self)
204 {
205         modest_widget_memory_restore (modest_runtime_get_conf (),
206                                       G_OBJECT(self), 
207                                       MODEST_CONF_MSG_VIEW_WINDOW_KEY);
208 }
209
210 static void
211 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
212 {
213         GObjectClass *gobject_class;
214         ModestWindowClass *modest_window_class;
215         gobject_class = (GObjectClass*) klass;
216         modest_window_class = (ModestWindowClass *) klass;
217
218         parent_class            = g_type_class_peek_parent (klass);
219         gobject_class->finalize = modest_msg_view_window_finalize;
220
221         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
222         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
223         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
224         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
225         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
226
227         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
228
229         modest_window_class->save_state_func = save_state;
230 }
231
232 static void
233 modest_msg_view_window_init (ModestMsgViewWindow *obj)
234 {
235         ModestMsgViewWindowPrivate *priv;
236         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
237
238         priv->msg_view      = NULL;
239         priv->header_model  = NULL;
240         priv->clipboard_change_handler = 0;
241         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
242
243         priv->optimized_view  = FALSE;
244         priv->progress_bar_timeout = 0;
245         priv->msg_uid = NULL;
246 }
247
248
249 static gboolean
250 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
251 {
252         ModestMsgViewWindowPrivate *priv = NULL;
253         
254         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
255
256         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
257
258         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
259         
260         if (priv->progress_bar_timeout > 0) {
261                 g_source_remove (priv->progress_bar_timeout);
262                 priv->progress_bar_timeout = 0;
263         }
264         
265         return FALSE;
266 }
267
268 static void 
269 set_toolbar_mode (ModestMsgViewWindow *self, 
270                   ModestToolBarModes mode)
271 {
272         ModestWindowPrivate *parent_priv;
273         ModestMsgViewWindowPrivate *priv;
274         GtkAction *widget = NULL;
275
276         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
277
278         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
279         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
280                         
281         /* Sets current toolbar mode */
282         priv->current_toolbar_mode = mode;
283
284         switch (mode) {
285         case TOOLBAR_MODE_NORMAL:
286                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNew");
287                 gtk_action_set_sensitive (widget, TRUE);
288                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
289                 gtk_action_set_sensitive (widget, TRUE);
290                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
291                 gtk_action_set_sensitive (widget, TRUE);
292                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
293                 gtk_action_set_sensitive (widget, TRUE);
294                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
295                 gtk_action_set_sensitive (widget, TRUE);
296
297                 if (priv->prev_toolitem)
298                         gtk_widget_show (priv->prev_toolitem);
299                 
300                 if (priv->next_toolitem)
301                         gtk_widget_show (priv->next_toolitem);
302                         
303                 if (priv->progress_toolitem)
304                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
305                 if (priv->progress_bar)
306                         gtk_widget_hide (priv->progress_bar);
307                         
308                 if (priv->cancel_toolitem)
309                         gtk_widget_hide (priv->cancel_toolitem);
310
311                 /* Hide toolbar if optimized view is enabled */
312                 if (priv->optimized_view) {
313                         gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
314                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
315                 }
316
317                 break;
318         case TOOLBAR_MODE_TRANSFER:
319                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNew");
320                 gtk_action_set_sensitive (widget, FALSE);
321                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
322                 gtk_action_set_sensitive (widget, FALSE);
323                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
324                 gtk_action_set_sensitive (widget, FALSE);
325                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
326                 gtk_action_set_sensitive (widget, FALSE);
327                 widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
328                 gtk_action_set_sensitive (widget, FALSE);
329
330                 if (priv->prev_toolitem)
331                         gtk_widget_hide (priv->prev_toolitem);
332                 
333                 if (priv->next_toolitem)
334                         gtk_widget_hide (priv->next_toolitem);
335                 
336                 if (priv->progress_toolitem)
337                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
338                 if (priv->progress_bar)
339                         gtk_widget_show (priv->progress_bar);
340                         
341                 if (priv->cancel_toolitem)
342                         gtk_widget_show (priv->cancel_toolitem);
343
344                 /* Show toolbar if it's hiden (optimized view ) */
345                 if (priv->optimized_view) {
346                         gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
347                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
348                 }
349
350                 break;
351         default:
352                 g_return_if_reached ();
353         }
354
355 }
356
357
358 static GtkWidget *
359 menubar_to_menu (GtkUIManager *ui_manager)
360 {
361         GtkWidget *main_menu;
362         GtkWidget *menubar;
363         GList *iter;
364
365         /* Create new main menu */
366         main_menu = gtk_menu_new();
367
368         /* Get the menubar from the UI manager */
369         menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
370
371         iter = gtk_container_get_children (GTK_CONTAINER (menubar));
372         while (iter) {
373                 GtkWidget *menu;
374
375                 menu = GTK_WIDGET (iter->data);
376                 gtk_widget_reparent(menu, main_menu);
377
378                 iter = g_list_next (iter);
379         }
380         return main_menu;
381 }
382
383 static void
384 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
385 {
386         GtkWidget *main_vbox;
387         ModestMsgViewWindowPrivate *priv;
388         ModestWindowPrivate *parent_priv;
389         
390         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
391         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
392
393         priv->msg_view = modest_msg_view_new (msg);
394         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
395         main_vbox = gtk_vbox_new  (FALSE, 6);
396
397         /* Menubar */
398         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
399         gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
400         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
401
402         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
403         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
404         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
405
406         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
407         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
408         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
409
410         priv->find_toolbar = hildon_find_toolbar_new (NULL);
411         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
412         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
413         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
414         
415         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);
416         gtk_widget_show_all (GTK_WIDGET(main_vbox));
417         gtk_box_pack_end (GTK_BOX (main_vbox), priv->find_toolbar, FALSE, FALSE, 0);
418
419 }       
420
421
422 static void
423 modest_msg_view_window_finalize (GObject *obj)
424 {
425         ModestMsgViewWindowPrivate *priv;
426
427         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
428         if (priv->clipboard_change_handler > 0) {
429                 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), priv->clipboard_change_handler);
430                 priv->clipboard_change_handler = 0;
431         }
432         if (priv->queue_change_handler > 0) {
433                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), priv->queue_change_handler);
434                 priv->queue_change_handler = 0;
435         }
436         if (priv->header_model != NULL) {
437                 g_object_unref (priv->header_model);
438                 priv->header_model = NULL;
439         }
440
441         /* disconnet operations queue observer */
442         
443         if (priv->progress_bar_timeout > 0) {
444                 g_source_remove (priv->progress_bar_timeout);
445                 priv->progress_bar_timeout = 0;
446         }
447
448         if (priv->row_reference) {
449                 gtk_tree_row_reference_free (priv->row_reference);
450                 priv->row_reference = NULL;
451         }
452
453         if (priv->msg_uid) {
454                 g_free (priv->msg_uid);
455                 priv->msg_uid = NULL;
456         }
457
458         G_OBJECT_CLASS(parent_class)->finalize (obj);
459 }
460
461
462
463 static gboolean
464 on_delete_event (GtkWidget *widget, GdkEvent *event, ModestMsgViewWindow *self)
465 {
466         modest_window_save_state (MODEST_WINDOW (self));
467         return FALSE;
468 }
469
470 ModestWindow *
471 modest_msg_view_window_new_with_header_model (TnyMsg *msg, 
472                                               const gchar *account_name,
473                                               const gchar *msg_uid,
474                                               GtkTreeModel *model, 
475                                               GtkTreeRowReference *row_reference)
476 {
477         ModestMsgViewWindow *window = NULL;
478         ModestMsgViewWindowPrivate *priv = NULL;
479
480         window = MODEST_MSG_VIEW_WINDOW(modest_msg_view_window_new (msg, account_name, msg_uid));
481         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
482
483         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
484
485         g_object_ref (model);
486         priv->header_model = model;
487         priv->row_reference = gtk_tree_row_reference_copy (row_reference);
488
489         modest_msg_view_window_update_priority (window);
490
491         /* Check toolbar dimming rules */
492         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
493
494         return MODEST_WINDOW(window);
495 }
496
497
498 ModestWindow *
499 modest_msg_view_window_new (TnyMsg *msg, 
500                             const gchar *account_name,
501                             const gchar *msg_uid)
502 {
503         ModestMsgViewWindow *self = NULL;
504         GObject *obj = NULL;
505         ModestMsgViewWindowPrivate *priv = NULL;
506         ModestWindowPrivate *parent_priv = NULL;
507         ModestDimmingRulesGroup *menu_rules_group = NULL;
508         ModestDimmingRulesGroup *toolbar_rules_group = NULL;
509         GtkActionGroup *action_group = NULL;
510         GError *error = NULL;
511         GdkPixbuf *window_icon = NULL;
512
513         g_return_val_if_fail (msg, NULL);
514         
515         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
516         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
517         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
518         self = MODEST_MSG_VIEW_WINDOW (obj);
519
520         priv->msg_uid = g_strdup (msg_uid);
521
522         parent_priv->ui_manager = gtk_ui_manager_new();
523         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
524
525         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
526         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
527
528         menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules");
529         toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules");
530
531         /* Add common actions */
532         gtk_action_group_add_actions (action_group,
533                                       modest_action_entries,
534                                       G_N_ELEMENTS (modest_action_entries),
535                                       obj);
536         gtk_action_group_add_toggle_actions (action_group,
537                                              modest_toggle_action_entries,
538                                              G_N_ELEMENTS (modest_toggle_action_entries),
539                                              obj);
540         gtk_action_group_add_toggle_actions (action_group,
541                                              msg_view_toggle_action_entries,
542                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
543                                              obj);
544         gtk_action_group_add_radio_actions (action_group,
545                                             msg_view_zoom_action_entries,
546                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
547                                             100,
548                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
549                                             obj);
550
551         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
552         g_object_unref (action_group);
553
554         /* Load the UI definition */
555         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
556                                          &error);
557         if (error) {
558                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
559                 g_error_free (error);
560                 error = NULL;
561         }
562         /* ****** */
563
564         /* Add common dimming rules */
565         modest_dimming_rules_group_add_rules (menu_rules_group, 
566                                               modest_msg_view_menu_dimming_entries,
567                                               G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
568                                               self);
569         modest_dimming_rules_group_add_rules (toolbar_rules_group, 
570                                               modest_msg_view_toolbar_dimming_entries,
571                                               G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
572                                               self);
573
574         /* Insert dimming rules group for this window */
575         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
576         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
577         g_object_unref (menu_rules_group);
578         g_object_unref (toolbar_rules_group);
579
580         /* Add accelerators */
581         gtk_window_add_accel_group (GTK_WINDOW (obj), 
582                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
583         
584         /* Init window */
585         init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
586         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
587         
588         g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj);
589
590         g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
591                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
592         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
593                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
594         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
595                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
596         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
597                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
598         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
599                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
600
601         g_signal_connect (G_OBJECT (obj), "key-release-event",
602                           G_CALLBACK (modest_msg_view_window_key_release_event),
603                           NULL);
604
605         g_signal_connect (G_OBJECT (obj), "window-state-event",
606                           G_CALLBACK (modest_msg_view_window_window_state_event),
607                           NULL);
608
609         /* Mail Operation Queue */
610         priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
611                                                        "queue-changed",
612                                                        G_CALLBACK (on_queue_changed),
613                                                        obj);
614
615         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
616
617         priv->last_search = NULL;
618
619
620         /* Set window icon */
621         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
622         gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
623
624         /* Init the clipboard actions dim status */
625         modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
626
627         update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
628
629         return MODEST_WINDOW(obj);
630 }
631
632
633 gboolean 
634 modest_msg_view_window_toolbar_on_transfer_mode     (ModestMsgViewWindow *self)
635 {
636         ModestMsgViewWindowPrivate *priv= NULL; 
637
638         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
639         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
640
641         return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
642 }
643
644 TnyHeader*
645 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
646 {
647         ModestMsgViewWindowPrivate *priv= NULL; 
648         TnyHeader *header = NULL;
649         GtkTreeIter iter;
650
651         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
652         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
653
654         /* Get current message iter */
655         gtk_tree_model_get_iter (priv->header_model, 
656                                  &iter, 
657                                  gtk_tree_row_reference_get_path (priv->row_reference));
658
659         /* Get current message header */
660         gtk_tree_model_get (priv->header_model, &iter, 
661                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
662                             &header, -1);
663
664         return header;
665 }
666
667 TnyMsg*
668 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
669 {
670         ModestMsgView *msg_view;
671         ModestMsgViewWindowPrivate *priv;
672
673         g_return_val_if_fail (self, NULL);
674
675         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
676
677         msg_view = MODEST_MSG_VIEW (priv->msg_view);
678
679         return modest_msg_view_get_message (msg_view);
680 }
681
682 const gchar*
683 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
684 {
685         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
686         return (const gchar*) priv->msg_uid;
687 }
688
689 static void 
690 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
691                                             gpointer data)
692 {
693         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
694         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
695
696         if (gtk_toggle_action_get_active (toggle)) {
697                 gtk_widget_show (priv->find_toolbar);
698                 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
699         } else {
700                 gtk_widget_hide (priv->find_toolbar);
701         }
702
703         
704 }
705
706 static void
707 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
708                                            ModestMsgViewWindow *obj)
709 {
710         GtkToggleAction *toggle;
711         ModestWindowPrivate *parent_priv;
712         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
713         
714         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
715         gtk_toggle_action_set_active (toggle, FALSE);
716 }
717
718 static void
719 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
720                                            ModestMsgViewWindow *obj)
721 {
722         gchar *current_search;
723         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
724
725         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
726
727         if ((current_search == NULL) && (strcmp (current_search, "") == 0)) {
728                 g_free (current_search);
729                 return;
730         }
731
732         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
733                 gboolean result;
734                 g_free (priv->last_search);
735                 priv->last_search = g_strdup (current_search);
736                 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
737                                                  priv->last_search);
738         } else {
739                 modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view));
740         }
741         
742         g_free (current_search);
743                 
744 }
745
746 static void
747 modest_msg_view_window_set_zoom (ModestWindow *window,
748                                  gdouble zoom)
749 {
750         ModestMsgViewWindowPrivate *priv;
751      
752         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
753
754         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
755         modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
756 }
757
758 static gdouble
759 modest_msg_view_window_get_zoom (ModestWindow *window)
760 {
761         ModestMsgViewWindowPrivate *priv;
762      
763         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
764
765         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
766         return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
767 }
768
769 static gboolean
770 modest_msg_view_window_zoom_plus (ModestWindow *window)
771 {
772         ModestWindowPrivate *parent_priv;
773         GtkRadioAction *zoom_radio_action;
774         GSList *group, *node;
775
776         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
777         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
778                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
779
780         group = gtk_radio_action_get_group (zoom_radio_action);
781
782         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
783                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_max_zoom_level"));
784                 return FALSE;
785         }
786
787         for (node = group; node != NULL; node = g_slist_next (node)) {
788                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
789                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
790                         return TRUE;
791                 }
792         }
793         return FALSE;
794 }
795
796 static gboolean
797 modest_msg_view_window_zoom_minus (ModestWindow *window)
798 {
799         ModestWindowPrivate *parent_priv;
800         GtkRadioAction *zoom_radio_action;
801         GSList *group, *node;
802
803         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
804         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
805                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
806
807         group = gtk_radio_action_get_group (zoom_radio_action);
808
809         for (node = group; node != NULL; node = g_slist_next (node)) {
810                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
811                         if (node->next != NULL) {
812                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
813                                 return TRUE;
814                         } else {
815                                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_min_zoom_level"));
816                                 return FALSE;
817                         }
818                         break;
819                 }
820         }
821         return FALSE;
822 }
823
824 static gboolean
825 modest_msg_view_window_key_release_event (GtkWidget *window,
826                                           GdkEventKey *event,
827                                           gpointer userdata)
828 {
829         if (event->type == GDK_KEY_RELEASE) {
830                 switch (event->keyval) {
831                 case GDK_Up:
832                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
833                         return TRUE;
834                         break;
835                 case GDK_Down:
836                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
837                         return TRUE;
838                         break;
839                 default:
840                         return FALSE;
841                         break;
842                 };
843         } else {
844                 return FALSE;
845         }
846 }
847
848 static void
849 modest_msg_view_window_scroll_up (ModestWindow *window)
850 {
851         ModestMsgViewWindowPrivate *priv;
852
853         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
854         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE);
855 }
856
857 static void
858 modest_msg_view_window_scroll_down (ModestWindow *window)
859 {
860         ModestMsgViewWindowPrivate *priv;
861
862         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
863         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE);
864 }
865
866 gboolean
867 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
868 {
869         GtkTreePath *path;
870         ModestMsgViewWindowPrivate *priv;
871         GtkTreeIter tmp_iter;
872         gboolean has_next = FALSE;
873
874         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
875         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
876
877         if (priv->header_model) {
878                 path = gtk_tree_row_reference_get_path (priv->row_reference);
879                 if (!path)
880                         return TRUE;
881                 while (!has_next) {
882                         TnyHeader *header;
883                         gtk_tree_path_next (path);
884                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
885                                 break;
886                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
887                                             &header, -1);
888                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
889                                 has_next = TRUE;
890                                 break;
891                         }
892                         
893                 }
894                 return !has_next;
895         } else {
896                 return TRUE;
897         }
898         
899 }
900
901 gboolean
902 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
903 {
904         ModestMsgViewWindowPrivate *priv;
905
906         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
907         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
908
909         return priv->header_model != NULL;
910 }
911
912 gboolean
913 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
914 {
915         GtkTreePath *path;
916         ModestMsgViewWindowPrivate *priv;
917         gboolean result;
918         GtkTreeIter tmp_iter;
919
920         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
921         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
922
923         if (priv->header_model) {
924                 gchar * path_string;
925                 path = gtk_tree_row_reference_get_path (priv->row_reference);
926                 if (!path)
927                         return TRUE;
928
929                 path_string = gtk_tree_path_to_string (path);
930                 result = (strcmp (path_string, "0")==0);
931                 if (result) {
932                         g_free (path_string);
933                         gtk_tree_path_free (path);
934                         return result;
935                 }
936
937                 while (result) {
938                         TnyHeader *header;
939
940                         gtk_tree_path_prev (path);
941                         
942                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
943                                 break;
944                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
945                                             &header, -1);
946                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
947                                 result = FALSE;
948                                 break;
949                         }
950
951                         path_string = gtk_tree_path_to_string (path);
952                         if (strcmp(path_string, "0")==0) {
953                                 g_free (path_string);
954                                 break;
955                         }
956                         g_free (path_string);
957                 }
958                 return result;
959         } else {
960                 return TRUE;
961         }
962         
963 }
964
965 gboolean        
966 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
967 {
968         TnyHeaderFlags flags;
969         ModestMailOperation *mail_op = NULL;
970         ModestMsgViewWindowPrivate *priv;
971         GtkTreeIter tmp_iter;
972
973         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
974         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
975
976         if (priv->header_model) {
977                 gtk_tree_model_get_iter (priv->header_model,
978                                          &tmp_iter,
979                                          gtk_tree_row_reference_get_path (priv->row_reference));
980                 while (gtk_tree_model_iter_next (priv->header_model, &tmp_iter)) {
981                         TnyHeader *header;
982                         GtkTreePath *path;
983
984                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
985                                             &header, -1);
986                         if (!header)
987                                 break;
988                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
989                                 continue;
990
991                         /* Update the row reference */
992                         gtk_tree_row_reference_free (priv->row_reference);
993                         path = gtk_tree_model_get_path (priv->header_model, &tmp_iter);
994                         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
995                         gtk_tree_path_free (path);
996
997                         /* Mark as read */
998                         flags = tny_header_get_flags (header);
999                         if (!(flags & TNY_HEADER_FLAG_SEEN))
1000                                 tny_header_set_flags (header, flags | TNY_HEADER_FLAG_SEEN);
1001
1002                         /* Msg download initied */
1003
1004                         /* New mail operation */
1005                         mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, G_OBJECT(window));
1006                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1007                         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, NULL);
1008                         g_object_unref (mail_op);
1009
1010                         /* Update toolbar dimming rules */
1011                         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1012
1013                         return TRUE;
1014                 }
1015         }
1016         return FALSE;           
1017 }
1018
1019 gboolean 
1020 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1021 {
1022         ModestMsgViewWindowPrivate *priv = NULL;
1023         ModestMailOperation *mail_op = NULL;
1024         TnyHeader *header = NULL;
1025         TnyHeaderFlags flags;
1026         GtkTreePath *path = NULL;
1027         GtkTreeIter iter;
1028
1029         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1030         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1031
1032         /* Check that the model is not empty */
1033         if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1034                 return FALSE;
1035
1036         /* Get the header */
1037         gtk_tree_model_get (priv->header_model, 
1038                             &iter, 
1039                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1040                             &header, -1);
1041         
1042         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1043         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1044                 g_object_unref (header);
1045                 return modest_msg_view_window_select_next_message (self);
1046         }
1047         
1048         /* Update the row reference */
1049         gtk_tree_row_reference_free (priv->row_reference);
1050         path = gtk_tree_path_new_first ();
1051         priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1052         gtk_tree_path_free (path);
1053
1054         /* Mark as read */
1055         flags = tny_header_get_flags (header);
1056         if (!(flags & TNY_HEADER_FLAG_SEEN))
1057                 tny_header_set_flags (header, flags | TNY_HEADER_FLAG_SEEN);
1058         
1059         /* New mail operation */
1060         mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, G_OBJECT(self));
1061         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1062         modest_mail_operation_get_msg (mail_op, header, view_msg_cb, NULL);
1063         g_object_unref (mail_op);
1064
1065         /* Update toolbar dimming rules */
1066         modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
1067         
1068         /* Free */
1069         g_object_unref (header);
1070
1071         return TRUE;
1072 }
1073  
1074 gboolean        
1075 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1076 {
1077         TnyHeaderFlags flags;
1078         ModestMsgViewWindowPrivate *priv = NULL;
1079         GtkTreePath *path;
1080         ModestMailOperation *mail_op = NULL;
1081
1082         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1083         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1084
1085         /* Return inmediatly if there is no header model */
1086         if (!priv->header_model)
1087                 return FALSE;
1088
1089         path = gtk_tree_row_reference_get_path (priv->row_reference);
1090         while (gtk_tree_path_prev (path)) {
1091                 TnyHeader *header;
1092                 GtkTreeIter iter;
1093
1094                 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1095                 gtk_tree_model_get (priv->header_model, &iter, 
1096                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1097                                     &header, -1);
1098                 if (!header)
1099                         break;
1100                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1101                         g_object_unref (header);
1102                         continue;
1103                 }
1104
1105                 /* Update the row reference */
1106                 gtk_tree_row_reference_free (priv->row_reference);
1107                 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1108                         
1109                 /* Mark as read */
1110                 flags = tny_header_get_flags (header);
1111                 if (!(flags & TNY_HEADER_FLAG_SEEN))
1112                         tny_header_set_flags (header, flags | TNY_HEADER_FLAG_SEEN);
1113
1114                 /* New mail operation */
1115                 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, G_OBJECT(window));
1116                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1117                 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, NULL);             
1118
1119                 /* Update toolbar dimming rules */
1120                 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1121
1122                 g_object_unref (header);
1123
1124                 return TRUE;
1125         }
1126
1127         return FALSE;
1128 }
1129
1130 static void
1131 view_msg_cb (ModestMailOperation *mail_op, 
1132              TnyHeader *header, 
1133              TnyMsg *msg, 
1134              gpointer user_data)
1135 {
1136         ModestMsgViewWindow *self = NULL;
1137         ModestMsgViewWindowPrivate *priv = NULL;
1138
1139         g_return_if_fail (TNY_IS_MSG (msg));
1140
1141         /* Get the window */ 
1142         self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1143         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1144
1145         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1146
1147         /* Set new message */
1148         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1149         modest_msg_view_window_update_priority (self);
1150         update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1151         modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1152 }
1153
1154 TnyFolderType
1155 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1156 {
1157         ModestMsgViewWindowPrivate *priv;
1158         TnyMsg *msg;
1159         TnyFolderType folder_type;
1160
1161         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1162
1163         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1164
1165         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1166         if (msg) {
1167                 TnyFolder *folder;
1168
1169                 folder = tny_msg_get_folder (msg);
1170                 
1171                 if (folder) {
1172                         folder_type = tny_folder_get_folder_type (folder);
1173                         
1174                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1175                                 const gchar *fname = tny_folder_get_name (folder);
1176                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1177                         }
1178
1179                         g_object_unref (folder);
1180                 }
1181                 g_object_unref (msg);
1182         }
1183
1184         return folder_type;
1185 }
1186
1187
1188 static void
1189 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1190 {
1191         ModestMsgViewWindowPrivate *priv;
1192         TnyHeaderFlags flags = 0;
1193
1194         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1195
1196         if (priv->header_model) {
1197                 TnyHeader *header;
1198                 GtkTreeIter iter;
1199
1200                 gtk_tree_model_get_iter (priv->header_model, 
1201                                          &iter, 
1202                                          gtk_tree_row_reference_get_path (priv->row_reference));
1203
1204                 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1205                                     &header, -1);
1206                 flags = tny_header_get_flags (header);
1207         }
1208
1209         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1210
1211 }
1212
1213 static gboolean
1214 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1215 {
1216         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1217                 ModestWindowPrivate *parent_priv;
1218                 ModestWindowMgr *mgr;
1219                 gboolean is_fullscreen;
1220                 GtkAction *fs_toggle_action;
1221                 gboolean active;
1222
1223                 mgr = modest_runtime_get_window_mgr ();
1224                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1225
1226                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1227                 
1228                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1229                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1230                 if (is_fullscreen != active) {
1231                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1232                 }
1233         }
1234
1235         return FALSE;
1236
1237 }
1238
1239 void
1240 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
1241 {
1242                 ModestWindowPrivate *parent_priv;
1243                 GtkAction *fs_toggle_action;
1244                 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1245                 
1246                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1247                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
1248                                               !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
1249 }
1250
1251 static void
1252 set_homogeneous (GtkWidget *widget,
1253                  gpointer data)
1254 {
1255         if (GTK_IS_TOOL_ITEM (widget)) {
1256                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1257                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1258         }
1259 }
1260
1261 static void
1262 modest_msg_view_window_show_toolbar (ModestWindow *self,
1263                                      gboolean show_toolbar)
1264 {
1265         ModestMsgViewWindowPrivate *priv = NULL;
1266         ModestWindowPrivate *parent_priv;
1267         GtkWidget *reply_button = NULL, *menu = NULL;
1268         GtkWidget *placeholder = NULL;
1269         gint insert_index;
1270         
1271         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1272         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1273
1274         /* Set optimized view status */
1275         priv->optimized_view = !show_toolbar;
1276
1277         if (!parent_priv->toolbar) {
1278                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1279                                                                   "/ToolBar");
1280
1281                 /* Set homogeneous toolbar */
1282                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
1283                                        set_homogeneous, NULL);
1284
1285                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1286                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1287                 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1288                 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1289                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1290                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1291                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1292                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1293
1294                 /* Add ProgressBar (Transfer toolbar) */ 
1295                 priv->progress_bar = modest_progress_bar_widget_new ();
1296                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1297                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1298                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1299                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1300                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1301                 
1302                 /* Connect cancel 'clicked' signal to abort progress mode */
1303                 g_signal_connect(priv->cancel_toolitem, "clicked",
1304                                  G_CALLBACK(cancel_progressbar),
1305                                  self);
1306                 
1307                 /* Add it to the observers list */
1308                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1309
1310                 /* Add to window */
1311                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
1312                                            GTK_TOOLBAR (parent_priv->toolbar));
1313
1314
1315                 /* Set reply button tap and hold menu */        
1316                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1317                                                           "/ToolBar/ToolbarMessageReply");
1318                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
1319                                                   "/ToolbarReplyCSM");
1320                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1321         }
1322
1323         if (show_toolbar) {
1324                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
1325                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1326                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1327                 
1328                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1329                 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);                   
1330         } else {
1331                 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1332                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1333         }
1334 }
1335
1336 static void 
1337 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1338                                                GdkEvent *event,
1339                                                ModestMsgViewWindow *window)
1340 {
1341         ModestWindowPrivate *parent_priv;
1342 /*      GtkAction *action; */
1343         gboolean is_address;
1344         gchar *selection;
1345         GtkWidget *focused;
1346
1347         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1348         selection = gtk_clipboard_wait_for_text (clipboard);
1349
1350         is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection)));
1351         
1352 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */
1353 /*      gtk_action_set_sensitive (action, is_address); */
1354
1355         focused = gtk_window_get_focus (GTK_WINDOW (window));
1356
1357 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */
1358 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1359
1360 /*      action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */
1361 /*      gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */
1362
1363         g_free (selection);
1364 /*      modest_msg_view_window_update_dimmed (window); */
1365         
1366 }
1367
1368 static void
1369 cancel_progressbar (GtkToolButton *toolbutton,
1370                     ModestMsgViewWindow *self)
1371 {
1372         GSList *tmp;
1373         ModestMsgViewWindowPrivate *priv;
1374         
1375         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1376
1377         /* Get operation observers and cancel its current operation */
1378         tmp = priv->progress_widgets;
1379         while (tmp) {
1380                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1381                 tmp=g_slist_next(tmp);
1382         }
1383 }
1384 static gboolean
1385 observers_empty (ModestMsgViewWindow *self)
1386 {
1387         GSList *tmp = NULL;
1388         ModestMsgViewWindowPrivate *priv;
1389         gboolean is_empty = TRUE;
1390         guint pending_ops = 0;
1391  
1392         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1393         tmp = priv->progress_widgets;
1394
1395         /* Check all observers */
1396         while (tmp && is_empty)  {
1397                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
1398                 is_empty = pending_ops == 0;
1399                 
1400                 tmp = g_slist_next(tmp);
1401         }
1402         
1403         return is_empty;
1404 }
1405
1406
1407 static void
1408 on_queue_changed (ModestMailOperationQueue *queue,
1409                   ModestMailOperation *mail_op,
1410                   ModestMailOperationQueueNotification type,
1411                   ModestMsgViewWindow *self)
1412 {
1413         GSList *tmp;
1414         ModestMsgViewWindowPrivate *priv;
1415         ModestMailOperationTypeOperation op_type;
1416         ModestToolBarModes mode;
1417         
1418         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1419         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1420
1421         /* If this operations was created by another window, do nothing */
1422         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
1423             return;
1424
1425         /* Get toolbar mode from operation id*/
1426         op_type = modest_mail_operation_get_type_operation (mail_op);
1427         switch (op_type) {
1428         case MODEST_MAIL_OPERATION_TYPE_SEND:
1429         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
1430                 mode = TOOLBAR_MODE_TRANSFER;
1431                 break;
1432         default:
1433                 mode = TOOLBAR_MODE_NORMAL;
1434                 
1435         }
1436                 
1437         /* Add operation observers and change toolbar if neccessary*/
1438         tmp = priv->progress_widgets;
1439         switch (type) {
1440         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
1441                 if (mode == TOOLBAR_MODE_TRANSFER) {
1442                         /* Enable transfer toolbar mode */
1443                         set_toolbar_transfer_mode(self);
1444                         while (tmp) {
1445                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1446                                                                       mail_op);
1447                                 tmp = g_slist_next (tmp);
1448                         }
1449                         
1450                 }
1451                 break;
1452         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
1453                 if (mode == TOOLBAR_MODE_TRANSFER) {
1454                         while (tmp) {
1455                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1456                                                                  mail_op);
1457                                 tmp = g_slist_next (tmp);
1458                                 
1459                         }
1460
1461                         /* If no more operations are being observed, NORMAL mode is enabled again */
1462                         if (observers_empty (self)) {
1463                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
1464                         }
1465                 }
1466                 break;
1467         }
1468 }
1469
1470 GList *
1471 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) 
1472 {
1473         ModestMsgViewWindowPrivate *priv;
1474         GList *selected_attachments = NULL;
1475         
1476         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
1477         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
1478
1479         selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1480         
1481         return selected_attachments;
1482 }
1483
1484 void
1485 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
1486 {
1487         ModestMsgViewWindowPrivate *priv;
1488         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1489         g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
1490
1491         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1492
1493         if (mime_part == NULL) {
1494                 gboolean error = FALSE;
1495                 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1496                 if (selected_attachments == NULL) {
1497                         error = TRUE;
1498                 } else if (g_list_length (selected_attachments) > 1) {
1499                         hildon_banner_show_information (NULL, NULL, _("TODO: more than one attachment is selected"));
1500                         error = TRUE;
1501                 } else {
1502                         mime_part = (TnyMimePart *) selected_attachments->data;
1503                         g_object_ref (mime_part);
1504                 }
1505                 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
1506                 g_list_free (selected_attachments);
1507
1508                 if (error)
1509                         return;
1510         } else {
1511                 g_object_ref (mime_part);
1512         }
1513
1514         if (!TNY_IS_MSG (mime_part)) {
1515                 gchar *filepath = NULL;
1516                 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
1517                 gchar *extension = NULL;
1518                 TnyFsStream *temp_stream = NULL;
1519
1520                 if (att_filename) {
1521                         extension = g_strrstr (att_filename, ".");
1522                         if (extension != NULL)
1523                                 extension++;
1524                 }
1525
1526                 temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath);
1527
1528                 if (temp_stream) {
1529                         const gchar *content_type;
1530                         content_type = tny_mime_part_get_content_type (mime_part);
1531                         tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
1532                         
1533                         modest_platform_activate_file (filepath, content_type);
1534                         g_object_unref (temp_stream);
1535                         g_free (filepath);
1536                         /* TODO: delete temporary file */
1537                 }
1538         } else {
1539                 /* message attachment */
1540                 TnyHeader *header = NULL;
1541                 ModestWindowMgr *mgr;
1542                 ModestWindow *msg_win = NULL;
1543
1544                 header = tny_msg_get_header (TNY_MSG (mime_part));
1545                 mgr = modest_runtime_get_window_mgr ();
1546                 msg_win = modest_window_mgr_find_window_by_header (mgr, header);
1547
1548                 if (!msg_win) {
1549                         gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
1550                         if (!account)
1551                                 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
1552                         msg_win = modest_msg_view_window_new (TNY_MSG (mime_part), account, NULL);
1553                         modest_window_mgr_register_window (mgr, msg_win);
1554                         gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
1555                 }
1556
1557                 gtk_widget_show_all (GTK_WIDGET (msg_win));
1558         }
1559         g_object_unref (mime_part);
1560 }
1561
1562 static gboolean
1563 save_mime_part_to_file (const gchar *filename, TnyMimePart *mime_part)
1564 {
1565         GnomeVFSResult result;
1566         GnomeVFSHandle *handle;
1567         TnyStream *stream;
1568         
1569         result = gnome_vfs_create (&handle, filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
1570         if (result != GNOME_VFS_OK) {
1571                 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
1572                 return FALSE;
1573         }
1574         stream = tny_vfs_stream_new (handle);
1575         tny_mime_part_decode_to_stream (mime_part, stream);
1576         g_object_unref (G_OBJECT (stream));
1577         return TRUE;
1578 }
1579
1580 static gboolean
1581 save_mime_part_to_file_with_checks (GtkWindow *parent, const gchar *filename, TnyMimePart *mime_part)
1582 {
1583         if (modest_maemo_utils_file_exists (filename)) {
1584                 GtkWidget *confirm_overwrite_dialog;
1585                 confirm_overwrite_dialog = hildon_note_new_confirmation (GTK_WINDOW (parent),
1586                                                                          _("TODO: confirm overwrite"));
1587                 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
1588                         gtk_widget_destroy (confirm_overwrite_dialog);
1589                         return FALSE;
1590                 }
1591                 gtk_widget_destroy (confirm_overwrite_dialog);
1592         }
1593
1594         return save_mime_part_to_file (filename, mime_part);
1595 }
1596
1597 void
1598 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
1599 {
1600         gboolean clean_list = FALSE;
1601         ModestMsgViewWindowPrivate *priv;
1602         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1603
1604         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1605
1606         if (mime_parts == NULL) {
1607                 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
1608                 if (mime_parts == NULL)
1609                         return;
1610                 clean_list = TRUE;
1611         }
1612
1613         if (mime_parts->next == NULL) {
1614                 /* only one attachment selected */
1615                 GtkWidget *save_dialog = NULL;
1616                 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
1617                 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
1618                         const gchar *filename;
1619                         gchar *folder;
1620                         save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_SAVE);
1621                         folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
1622                         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
1623                         g_free (folder);
1624                         filename = tny_mime_part_get_filename (mime_part);
1625                         if (filename != NULL)
1626                                 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), filename);
1627                         while (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
1628                                 gchar *filename_tmp = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
1629                                 gboolean save_result;
1630                                 if (!modest_maemo_utils_folder_writable (filename_tmp)) {
1631                                         g_free (filename_tmp);
1632                                         hildon_banner_show_information (NULL, NULL, _("TODO: read only location"));
1633                                         continue;
1634                                 }
1635                                 save_result = save_mime_part_to_file_with_checks (GTK_WINDOW (save_dialog), 
1636                                                                                   filename_tmp, mime_part);
1637                                 g_free (filename_tmp);
1638                                 if (save_result)
1639                                         break;
1640                                 else
1641                                         continue;
1642                         }
1643                         gtk_widget_destroy (save_dialog);
1644                 } else {
1645                         g_warning ("Tried to save a non-file attachment");
1646                 }
1647         } else {
1648                 GtkWidget *save_dialog = NULL;
1649                 gchar *folder;
1650                 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
1651                 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
1652                 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
1653                 g_free (folder);
1654                 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
1655                         gchar *foldername = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
1656                         GList *node = NULL;
1657                         gboolean attachment_found = FALSE;
1658                         if (!modest_maemo_utils_folder_writable (foldername)) {
1659                                 g_free (foldername);
1660                                 hildon_banner_show_information (NULL, NULL, _("TODO: read only location"));
1661                         }
1662                         for (node = mime_parts; node != NULL; node = g_list_next (node)) {
1663                                 TnyMimePart *mime_part = (TnyMimePart *) node->data;
1664                                 if (tny_mime_part_is_attachment (mime_part)) {
1665                                         const gchar *att_filename = tny_mime_part_get_filename (mime_part);
1666                                         if (att_filename != NULL) {
1667                                                 gchar *full_filename;
1668                                                 gboolean save_result;
1669                                                 full_filename = g_build_filename (foldername, att_filename, NULL);
1670                                                 attachment_found = TRUE;
1671                                                 
1672                                                 save_result = save_mime_part_to_file_with_checks (GTK_WINDOW (save_dialog), 
1673                                                                                                   full_filename, mime_part);
1674                                                 g_free (full_filename);
1675                                                 if (!save_result)
1676                                                         break;
1677                                         }
1678                                 }
1679                         }
1680                         gtk_widget_destroy (save_dialog);
1681                 } else {
1682                         g_warning ("Tried to save a non-file attachment");
1683                 }
1684                 /* more than one attachment selected */
1685         }
1686         if (clean_list) {
1687                 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
1688                 g_list_free (mime_parts);
1689         }
1690 }
1691
1692 void
1693 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, GList *mime_parts)
1694 {
1695 /*      g_message ("not implemented %s", __FUNCTION__); */
1696 }
1697
1698
1699 static void
1700 update_window_title (ModestMsgViewWindow *window)
1701 {
1702         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1703         TnyMsg *msg = NULL;
1704         TnyHeader *header = NULL;
1705         const gchar *subject = NULL;
1706
1707         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1708         if (msg != NULL) {
1709                 header = tny_msg_get_header (msg);
1710                 subject = tny_header_get_subject (header);
1711         }
1712
1713         if ((subject == NULL)||(subject[0] == '\0'))
1714                 subject = _("mail_va_no_subject");
1715
1716         gtk_window_set_title (GTK_WINDOW (window), subject);
1717 }