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