212fd2d052419882dd3e71db95bc4374030441eb
[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         GtkAccelGroup *accel_group;
275         
276         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
277         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
278
279         priv->msg_view = modest_msg_view_new (msg);
280         modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
281         main_vbox = gtk_vbox_new  (FALSE, 6);
282
283         /* Toolbar / Menubar */
284         
285         parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
286         gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar));
287         hildon_window_set_menu    (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
288
289         parent_priv->toolbar = get_toolbar (obj);
290         gtk_widget_show_all (GTK_WIDGET(parent_priv->toolbar));
291         hildon_window_add_toolbar (HILDON_WINDOW(obj), GTK_TOOLBAR(parent_priv->toolbar));
292
293         accel_group = gtk_ui_manager_get_accel_group (parent_priv->ui_manager);
294         gtk_window_add_accel_group (GTK_WINDOW (obj), accel_group);
295
296         priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
297         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
298         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
299
300         gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
301         gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
302         gtk_container_add   (GTK_CONTAINER(obj), main_vbox);
303
304         priv->find_toolbar = hildon_find_toolbar_new (NULL);
305         gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
306         g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
307         g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
308         
309         gtk_widget_show_all (GTK_WIDGET(main_vbox));
310         gtk_box_pack_end (GTK_BOX (main_vbox), priv->find_toolbar, FALSE, FALSE, 0);
311 }       
312
313
314 static void
315 modest_msg_view_window_finalize (GObject *obj)
316 {
317         ModestMsgViewWindowPrivate *priv;
318
319         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
320         if (priv->header_model != NULL) {
321                 g_object_unref (priv->header_model);
322                 priv->header_model = NULL;
323         }
324
325         G_OBJECT_CLASS(parent_class)->finalize (obj);
326 }
327
328
329
330 static gboolean
331 on_delete_event (GtkWidget *widget, GdkEvent *event, ModestMsgViewWindow *self)
332 {
333         save_settings (self);
334         return FALSE;
335 }
336
337 ModestWindow *
338 modest_msg_view_window_new_with_header_model (TnyMsg *msg, const gchar *account_name,
339                                               GtkTreeModel *model, GtkTreeIter iter)
340 {
341         ModestMsgViewWindow *window = NULL;
342         ModestMsgViewWindowPrivate *priv = NULL;
343
344         window = MODEST_MSG_VIEW_WINDOW(modest_msg_view_window_new (msg, account_name));
345         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
346
347         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
348
349         g_object_ref (model);
350         priv->header_model = model;
351         priv->iter = iter;
352
353         modest_msg_view_window_update_priority (window);
354
355         modest_msg_view_window_update_dimmed (window);
356
357         return MODEST_WINDOW(window);
358 }
359
360
361 ModestWindow *
362 modest_msg_view_window_new (TnyMsg *msg, const gchar *account_name)
363 {
364         GObject *obj;
365         ModestMsgViewWindowPrivate *priv;
366         ModestWindowPrivate *parent_priv;
367         GtkActionGroup *action_group;
368         GError *error = NULL;
369
370         g_return_val_if_fail (msg, NULL);
371         
372         obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
373         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
374         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
375         
376         parent_priv->ui_manager = gtk_ui_manager_new();
377         action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
378         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
379
380         /* Add common actions */
381         gtk_action_group_add_actions (action_group,
382                                       modest_action_entries,
383                                       G_N_ELEMENTS (modest_action_entries),
384                                       obj);
385         gtk_action_group_add_actions (action_group,
386                                       modest_msg_view_action_entries,
387                                       G_N_ELEMENTS (modest_msg_view_action_entries),
388                                       obj);
389         gtk_action_group_add_toggle_actions (action_group,
390                                       modest_toggle_action_entries,
391                                       G_N_ELEMENTS (modest_toggle_action_entries),
392                                       obj);
393         gtk_action_group_add_toggle_actions (action_group,
394                                              msg_view_toggle_action_entries,
395                                              G_N_ELEMENTS (msg_view_toggle_action_entries),
396                                              obj);
397         gtk_action_group_add_radio_actions (action_group,
398                                             msg_view_zoom_action_entries,
399                                             G_N_ELEMENTS (msg_view_zoom_action_entries),
400                                             100,
401                                             G_CALLBACK (modest_ui_actions_on_change_zoom),
402                                             obj);
403
404         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
405         g_object_unref (action_group);
406
407         /* Load the UI definition */
408         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
409                                          &error);
410         if (error) {
411                 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
412                 g_error_free (error);
413                 error = NULL;
414         }
415         /* ****** */
416
417         /* Add accelerators */
418         gtk_window_add_accel_group (GTK_WINDOW (obj), 
419                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
420         
421         /* Init window */
422         init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
423         restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
424         
425         gtk_window_set_title (GTK_WINDOW(obj), "Modest");
426         gtk_window_set_icon_from_file (GTK_WINDOW(obj), MODEST_APP_ICON, NULL);
427
428         g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj);
429
430         g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
431                           G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
432         g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
433                           G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
434         g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
435                           G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
436         g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
437                           G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
438         g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
439                           G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
440
441         g_signal_connect (G_OBJECT (obj), "key-release-event",
442                           G_CALLBACK (modest_msg_view_window_key_release_event),
443                           NULL);
444
445         modest_window_set_active_account (MODEST_WINDOW(obj), account_name);
446
447         priv->last_search = NULL;
448
449         modest_msg_view_window_update_dimmed (MODEST_MSG_VIEW_WINDOW (obj));
450
451         gtk_widget_grab_focus (priv->msg_view);
452
453         return MODEST_WINDOW(obj);
454 }
455
456
457
458 TnyMsg*
459 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
460 {
461         ModestMsgView *msg_view;
462         ModestMsgViewWindowPrivate *priv;
463
464         g_return_val_if_fail (self, NULL);
465
466         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
467
468         msg_view = MODEST_MSG_VIEW (priv->msg_view);
469
470         return modest_msg_view_get_message (msg_view);
471 }
472
473 static void 
474 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
475                                             gpointer data)
476 {
477         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
478         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
479
480         if (gtk_toggle_action_get_active (toggle)) {
481                 gtk_widget_show (priv->find_toolbar);
482         } else {
483                 gtk_widget_hide (priv->find_toolbar);
484         }
485
486         
487 }
488
489 static void
490 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
491                                            ModestMsgViewWindow *obj)
492 {
493         GtkToggleAction *toggle;
494         ModestWindowPrivate *parent_priv;
495         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
496         
497         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
498         gtk_toggle_action_set_active (toggle, FALSE);
499 }
500
501 static void
502 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
503                                            ModestMsgViewWindow *obj)
504 {
505         gchar *current_search;
506         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
507
508         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
509
510         if ((current_search == NULL) && (strcmp (current_search, "") == 0)) {
511                 g_free (current_search);
512                 return;
513         }
514
515         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
516                 gboolean result;
517                 g_free (priv->last_search);
518                 priv->last_search = g_strdup (current_search);
519                 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
520                                                  priv->last_search);
521         } else {
522                 modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view));
523         }
524         
525         g_free (current_search);
526                 
527 }
528
529 static void
530 modest_msg_view_window_set_zoom (ModestWindow *window,
531                                  gdouble zoom)
532 {
533         ModestMsgViewWindowPrivate *priv;
534      
535         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
536
537         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
538         modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
539 }
540
541 static gdouble
542 modest_msg_view_window_get_zoom (ModestWindow *window)
543 {
544         ModestMsgViewWindowPrivate *priv;
545      
546         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
547
548         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
549         return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
550 }
551
552 static void
553 modest_msg_view_window_zoom_plus (GtkAction *action, ModestWindow *window)
554 {
555         ModestWindowPrivate *parent_priv;
556         GtkRadioAction *zoom_radio_action;
557         GSList *group, *node;
558
559         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
560         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
561                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
562
563         group = gtk_radio_action_get_group (zoom_radio_action);
564
565         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
566                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_max_zoom_level"));
567                 return;
568         }
569
570         for (node = group; node != NULL; node = g_slist_next (node)) {
571                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
572                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
573                         return;
574                 }
575         }
576 }
577
578 static void
579 modest_msg_view_window_zoom_minus (GtkAction *action, ModestWindow *window)
580 {
581         ModestWindowPrivate *parent_priv;
582         GtkRadioAction *zoom_radio_action;
583         GSList *group, *node;
584
585         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
586         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
587                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
588
589         group = gtk_radio_action_get_group (zoom_radio_action);
590
591         for (node = group; node != NULL; node = g_slist_next (node)) {
592                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
593                         if (node->next != NULL)
594                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
595                         else
596                                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_min_zoom_level"));
597                         break;
598                 }
599         }
600 }
601
602 static gboolean
603 modest_msg_view_window_key_release_event (GtkWidget *window,
604                                           GdkEventKey *event,
605                                           gpointer userdata)
606 {
607         if (event->type == GDK_KEY_RELEASE) {
608                 switch (event->keyval) {
609                 case GDK_Up:
610                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
611                         return TRUE;
612                         break;
613                 case GDK_Down:
614                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
615                         return TRUE;
616                         break;
617                 default:
618                         return FALSE;
619                         break;
620                 };
621         } else {
622                 return FALSE;
623         }
624 }
625
626 static void
627 modest_msg_view_window_scroll_up (ModestWindow *window)
628 {
629         ModestMsgViewWindowPrivate *priv;
630
631         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
632         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE);
633 }
634
635 static void
636 modest_msg_view_window_scroll_down (ModestWindow *window)
637 {
638         ModestMsgViewWindowPrivate *priv;
639
640         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
641         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE);
642 }
643
644 static gboolean 
645 modest_msg_view_window_is_last_message (ModestMsgViewWindow *window)
646 {
647         GtkTreePath *path;
648         ModestMsgViewWindowPrivate *priv;
649         GtkTreeIter tmp_iter;
650         gboolean has_next = FALSE;
651
652         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
653         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
654
655         if (priv->header_model) {
656                 path = gtk_tree_model_get_path (priv->header_model, &priv->iter);
657                 if (!path)
658                         return TRUE;
659                 while (!has_next) {
660                         TnyHeader *header;
661                         gtk_tree_path_next (path);
662                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
663                                 break;
664                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
665                                             &header, -1);
666                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
667                                 has_next = TRUE;
668                                 break;
669                         }
670                         
671                 }
672                 gtk_tree_path_free (path);
673                 return !has_next;
674         } else {
675                 return TRUE;
676         }
677         
678 }
679
680 static gboolean 
681 modest_msg_view_window_is_first_message (ModestMsgViewWindow *window)
682 {
683         GtkTreePath *path;
684         ModestMsgViewWindowPrivate *priv;
685         gboolean result;
686         GtkTreeIter tmp_iter;
687
688         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
689         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
690
691         if (priv->header_model) {
692                 gchar * path_string;
693                 path = gtk_tree_model_get_path (priv->header_model, &priv->iter);
694                 if (!path)
695                         return TRUE;
696
697                 path_string = gtk_tree_path_to_string (path);
698                 result = (strcmp (path_string, "0")==0);
699                 if (result) {
700                         g_free (path_string);
701                         gtk_tree_path_free (path);
702                         return result;
703                 }
704
705                 while (result) {
706                         TnyHeader *header;
707
708                         gtk_tree_path_prev (path);
709                         
710                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
711                                 break;
712                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
713                                             &header, -1);
714                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
715                                 result = FALSE;
716                                 break;
717                         }
718
719                         path_string = gtk_tree_path_to_string (path);
720                         if (strcmp(path_string, "0")==0) {
721                                 g_free (path_string);
722                                 break;
723                         }
724                         g_free (path_string);
725                 }
726                 gtk_tree_path_free (path);
727                 return result;
728         } else {
729                 return TRUE;
730         }
731         
732 }
733
734 gboolean        
735 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
736 {
737         ModestMsgViewWindowPrivate *priv;
738         GtkTreeIter tmp_iter;
739         gboolean has_next = FALSE;
740
741         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
742         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
743
744         if (priv->header_model) {
745                 tmp_iter = priv->iter;
746                 while (gtk_tree_model_iter_next (priv->header_model, &tmp_iter)) {
747                         TnyHeader *header;
748                         TnyFolder *folder;
749                         TnyMsg *msg;
750
751                         priv->iter = tmp_iter;
752                         gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
753                                             &header, -1);
754                         if (!header)
755                                 break;
756                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
757                                 continue;
758
759                         folder = tny_header_get_folder (header);
760                         if (!folder)
761                                 break;
762                         msg = tny_folder_get_msg (folder, header, NULL);
763                         if (!msg) {
764                                 g_object_unref (folder);
765                                 break;
766                         }
767                         has_next = TRUE;
768                         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
769                         modest_msg_view_window_update_dimmed (window);
770                         modest_msg_view_window_update_priority (window);
771                         gtk_widget_grab_focus (priv->msg_view);
772
773                         g_object_unref (msg);
774                         break;
775                 }
776
777                 return has_next;
778         } else {
779                 return FALSE;
780         }
781 }
782
783 gboolean        
784 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
785 {
786         ModestMsgViewWindowPrivate *priv;
787
788         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
789         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
790
791         if (priv->header_model) {
792                 GtkTreePath *path;
793                 gboolean has_prev = FALSE;
794
795                 path = gtk_tree_model_get_path (priv->header_model, &(priv->iter));
796                 while (gtk_tree_path_prev (path)) {
797                         TnyHeader *header;
798                         TnyFolder *folder;
799                         TnyMsg *msg;
800
801                         gtk_tree_model_get_iter (priv->header_model, &(priv->iter), path);
802                         gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
803                                             &header, -1);
804                         if (!header)
805                                 break;
806                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
807                                 continue;
808                         folder = tny_header_get_folder (header);
809                         if (!folder)
810                                 break;
811                         msg = tny_folder_get_msg (folder, header, NULL);
812                         if (!msg) {
813                                 g_object_unref (folder);
814                                 break;
815                         }
816                         has_prev = TRUE;
817                         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
818                         modest_msg_view_window_update_dimmed (window);
819                         modest_msg_view_window_update_priority (window);
820                         gtk_widget_grab_focus (priv->msg_view);
821
822                         g_object_unref (msg);
823                         break;
824                 }
825                 gtk_tree_path_free (path);
826                 return has_prev;
827         } else {
828                 return FALSE;
829         }
830 }
831
832 static TnyFolderType
833 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
834 {
835         ModestMsgViewWindowPrivate *priv;
836         TnyMsg *msg;
837         TnyFolderType folder_type;
838
839         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
840
841         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
842
843         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
844         if (msg) {
845                 TnyFolder *folder;
846
847                 folder = tny_msg_get_folder (msg);
848                 
849                 if (folder) {
850                         folder_type = tny_folder_get_folder_type (folder);
851                         
852                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
853                                 const gchar *fname = tny_folder_get_name (folder);
854                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
855                         }
856
857                         g_object_unref (folder);
858                 }
859         }
860
861         return folder_type;
862 }
863
864 static void
865 modest_msg_view_window_update_dimmed (ModestMsgViewWindow *window)
866 {
867         ModestWindowPrivate *parent_priv;
868         GtkAction *widget;
869         gboolean is_first, is_last;
870         TnyFolderType folder_type;
871         gboolean is_not_sent;
872
873         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
874
875         is_first = modest_msg_view_window_is_first_message (window);
876         is_last = modest_msg_view_window_is_last_message (window);
877
878         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
879         gtk_action_set_sensitive (widget, !is_first);
880         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewPreviousMessageMenu");
881         gtk_action_set_sensitive (widget, !is_first);
882                 
883         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
884         gtk_action_set_sensitive (widget, !is_last);
885         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewNextMessageMenu");
886         gtk_action_set_sensitive (widget, !is_last);
887
888         folder_type = modest_msg_view_window_get_folder_type (MODEST_MSG_VIEW_WINDOW (window));
889         is_not_sent = ((folder_type == TNY_FOLDER_TYPE_DRAFTS)||(folder_type == TNY_FOLDER_TYPE_OUTBOX));
890         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
891         gtk_action_set_sensitive (widget, !is_not_sent);
892         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageReplyMenu");
893         gtk_action_set_sensitive (widget, !is_not_sent);
894         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageReplyAllMenu");
895         gtk_action_set_sensitive (widget, !is_not_sent);
896         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageForwardMenu");
897         gtk_action_set_sensitive (widget, !is_not_sent);
898                 
899 }
900
901 static void
902 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
903 {
904         ModestMsgViewWindowPrivate *priv;
905         TnyHeaderFlags flags = 0;
906
907         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
908
909         if (priv->header_model) {
910                 TnyHeader *header;
911
912                 gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
913                                     &header, -1);
914                 flags = tny_header_get_flags (header);
915         }
916
917         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
918
919 }
920
921 static void
922 modest_msg_view_window_toggle_fullscreen (GtkAction *action, ModestWindow *window)
923 {
924         ModestWindowPrivate *parent_priv;
925         GtkAction *fs_toggle_action;
926         gboolean active;
927
928         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
929
930         fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ShowToggleFullscreenMenu");
931         active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action));
932         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), !active);
933 }