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