* fixes for latest hildon version
[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-hildon-includes.h"
43 #include <gtkhtml/gtkhtml-search.h>
44 #include <gdk/gdkkeysyms.h>
45
46 static void  modest_msg_view_window_class_init   (ModestMsgViewWindowClass *klass);
47 static void  modest_msg_view_window_init         (ModestMsgViewWindow *obj);
48 static void  modest_msg_view_window_finalize     (GObject *obj);
49 static void  modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
50                                                          gpointer data);
51 static void  modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
52                                                         ModestMsgViewWindow *obj);
53 static void  modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
54                                                         ModestMsgViewWindow *obj);
55
56 static void  modest_msg_view_window_set_zoom (ModestWindow *window,
57                                               gdouble zoom);
58 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
59 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
60 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
61 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
62                                                           GdkEventKey *event,
63                                                           gpointer userdata);
64 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, 
65                                                            GdkEventWindowState *event, 
66                                                            gpointer userdata);
67 static void modest_msg_view_window_scroll_up (ModestWindow *window);
68 static void modest_msg_view_window_scroll_down (ModestWindow *window);
69 static gboolean modest_msg_view_window_is_last_message (ModestMsgViewWindow *window);
70 static gboolean modest_msg_view_window_is_first_message (ModestMsgViewWindow *window);
71 static TnyFolderType modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window);
72 static void modest_msg_view_window_update_dimmed (ModestMsgViewWindow *window);
73 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
74
75 static void modest_msg_view_window_show_toolbar   (ModestWindow *window,
76                                                    gboolean show_toolbar);
77
78
79 /* list my signals */
80 enum {
81         /* MY_SIGNAL_1, */
82         /* MY_SIGNAL_2, */
83         LAST_SIGNAL
84 };
85
86 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
87         { "FindInMessage",    GTK_STOCK_FIND,    N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
88 };
89
90 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
91         { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
92         { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
93         { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
94         { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
95         { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
96         { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
97 };
98
99 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
100 struct _ModestMsgViewWindowPrivate {
101
102         GtkWidget   *msg_view;
103         GtkWidget   *main_scroll;
104         GtkWidget   *find_toolbar;
105         gchar       *last_search;
106
107         GtkTreeModel *header_model;
108         GtkTreeIter   iter;
109 };
110
111 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
112                                                     MODEST_TYPE_MSG_VIEW_WINDOW, \
113                                                     ModestMsgViewWindowPrivate))
114 /* globals */
115 static GtkWindowClass *parent_class = NULL;
116
117 /* uncomment the following if you have defined any signals */
118 /* static guint signals[LAST_SIGNAL] = {0}; */
119
120 GType
121 modest_msg_view_window_get_type (void)
122 {
123         static GType my_type = 0;
124         if (!my_type) {
125                 static const GTypeInfo my_info = {
126                         sizeof(ModestMsgViewWindowClass),
127                         NULL,           /* base init */
128                         NULL,           /* base finalize */
129                         (GClassInitFunc) modest_msg_view_window_class_init,
130                         NULL,           /* class finalize */
131                         NULL,           /* class data */
132                         sizeof(ModestMsgViewWindow),
133                         1,              /* n_preallocs */
134                         (GInstanceInitFunc) modest_msg_view_window_init,
135                         NULL
136                 };
137                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
138                                                   "ModestMsgViewWindow",
139                                                   &my_info, 0);
140         }
141         return my_type;
142 }
143
144 static void
145 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
146 {
147         GObjectClass *gobject_class;
148         ModestWindowClass *modest_window_class;
149         gobject_class = (GObjectClass*) klass;
150         modest_window_class = (ModestWindowClass *) klass;
151
152         parent_class            = g_type_class_peek_parent (klass);
153         gobject_class->finalize = modest_msg_view_window_finalize;
154
155         modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
156         modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
157         modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
158         modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
159         modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
160
161         g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
162 }
163
164 static void
165 modest_msg_view_window_init (ModestMsgViewWindow *obj)
166 {
167         ModestMsgViewWindowPrivate *priv;
168         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
169
170         priv->msg_view      = NULL;
171         priv->header_model  = NULL;
172 }
173
174 static void
175 save_settings (ModestMsgViewWindow *self)
176 {
177         modest_widget_memory_save (modest_runtime_get_conf (),
178                                     G_OBJECT(self), "modest-msg-view-window");
179 }
180
181
182 static void
183 restore_settings (ModestMsgViewWindow *self)
184 {
185         modest_widget_memory_restore (modest_runtime_get_conf (),
186                                       G_OBJECT(self), "modest-msg-view-window");
187 }
188
189
190
191 static GtkWidget *
192 menubar_to_menu (GtkUIManager *ui_manager)
193 {
194         GtkWidget *main_menu;
195         GtkWidget *menubar;
196         GList *iter;
197
198         /* Create new main menu */
199         main_menu = gtk_menu_new();
200
201         /* Get the menubar from the UI manager */
202         menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
203
204         iter = gtk_container_get_children (GTK_CONTAINER (menubar));
205         while (iter) {
206                 GtkWidget *menu;
207
208                 menu = GTK_WIDGET (iter->data);
209                 gtk_widget_reparent(menu, main_menu);
210
211                 iter = g_list_next (iter);
212         }
213         return main_menu;
214 }
215
216 static void
217 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
218 {
219         GtkWidget *main_vbox;
220         ModestMsgViewWindowPrivate *priv;
221         ModestWindowPrivate *parent_priv;
222         
223         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
224         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
225
226         priv->msg_view = modest_msg_view_new (msg);
227         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
228         main_vbox = gtk_vbox_new  (FALSE, 6);
229
230         /* Menubar */
231         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
232         gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
233         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
234
235         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
236         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
237         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
238
239         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
240         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
241         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
242
243         priv->find_toolbar = hildon_find_toolbar_new (NULL);
244         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
245         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
246         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
247         
248         gtk_widget_show_all (GTK_WIDGET(main_vbox));
249         gtk_box_pack_end (GTK_BOX (main_vbox), priv->find_toolbar, FALSE, FALSE, 0);
250 }       
251
252
253 static void
254 modest_msg_view_window_finalize (GObject *obj)
255 {
256         ModestMsgViewWindowPrivate *priv;
257
258         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
259         if (priv->header_model != NULL) {
260                 g_object_unref (priv->header_model);
261                 priv->header_model = NULL;
262         }
263
264         G_OBJECT_CLASS(parent_class)->finalize (obj);
265 }
266
267
268
269 static gboolean
270 on_delete_event (GtkWidget *widget, GdkEvent *event, ModestMsgViewWindow *self)
271 {
272         save_settings (self);
273         return FALSE;
274 }
275
276 ModestWindow *
277 modest_msg_view_window_new_with_header_model (TnyMsg *msg, const gchar *account_name,
278                                               GtkTreeModel *model, GtkTreeIter iter)
279 {
280         ModestMsgViewWindow *window = NULL;
281         ModestMsgViewWindowPrivate *priv = NULL;
282
283         window = MODEST_MSG_VIEW_WINDOW(modest_msg_view_window_new (msg, account_name));
284         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
285
286         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
287
288         g_object_ref (model);
289         priv->header_model = model;
290         priv->iter = iter;
291
292         modest_msg_view_window_update_priority (window);
293
294         modest_msg_view_window_update_dimmed (window);
295
296         return MODEST_WINDOW(window);
297 }
298
299
300 ModestWindow *
301 modest_msg_view_window_new (TnyMsg *msg, const gchar *account_name)
302 {
303         GObject *obj;
304         ModestMsgViewWindowPrivate *priv;
305         ModestWindowPrivate *parent_priv;
306         GtkActionGroup *action_group;
307         GError *error = NULL;
308         GdkPixbuf *window_icon = NULL;
309
310         g_return_val_if_fail (msg, NULL);
311         
312         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
313         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
314         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
315         
316         parent_priv->ui_manager = gtk_ui_manager_new();
317         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
318         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
319
320         /* Add common actions */
321         gtk_action_group_add_actions (action_group,
322                                       modest_action_entries,
323                                       G_N_ELEMENTS (modest_action_entries),
324                                       obj);
325         gtk_action_group_add_toggle_actions (action_group,
326                                              modest_toggle_action_entries,
327                                              G_N_ELEMENTS (modest_toggle_action_entries),
328                                              obj);
329         gtk_action_group_add_toggle_actions (action_group,
330                                              msg_view_toggle_action_entries,
331                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
332                                              obj);
333         gtk_action_group_add_radio_actions (action_group,
334                                             msg_view_zoom_action_entries,
335                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
336                                             100,
337                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
338                                             obj);
339
340         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
341         g_object_unref (action_group);
342
343         /* Load the UI definition */
344         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
345                                          &error);
346         if (error) {
347                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
348                 g_error_free (error);
349                 error = NULL;
350         }
351         /* ****** */
352
353         /* Add accelerators */
354         gtk_window_add_accel_group (GTK_WINDOW (obj), 
355                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
356         
357         /* Init window */
358         init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
359         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
360         
361         g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj);
362
363         g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
364                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
365         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
366                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
367         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
368                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
369         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
370                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
371         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
372                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
373
374         g_signal_connect (G_OBJECT (obj), "key-release-event",
375                           G_CALLBACK (modest_msg_view_window_key_release_event),
376                           NULL);
377
378         g_signal_connect (G_OBJECT (obj), "window-state-event",
379                           G_CALLBACK (modest_msg_view_window_window_state_event),
380                           NULL);
381
382         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
383
384         priv->last_search = NULL;
385
386         modest_msg_view_window_update_dimmed (MODEST_MSG_VIEW_WINDOW (obj));
387
388         /* Set window icon */
389         window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
390         gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
391
392         gtk_widget_grab_focus (priv->msg_view);
393
394         return MODEST_WINDOW(obj);
395 }
396
397
398
399 TnyMsg*
400 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
401 {
402         ModestMsgView *msg_view;
403         ModestMsgViewWindowPrivate *priv;
404
405         g_return_val_if_fail (self, NULL);
406
407         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
408
409         msg_view = MODEST_MSG_VIEW (priv->msg_view);
410
411         return modest_msg_view_get_message (msg_view);
412 }
413
414 const gchar*
415 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
416 {
417         TnyMsg *msg;
418         TnyHeader *header;
419         const gchar *retval = NULL;
420
421         msg = modest_msg_view_window_get_message (self);
422
423         if (!msg)
424                 return NULL;
425
426         header = tny_msg_get_header (msg);
427         if (header) {
428                 retval = tny_header_get_uid (header);
429                 g_object_unref (header);
430         }
431         return retval;
432 }
433
434 static void 
435 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
436                                             gpointer data)
437 {
438         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
439         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
440
441         if (gtk_toggle_action_get_active (toggle)) {
442                 gtk_widget_show (priv->find_toolbar);
443         } else {
444                 gtk_widget_hide (priv->find_toolbar);
445         }
446
447         
448 }
449
450 static void
451 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
452                                            ModestMsgViewWindow *obj)
453 {
454         GtkToggleAction *toggle;
455         ModestWindowPrivate *parent_priv;
456         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
457         
458         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
459         gtk_toggle_action_set_active (toggle, FALSE);
460 }
461
462 static void
463 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
464                                            ModestMsgViewWindow *obj)
465 {
466         gchar *current_search;
467         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
468
469         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
470
471         if ((current_search == NULL) && (strcmp (current_search, "") == 0)) {
472                 g_free (current_search);
473                 return;
474         }
475
476         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
477                 gboolean result;
478                 g_free (priv->last_search);
479                 priv->last_search = g_strdup (current_search);
480                 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
481                                                  priv->last_search);
482         } else {
483                 modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view));
484         }
485         
486         g_free (current_search);
487                 
488 }
489
490 static void
491 modest_msg_view_window_set_zoom (ModestWindow *window,
492                                  gdouble zoom)
493 {
494         ModestMsgViewWindowPrivate *priv;
495      
496         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
497
498         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
499         modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
500 }
501
502 static gdouble
503 modest_msg_view_window_get_zoom (ModestWindow *window)
504 {
505         ModestMsgViewWindowPrivate *priv;
506      
507         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
508
509         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
510         return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
511 }
512
513 static gboolean
514 modest_msg_view_window_zoom_plus (ModestWindow *window)
515 {
516         ModestWindowPrivate *parent_priv;
517         GtkRadioAction *zoom_radio_action;
518         GSList *group, *node;
519
520         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
521         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
522                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
523
524         group = gtk_radio_action_get_group (zoom_radio_action);
525
526         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
527                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_max_zoom_level"));
528                 return FALSE;
529         }
530
531         for (node = group; node != NULL; node = g_slist_next (node)) {
532                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
533                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
534                         return TRUE;
535                 }
536         }
537         return FALSE;
538 }
539
540 static gboolean
541 modest_msg_view_window_zoom_minus (ModestWindow *window)
542 {
543         ModestWindowPrivate *parent_priv;
544         GtkRadioAction *zoom_radio_action;
545         GSList *group, *node;
546
547         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
548         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
549                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
550
551         group = gtk_radio_action_get_group (zoom_radio_action);
552
553         for (node = group; node != NULL; node = g_slist_next (node)) {
554                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
555                         if (node->next != NULL) {
556                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
557                                 return TRUE;
558                         } else {
559                                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_min_zoom_level"));
560                                 return FALSE;
561                         }
562                         break;
563                 }
564         }
565         return FALSE;
566 }
567
568 static gboolean
569 modest_msg_view_window_key_release_event (GtkWidget *window,
570                                           GdkEventKey *event,
571                                           gpointer userdata)
572 {
573         if (event->type == GDK_KEY_RELEASE) {
574                 switch (event->keyval) {
575                 case GDK_Up:
576                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
577                         return TRUE;
578                         break;
579                 case GDK_Down:
580                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
581                         return TRUE;
582                         break;
583                 default:
584                         return FALSE;
585                         break;
586                 };
587         } else {
588                 return FALSE;
589         }
590 }
591
592 static void
593 modest_msg_view_window_scroll_up (ModestWindow *window)
594 {
595         ModestMsgViewWindowPrivate *priv;
596
597         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
598         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE);
599 }
600
601 static void
602 modest_msg_view_window_scroll_down (ModestWindow *window)
603 {
604         ModestMsgViewWindowPrivate *priv;
605
606         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
607         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE);
608 }
609
610 static gboolean 
611 modest_msg_view_window_is_last_message (ModestMsgViewWindow *window)
612 {
613         GtkTreePath *path;
614         ModestMsgViewWindowPrivate *priv;
615         GtkTreeIter tmp_iter;
616         gboolean has_next = FALSE;
617
618         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
619         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
620
621         if (priv->header_model) {
622                 path = gtk_tree_model_get_path (priv->header_model, &priv->iter);
623                 if (!path)
624                         return TRUE;
625                 while (!has_next) {
626                         TnyHeader *header;
627                         gtk_tree_path_next (path);
628                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
629                                 break;
630                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
631                                             &header, -1);
632                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
633                                 has_next = TRUE;
634                                 break;
635                         }
636                         
637                 }
638                 gtk_tree_path_free (path);
639                 return !has_next;
640         } else {
641                 return TRUE;
642         }
643         
644 }
645
646 static gboolean 
647 modest_msg_view_window_is_first_message (ModestMsgViewWindow *window)
648 {
649         GtkTreePath *path;
650         ModestMsgViewWindowPrivate *priv;
651         gboolean result;
652         GtkTreeIter tmp_iter;
653
654         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
655         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
656
657         if (priv->header_model) {
658                 gchar * path_string;
659                 path = gtk_tree_model_get_path (priv->header_model, &priv->iter);
660                 if (!path)
661                         return TRUE;
662
663                 path_string = gtk_tree_path_to_string (path);
664                 result = (strcmp (path_string, "0")==0);
665                 if (result) {
666                         g_free (path_string);
667                         gtk_tree_path_free (path);
668                         return result;
669                 }
670
671                 while (result) {
672                         TnyHeader *header;
673
674                         gtk_tree_path_prev (path);
675                         
676                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
677                                 break;
678                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
679                                             &header, -1);
680                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
681                                 result = FALSE;
682                                 break;
683                         }
684
685                         path_string = gtk_tree_path_to_string (path);
686                         if (strcmp(path_string, "0")==0) {
687                                 g_free (path_string);
688                                 break;
689                         }
690                         g_free (path_string);
691                 }
692                 gtk_tree_path_free (path);
693                 return result;
694         } else {
695                 return TRUE;
696         }
697         
698 }
699
700 gboolean        
701 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
702 {
703         ModestMsgViewWindowPrivate *priv;
704         GtkTreeIter tmp_iter;
705         gboolean has_next = FALSE;
706
707         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
708         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
709
710         if (priv->header_model) {
711                 tmp_iter = priv->iter;
712                 while (gtk_tree_model_iter_next (priv->header_model, &tmp_iter)) {
713                         TnyHeader *header;
714                         TnyFolder *folder;
715                         TnyMsg *msg;
716
717                         priv->iter = tmp_iter;
718                         gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
719                                             &header, -1);
720                         if (!header)
721                                 break;
722                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
723                                 continue;
724
725                         folder = tny_header_get_folder (header);
726                         if (!folder)
727                                 break;
728                         msg = tny_folder_get_msg (folder, header, NULL);
729                         if (!msg) {
730                                 g_object_unref (folder);
731                                 break;
732                         }
733                         has_next = TRUE;
734                         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
735                         modest_msg_view_window_update_dimmed (window);
736                         modest_msg_view_window_update_priority (window);
737                         gtk_widget_grab_focus (priv->msg_view);
738
739                         g_object_unref (msg);
740                         break;
741                 }
742
743                 return has_next;
744         } else {
745                 return FALSE;
746         }
747 }
748
749 gboolean        
750 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
751 {
752         ModestMsgViewWindowPrivate *priv;
753
754         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
755         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
756
757         if (priv->header_model) {
758                 GtkTreePath *path;
759                 gboolean has_prev = FALSE;
760
761                 path = gtk_tree_model_get_path (priv->header_model, &(priv->iter));
762                 while (gtk_tree_path_prev (path)) {
763                         TnyHeader *header;
764                         TnyFolder *folder;
765                         TnyMsg *msg;
766
767                         gtk_tree_model_get_iter (priv->header_model, &(priv->iter), path);
768                         gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
769                                             &header, -1);
770                         if (!header)
771                                 break;
772                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
773                                 continue;
774                         folder = tny_header_get_folder (header);
775                         if (!folder)
776                                 break;
777                         msg = tny_folder_get_msg (folder, header, NULL);
778                         if (!msg) {
779                                 g_object_unref (folder);
780                                 break;
781                         }
782                         has_prev = TRUE;
783                         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
784                         modest_msg_view_window_update_dimmed (window);
785                         modest_msg_view_window_update_priority (window);
786                         gtk_widget_grab_focus (priv->msg_view);
787
788                         g_object_unref (msg);
789                         break;
790                 }
791                 gtk_tree_path_free (path);
792                 return has_prev;
793         } else {
794                 return FALSE;
795         }
796 }
797
798 static TnyFolderType
799 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
800 {
801         ModestMsgViewWindowPrivate *priv;
802         TnyMsg *msg;
803         TnyFolderType folder_type;
804
805         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
806
807         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
808
809         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
810         if (msg) {
811                 TnyFolder *folder;
812
813                 folder = tny_msg_get_folder (msg);
814                 
815                 if (folder) {
816                         folder_type = tny_folder_get_folder_type (folder);
817                         
818                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
819                                 const gchar *fname = tny_folder_get_name (folder);
820                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
821                         }
822
823                         g_object_unref (folder);
824                 }
825         }
826
827         return folder_type;
828 }
829
830 static void
831 modest_msg_view_window_update_dimmed (ModestMsgViewWindow *window)
832 {
833         ModestWindowPrivate *parent_priv;
834         GtkAction *widget;
835         gboolean is_first, is_last;
836         TnyFolderType folder_type;
837         gboolean is_not_sent;
838
839         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
840
841         is_first = modest_msg_view_window_is_first_message (window);
842         is_last = modest_msg_view_window_is_last_message (window);
843
844         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
845         gtk_action_set_sensitive (widget, !is_first);
846         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewPreviousMessageMenu");
847         gtk_action_set_sensitive (widget, !is_first);
848                 
849         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
850         gtk_action_set_sensitive (widget, !is_last);
851         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewNextMessageMenu");
852         gtk_action_set_sensitive (widget, !is_last);
853
854         folder_type = modest_msg_view_window_get_folder_type (MODEST_MSG_VIEW_WINDOW (window));
855         is_not_sent = ((folder_type == TNY_FOLDER_TYPE_DRAFTS)||(folder_type == TNY_FOLDER_TYPE_OUTBOX));
856         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
857         gtk_action_set_sensitive (widget, !is_not_sent);
858         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageReplyMenu");
859         gtk_action_set_sensitive (widget, !is_not_sent);
860         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageReplyAllMenu");
861         gtk_action_set_sensitive (widget, !is_not_sent);
862         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageForwardMenu");
863         gtk_action_set_sensitive (widget, !is_not_sent);
864                 
865 }
866
867 static void
868 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
869 {
870         ModestMsgViewWindowPrivate *priv;
871         TnyHeaderFlags flags = 0;
872
873         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
874
875         if (priv->header_model) {
876                 TnyHeader *header;
877
878                 gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
879                                     &header, -1);
880                 flags = tny_header_get_flags (header);
881         }
882
883         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
884
885 }
886
887 static gboolean
888 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
889 {
890         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
891                 ModestWindowPrivate *parent_priv;
892                 ModestWindowMgr *mgr;
893                 gboolean is_fullscreen;
894                 GtkAction *fs_toggle_action;
895                 gboolean active;
896
897                 mgr = modest_runtime_get_window_mgr ();
898                 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
899
900                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
901                 
902                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
903                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
904                 if (is_fullscreen != active) {
905                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
906                 }
907         }
908
909         return FALSE;
910
911 }
912
913 void
914 modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window)
915 {
916                 ModestWindowPrivate *parent_priv;
917                 GtkAction *fs_toggle_action;
918                 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
919                 
920                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
921                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action),
922                                               !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)));
923 }
924
925 static void
926 set_homogeneous (GtkWidget *widget,
927                  gpointer data)
928 {
929         if (GTK_IS_TOOL_ITEM (widget)) {
930                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
931                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
932         }
933 }
934
935 static void
936 modest_msg_view_window_show_toolbar (ModestWindow *self,
937                                      gboolean show_toolbar)
938 {
939         ModestWindowPrivate *parent_priv;
940         GtkWidget *reply_button = NULL, *menu = NULL;
941         
942         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
943
944         if (!parent_priv->toolbar && show_toolbar) {
945                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
946                                                                   "/ToolBar");
947
948                 /* Set homogeneous toolbar */
949                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
950                                        set_homogeneous, NULL);
951
952                 /* Add to window */
953                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
954                                            GTK_TOOLBAR (parent_priv->toolbar));
955
956
957                 /* Set reply button tap and hold menu */        
958                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
959                                                           "/ToolBar/ToolbarMessageReply");
960                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
961                                                   "/ToolbarReplyCSM");
962                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
963         }
964
965         /* TODO: Why is this sometimes NULL? murrayc */
966         if (parent_priv->toolbar) {
967                 if (show_toolbar)
968                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
969                 else
970                         gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
971         }
972 }