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