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