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