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