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