* Added code to handle fullscreen mode for the whole application
[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 const gchar*
470 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
471 {
472         TnyMsg *msg;
473         TnyHeader *header;
474         const gchar *retval = NULL;
475
476         msg = modest_msg_view_window_get_message (self);
477
478         if (!msg)
479                 return NULL;
480
481         header = tny_msg_get_header (msg);
482         if (header) {
483                 retval = tny_header_get_uid (header);
484                 g_object_unref (header);
485         }
486         return retval;
487 }
488
489 static void 
490 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
491                                             gpointer data)
492 {
493         ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
494         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
495
496         if (gtk_toggle_action_get_active (toggle)) {
497                 gtk_widget_show (priv->find_toolbar);
498         } else {
499                 gtk_widget_hide (priv->find_toolbar);
500         }
501
502         
503 }
504
505 static void
506 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
507                                            ModestMsgViewWindow *obj)
508 {
509         GtkToggleAction *toggle;
510         ModestWindowPrivate *parent_priv;
511         parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
512         
513         toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
514         gtk_toggle_action_set_active (toggle, FALSE);
515 }
516
517 static void
518 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
519                                            ModestMsgViewWindow *obj)
520 {
521         gchar *current_search;
522         ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
523
524         g_object_get (G_OBJECT (widget), "prefix", &current_search, NULL);
525
526         if ((current_search == NULL) && (strcmp (current_search, "") == 0)) {
527                 g_free (current_search);
528                 return;
529         }
530
531         if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
532                 gboolean result;
533                 g_free (priv->last_search);
534                 priv->last_search = g_strdup (current_search);
535                 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
536                                                  priv->last_search);
537         } else {
538                 modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view));
539         }
540         
541         g_free (current_search);
542                 
543 }
544
545 static void
546 modest_msg_view_window_set_zoom (ModestWindow *window,
547                                  gdouble zoom)
548 {
549         ModestMsgViewWindowPrivate *priv;
550      
551         g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
552
553         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
554         modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
555 }
556
557 static gdouble
558 modest_msg_view_window_get_zoom (ModestWindow *window)
559 {
560         ModestMsgViewWindowPrivate *priv;
561      
562         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
563
564         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
565         return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
566 }
567
568 static void
569 modest_msg_view_window_zoom_plus (GtkAction *action, ModestWindow *window)
570 {
571         ModestWindowPrivate *parent_priv;
572         GtkRadioAction *zoom_radio_action;
573         GSList *group, *node;
574
575         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
576         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
577                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
578
579         group = gtk_radio_action_get_group (zoom_radio_action);
580
581         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
582                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_max_zoom_level"));
583                 return;
584         }
585
586         for (node = group; node != NULL; node = g_slist_next (node)) {
587                 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
588                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
589                         return;
590                 }
591         }
592 }
593
594 static void
595 modest_msg_view_window_zoom_minus (GtkAction *action, ModestWindow *window)
596 {
597         ModestWindowPrivate *parent_priv;
598         GtkRadioAction *zoom_radio_action;
599         GSList *group, *node;
600
601         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
602         zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, 
603                                                                          "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
604
605         group = gtk_radio_action_get_group (zoom_radio_action);
606
607         for (node = group; node != NULL; node = g_slist_next (node)) {
608                 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
609                         if (node->next != NULL)
610                                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
611                         else
612                                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_min_zoom_level"));
613                         break;
614                 }
615         }
616 }
617
618 static gboolean
619 modest_msg_view_window_key_release_event (GtkWidget *window,
620                                           GdkEventKey *event,
621                                           gpointer userdata)
622 {
623         if (event->type == GDK_KEY_RELEASE) {
624                 switch (event->keyval) {
625                 case GDK_Up:
626                         modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
627                         return TRUE;
628                         break;
629                 case GDK_Down:
630                         modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
631                         return TRUE;
632                         break;
633                 default:
634                         return FALSE;
635                         break;
636                 };
637         } else {
638                 return FALSE;
639         }
640 }
641
642 static void
643 modest_msg_view_window_scroll_up (ModestWindow *window)
644 {
645         ModestMsgViewWindowPrivate *priv;
646
647         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
648         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE);
649 }
650
651 static void
652 modest_msg_view_window_scroll_down (ModestWindow *window)
653 {
654         ModestMsgViewWindowPrivate *priv;
655
656         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
657         g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE);
658 }
659
660 static gboolean 
661 modest_msg_view_window_is_last_message (ModestMsgViewWindow *window)
662 {
663         GtkTreePath *path;
664         ModestMsgViewWindowPrivate *priv;
665         GtkTreeIter tmp_iter;
666         gboolean has_next = FALSE;
667
668         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
669         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
670
671         if (priv->header_model) {
672                 path = gtk_tree_model_get_path (priv->header_model, &priv->iter);
673                 if (!path)
674                         return TRUE;
675                 while (!has_next) {
676                         TnyHeader *header;
677                         gtk_tree_path_next (path);
678                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
679                                 break;
680                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
681                                             &header, -1);
682                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
683                                 has_next = TRUE;
684                                 break;
685                         }
686                         
687                 }
688                 gtk_tree_path_free (path);
689                 return !has_next;
690         } else {
691                 return TRUE;
692         }
693         
694 }
695
696 static gboolean 
697 modest_msg_view_window_is_first_message (ModestMsgViewWindow *window)
698 {
699         GtkTreePath *path;
700         ModestMsgViewWindowPrivate *priv;
701         gboolean result;
702         GtkTreeIter tmp_iter;
703
704         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
705         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
706
707         if (priv->header_model) {
708                 gchar * path_string;
709                 path = gtk_tree_model_get_path (priv->header_model, &priv->iter);
710                 if (!path)
711                         return TRUE;
712
713                 path_string = gtk_tree_path_to_string (path);
714                 result = (strcmp (path_string, "0")==0);
715                 if (result) {
716                         g_free (path_string);
717                         gtk_tree_path_free (path);
718                         return result;
719                 }
720
721                 while (result) {
722                         TnyHeader *header;
723
724                         gtk_tree_path_prev (path);
725                         
726                         if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
727                                 break;
728                         gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
729                                             &header, -1);
730                         if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) {
731                                 result = FALSE;
732                                 break;
733                         }
734
735                         path_string = gtk_tree_path_to_string (path);
736                         if (strcmp(path_string, "0")==0) {
737                                 g_free (path_string);
738                                 break;
739                         }
740                         g_free (path_string);
741                 }
742                 gtk_tree_path_free (path);
743                 return result;
744         } else {
745                 return TRUE;
746         }
747         
748 }
749
750 gboolean        
751 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
752 {
753         ModestMsgViewWindowPrivate *priv;
754         GtkTreeIter tmp_iter;
755         gboolean has_next = FALSE;
756
757         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
758         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
759
760         if (priv->header_model) {
761                 tmp_iter = priv->iter;
762                 while (gtk_tree_model_iter_next (priv->header_model, &tmp_iter)) {
763                         TnyHeader *header;
764                         TnyFolder *folder;
765                         TnyMsg *msg;
766
767                         priv->iter = tmp_iter;
768                         gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
769                                             &header, -1);
770                         if (!header)
771                                 break;
772                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
773                                 continue;
774
775                         folder = tny_header_get_folder (header);
776                         if (!folder)
777                                 break;
778                         msg = tny_folder_get_msg (folder, header, NULL);
779                         if (!msg) {
780                                 g_object_unref (folder);
781                                 break;
782                         }
783                         has_next = TRUE;
784                         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
785                         modest_msg_view_window_update_dimmed (window);
786                         modest_msg_view_window_update_priority (window);
787                         gtk_widget_grab_focus (priv->msg_view);
788
789                         g_object_unref (msg);
790                         break;
791                 }
792
793                 return has_next;
794         } else {
795                 return FALSE;
796         }
797 }
798
799 gboolean        
800 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
801 {
802         ModestMsgViewWindowPrivate *priv;
803
804         g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
805         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
806
807         if (priv->header_model) {
808                 GtkTreePath *path;
809                 gboolean has_prev = FALSE;
810
811                 path = gtk_tree_model_get_path (priv->header_model, &(priv->iter));
812                 while (gtk_tree_path_prev (path)) {
813                         TnyHeader *header;
814                         TnyFolder *folder;
815                         TnyMsg *msg;
816
817                         gtk_tree_model_get_iter (priv->header_model, &(priv->iter), path);
818                         gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
819                                             &header, -1);
820                         if (!header)
821                                 break;
822                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED)
823                                 continue;
824                         folder = tny_header_get_folder (header);
825                         if (!folder)
826                                 break;
827                         msg = tny_folder_get_msg (folder, header, NULL);
828                         if (!msg) {
829                                 g_object_unref (folder);
830                                 break;
831                         }
832                         has_prev = TRUE;
833                         modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
834                         modest_msg_view_window_update_dimmed (window);
835                         modest_msg_view_window_update_priority (window);
836                         gtk_widget_grab_focus (priv->msg_view);
837
838                         g_object_unref (msg);
839                         break;
840                 }
841                 gtk_tree_path_free (path);
842                 return has_prev;
843         } else {
844                 return FALSE;
845         }
846 }
847
848 static TnyFolderType
849 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
850 {
851         ModestMsgViewWindowPrivate *priv;
852         TnyMsg *msg;
853         TnyFolderType folder_type;
854
855         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
856
857         folder_type = TNY_FOLDER_TYPE_UNKNOWN;
858
859         msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
860         if (msg) {
861                 TnyFolder *folder;
862
863                 folder = tny_msg_get_folder (msg);
864                 
865                 if (folder) {
866                         folder_type = tny_folder_get_folder_type (folder);
867                         
868                         if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
869                                 const gchar *fname = tny_folder_get_name (folder);
870                                 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
871                         }
872
873                         g_object_unref (folder);
874                 }
875         }
876
877         return folder_type;
878 }
879
880 static void
881 modest_msg_view_window_update_dimmed (ModestMsgViewWindow *window)
882 {
883         ModestWindowPrivate *parent_priv;
884         GtkAction *widget;
885         gboolean is_first, is_last;
886         TnyFolderType folder_type;
887         gboolean is_not_sent;
888
889         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
890
891         is_first = modest_msg_view_window_is_first_message (window);
892         is_last = modest_msg_view_window_is_last_message (window);
893
894         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
895         gtk_action_set_sensitive (widget, !is_first);
896         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewPreviousMessageMenu");
897         gtk_action_set_sensitive (widget, !is_first);
898                 
899         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
900         gtk_action_set_sensitive (widget, !is_last);
901         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewNextMessageMenu");
902         gtk_action_set_sensitive (widget, !is_last);
903
904         folder_type = modest_msg_view_window_get_folder_type (MODEST_MSG_VIEW_WINDOW (window));
905         is_not_sent = ((folder_type == TNY_FOLDER_TYPE_DRAFTS)||(folder_type == TNY_FOLDER_TYPE_OUTBOX));
906         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
907         gtk_action_set_sensitive (widget, !is_not_sent);
908         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageReplyMenu");
909         gtk_action_set_sensitive (widget, !is_not_sent);
910         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageReplyAllMenu");
911         gtk_action_set_sensitive (widget, !is_not_sent);
912         widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/MessageMenu/MessageForwardMenu");
913         gtk_action_set_sensitive (widget, !is_not_sent);
914                 
915 }
916
917 static void
918 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
919 {
920         ModestMsgViewWindowPrivate *priv;
921         TnyHeaderFlags flags = 0;
922
923         priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
924
925         if (priv->header_model) {
926                 TnyHeader *header;
927
928                 gtk_tree_model_get (priv->header_model, &(priv->iter), TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
929                                     &header, -1);
930                 flags = tny_header_get_flags (header);
931         }
932
933         modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
934
935 }
936
937 static void
938 modest_msg_view_window_toggle_fullscreen (GtkAction *action, ModestWindow *window)
939 {
940         ModestWindowPrivate *parent_priv;
941         GtkAction *fs_toggle_action;
942         gboolean active;
943
944         parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
945
946         fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ShowToggleFullscreenMenu");
947         active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action));
948         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), !active);
949 }