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