* src/widgets/modest-gtkhtml-msg-view.c:
[modest] / src / widgets / modest-gtkhtml-msg-view.c
1 /* Copyright (c) 2006, 2007, 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
30 #include <config.h>
31 #include <tny-gtk-text-buffer-stream.h>
32 #include <string.h>
33 #include <regex.h>
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <glib/gi18n.h>
37 #include <tny-list.h>
38 #include <tny-simple-list.h>
39 #include <tny-vfs-stream.h>
40
41 #include <modest-tny-msg.h>
42 #include <modest-text-utils.h>
43 #include <widgets/modest-msg-view.h>
44 #include <widgets/modest-mail-header-view.h>
45 #include <widgets/modest-attachments-view.h>
46 #include <modest-marshal.h>
47 #include <widgets/modest-gtkhtml-mime-part-view.h>
48 #include <widgets/modest-gtkhtml-msg-view.h>
49 #include <widgets/modest-isearch-view.h>
50 #include <widgets/modest-ui-constants.h>
51
52 /* FIXNE: we should have no maemo-deps in widgets/ */
53 #ifdef MODEST_PLATFORM_MAEMO
54 #include "maemo/modest-hildon-includes.h"
55 #endif /*MODEST_PLATFORM_MAEMO*/
56
57
58 /* 'private'/'protected' functions */
59 static void     modest_gtkhtml_msg_view_class_init   (ModestGtkhtmlMsgViewClass *klass);
60 static void     tny_msg_view_init (gpointer g, gpointer iface_data);
61 static void     tny_mime_part_view_init (gpointer g, gpointer iface_data);
62 static void     modest_mime_part_view_init (gpointer g, gpointer iface_data);
63 static void     modest_zoomable_init (gpointer g, gpointer iface_data);
64 static void     modest_isearch_view_init (gpointer g, gpointer iface_data);
65 static void     modest_msg_view_init (gpointer g, gpointer iface_data);
66 static void     modest_gtkhtml_msg_view_init         (ModestGtkhtmlMsgView *obj);
67 static void     modest_gtkhtml_msg_view_finalize     (GObject *obj);
68 static void     modest_gtkhtml_msg_view_destroy     (GtkObject *obj);
69 static void     set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
70 static void     get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
71
72 /* headers signals */
73 static void on_recpt_activated (ModestMailHeaderView *header_view, const gchar *address, ModestGtkhtmlMsgView *msg_view);
74 static void on_attachment_activated (ModestAttachmentsView * att_view, TnyMimePart *mime_part, gpointer userdata);
75 static void on_view_images_clicked (GtkButton * button, gpointer self);
76
77 /* body view signals */
78 static gboolean on_activate_link (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *msg_view);
79 static gboolean on_fetch_url (GtkWidget *widget, const gchar *uri, TnyStream *stream,
80                               ModestGtkhtmlMsgView *msg_view);
81 static gboolean on_link_hover (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *msg_view);
82
83 #ifdef MAEMO_CHANGES
84 static void     on_tap_and_hold (GtkWidget *widget, gpointer userdata); 
85 #endif /*MAEMO_CHANGES*/
86
87
88 /* size allocation and drawing handlers */
89 static void get_view_allocation (ModestGtkhtmlMsgView *msg_view, GtkAllocation *allocation);
90 static void size_request (GtkWidget *widget, GtkRequisition *req);
91 static void size_allocate (GtkWidget *widget, GtkAllocation *alloc);
92 static void realize (GtkWidget *widget);
93 static void unrealize (GtkWidget *widget);
94 static gint expose (GtkWidget *widget, GdkEventExpose *event);
95 static void reclamp_adjustment (GtkAdjustment *adj, gboolean *value_changed);
96 static void set_hadjustment_values (ModestGtkhtmlMsgView *msg_view, gboolean *value_changed);
97 static void set_scroll_adjustments (ModestGtkhtmlMsgView *msg_view, GtkAdjustment *hadj, GtkAdjustment *vadj);
98 static void adjustment_value_changed (GtkAdjustment *adj, gpointer data);
99 static void html_adjustment_changed (GtkAdjustment *adj, gpointer data);
100 static void disconnect_vadjustment (ModestGtkhtmlMsgView *obj);
101 static void disconnect_hadjustment (ModestGtkhtmlMsgView *obj);
102 static gboolean idle_readjust_scroll (ModestGtkhtmlMsgView *obj);
103
104 /* vertical panning implementation */
105 #ifdef MAEMO_CHANGES
106 static gboolean motion_notify_event (GtkWidget *widget,
107                                      GdkEventMotion *event,
108                                      gpointer userdata);
109 #endif
110
111 /* GtkContainer methods */
112 static void forall (GtkContainer *container, gboolean include_internals,
113                     GtkCallback callback, gpointer userdata);
114 static void container_remove (GtkContainer *container, GtkWidget *widget);
115
116 /* TnyMimePartView implementation */
117 static void modest_msg_view_mp_clear (TnyMimePartView *self);
118 static void modest_msg_view_mp_set_part (TnyMimePartView *self, TnyMimePart *part);
119 static void modest_msg_view_mp_set_part_default (TnyMimePartView *self, TnyMimePart *part);
120 static TnyMimePart* modest_msg_view_mp_get_part (TnyMimePartView *self);
121 static TnyMimePart* modest_msg_view_mp_get_part_default (TnyMimePartView *self);
122 /* ModestMimePartView implementation */
123 static gboolean modest_msg_view_mp_is_empty (ModestMimePartView *self);
124 static gboolean modest_msg_view_mp_is_empty_default (ModestMimePartView *self);
125 /* TnyMsgView implementation */
126 static TnyMsg *modest_msg_view_get_msg (TnyMsgView *self);
127 static TnyMsg *modest_msg_view_get_msg_default (TnyMsgView *self);
128 static void modest_msg_view_set_msg (TnyMsgView *self, TnyMsg *msg);
129 static void modest_msg_view_set_msg_default (TnyMsgView *self, TnyMsg *msg);
130 static void modest_msg_view_clear (TnyMsgView *self);
131 static void modest_msg_view_clear_default (TnyMsgView *self);
132 static void modest_msg_view_set_unavailable (TnyMsgView *self);
133 static void modest_msg_view_set_unavailable_default (TnyMsgView *self);
134 static TnyMimePartView *modest_msg_view_create_mime_part_view_for (TnyMsgView *self, TnyMimePart *part);
135 static TnyMimePartView *modest_msg_view_create_mime_part_view_for_default (TnyMsgView *self, TnyMimePart *part);
136 static TnyMsgView *modest_msg_view_create_new_inline_viewer (TnyMsgView *self);
137 static TnyMsgView *modest_msg_view_create_new_inline_viewer_default (TnyMsgView *self);
138 /* ModestZoomable implementation */
139 static gdouble modest_msg_view_get_zoom (ModestZoomable *self);
140 static void modest_msg_view_set_zoom (ModestZoomable *self, gdouble value);
141 static gboolean modest_msg_view_zoom_minus (ModestZoomable *self);
142 static gboolean modest_msg_view_zoom_plus (ModestZoomable *self);
143 static gdouble modest_msg_view_get_zoom_default (ModestZoomable *self);
144 static void modest_msg_view_set_zoom_default (ModestZoomable *self, gdouble value);
145 static gboolean modest_msg_view_zoom_minus_default (ModestZoomable *self);
146 static gboolean modest_msg_view_zoom_plus_default (ModestZoomable *self);
147 /* ModestISearchView implementation */
148 static gboolean modest_msg_view_search (ModestISearchView *self, const gchar *string);
149 static gboolean modest_msg_view_search_default (ModestISearchView *self, const gchar *string);
150 static gboolean modest_msg_view_search_next (ModestISearchView *self);
151 static gboolean modest_msg_view_search_next_default (ModestISearchView *self);
152 /* ModestMsgView implementation */
153 static GtkAdjustment *modest_gtkhtml_msg_view_get_vadjustment (ModestMsgView *self);
154 static GtkAdjustment *modest_gtkhtml_msg_view_get_hadjustment (ModestMsgView *self);
155 static void modest_gtkhtml_msg_view_set_vadjustment (ModestMsgView *self, GtkAdjustment *vadj);
156 static void modest_gtkhtml_msg_view_set_hadjustment (ModestMsgView *self, GtkAdjustment *hadj);
157 static void modest_gtkhtml_msg_view_set_shadow_type (ModestMsgView *self, GtkShadowType type);
158 static GtkShadowType modest_gtkhtml_msg_view_get_shadow_type (ModestMsgView *self);
159 static TnyHeaderFlags modest_gtkhtml_msg_view_get_priority (ModestMsgView *self);
160 static void modest_gtkhtml_msg_view_set_priority (ModestMsgView *self, TnyHeaderFlags flags);
161 static TnyList *modest_gtkhtml_msg_view_get_selected_attachments (ModestMsgView *self);
162 static TnyList *modest_gtkhtml_msg_view_get_attachments (ModestMsgView *self);
163 static void modest_gtkhtml_msg_view_grab_focus (ModestMsgView *self);
164 static void modest_gtkhtml_msg_view_remove_attachment (ModestMsgView *view, TnyMimePart *attachment);
165 static GtkAdjustment *modest_gtkhtml_msg_view_get_vadjustment_default (ModestMsgView *self);
166 static GtkAdjustment *modest_gtkhtml_msg_view_get_hadjustment_default (ModestMsgView *self);
167 static void modest_gtkhtml_msg_view_set_vadjustment_default (ModestMsgView *self, GtkAdjustment *vadj);
168 static void modest_gtkhtml_msg_view_set_hadjustment_default (ModestMsgView *self, GtkAdjustment *hadj);
169 static void modest_gtkhtml_msg_view_set_shadow_type_default (ModestMsgView *self, GtkShadowType type);
170 static GtkShadowType modest_gtkhtml_msg_view_get_shadow_type_default (ModestMsgView *self);
171 static TnyHeaderFlags modest_gtkhtml_msg_view_get_priority_default (ModestMsgView *self);
172 static void modest_gtkhtml_msg_view_set_priority_default (ModestMsgView *self, TnyHeaderFlags flags);
173 static TnyList *modest_gtkhtml_msg_view_get_selected_attachments_default (ModestMsgView *self);
174 static TnyList *modest_gtkhtml_msg_view_get_attachments_default (ModestMsgView *self);
175 static void modest_gtkhtml_msg_view_grab_focus_default (ModestMsgView *self);
176 static void modest_gtkhtml_msg_view_remove_attachment_default (ModestMsgView *view, TnyMimePart *attachment);
177
178 /* internal api */
179 static TnyMsg   *get_message   (ModestGtkhtmlMsgView *self);
180 static void     set_message    (ModestGtkhtmlMsgView *self, TnyMsg *tny_msg);
181 static gboolean is_empty       (ModestGtkhtmlMsgView *self); 
182 static void     set_zoom       (ModestGtkhtmlMsgView *self, gdouble zoom);
183 static gdouble  get_zoom       (ModestGtkhtmlMsgView *self);
184 static gboolean search         (ModestGtkhtmlMsgView *self, const gchar *search);
185 static gboolean search_next    (ModestGtkhtmlMsgView *self);
186 static GtkAdjustment *get_vadjustment (ModestGtkhtmlMsgView *self);
187 static GtkAdjustment *get_hadjustment (ModestGtkhtmlMsgView *self);
188 static void set_vadjustment (ModestGtkhtmlMsgView *self, GtkAdjustment *vadj);
189 static void set_hadjustment (ModestGtkhtmlMsgView *self, GtkAdjustment *hadj);
190 static void set_shadow_type (ModestGtkhtmlMsgView *self, GtkShadowType type);
191 static GtkShadowType get_shadow_type (ModestGtkhtmlMsgView *self);
192 static TnyHeaderFlags get_priority (ModestGtkhtmlMsgView *self);
193 static void set_priority (ModestGtkhtmlMsgView *self, TnyHeaderFlags flags);
194 static TnyList *get_selected_attachments (ModestGtkhtmlMsgView *self);
195 static TnyList *get_attachments (ModestGtkhtmlMsgView *self);
196 static void grab_focus (ModestGtkhtmlMsgView *self);
197 static void remove_attachment (ModestGtkhtmlMsgView *view, TnyMimePart *attachment);
198
199 /* list properties */
200 enum {
201         PROP_0,
202         PROP_HADJUSTMENT,
203         PROP_VADJUSTMENT,
204         PROP_SHADOW_TYPE
205 };
206
207 typedef struct _ModestGtkhtmlMsgViewPrivate ModestGtkhtmlMsgViewPrivate;
208 struct _ModestGtkhtmlMsgViewPrivate {
209         GtkWidget   *body_view;
210         GtkWidget   *mail_header_view;
211         GtkWidget   *attachments_view;
212
213         TnyMsg      *msg;
214
215         /* embedded elements */
216         GtkWidget   *headers_box;
217         GtkWidget   *html_scroll;
218         GtkWidget   *attachments_box;
219         GtkWidget   *view_images_button;
220
221         /* internal adjustments for set_scroll_adjustments */
222         GtkAdjustment *hadj;
223         GtkAdjustment *vadj;
224         GtkShadowType shadow_type;
225
226         /* gdk windows for drawing */
227         GdkWindow *view_window;
228         GdkWindow *headers_window;
229         GdkWindow *html_window;
230
231         /* zoom */
232         gdouble current_zoom;
233
234         /* link click management */
235         gchar *last_url;
236 };
237
238 #define MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
239                                                                                  MODEST_TYPE_GTKHTML_MSG_VIEW, \
240                                                                                  ModestGtkhtmlMsgViewPrivate))
241
242 /* globals */
243 static GtkContainerClass *parent_class = NULL;
244
245 GType
246 modest_gtkhtml_msg_view_get_type (void)
247 {
248         static GType my_type = 0;
249         if (!my_type) {
250                 static const GTypeInfo my_info = {
251                         sizeof(ModestGtkhtmlMsgViewClass),
252                         NULL,           /* base init */
253                         NULL,           /* base finalize */
254                         (GClassInitFunc) modest_gtkhtml_msg_view_class_init,
255                         NULL,           /* class finalize */
256                         NULL,           /* class data */
257                         sizeof(ModestGtkhtmlMsgView),
258                         1,              /* n_preallocs */
259                         (GInstanceInitFunc) modest_gtkhtml_msg_view_init,
260                         NULL
261                 };
262                 static const GInterfaceInfo tny_msg_view_info = 
263                 {
264                   (GInterfaceInitFunc) tny_msg_view_init, /* interface_init */
265                   NULL,         /* interface_finalize */
266                   NULL          /* interface_data */
267                 };
268
269                 static const GInterfaceInfo tny_mime_part_view_info = 
270                 {
271                   (GInterfaceInitFunc) tny_mime_part_view_init, /* interface_init */
272                   NULL,         /* interface_finalize */
273                   NULL          /* interface_data */
274                 };
275
276                 static const GInterfaceInfo modest_mime_part_view_info = 
277                 {
278                   (GInterfaceInitFunc) modest_mime_part_view_init, /* interface_init */
279                   NULL,         /* interface_finalize */
280                   NULL          /* interface_data */
281                 };
282
283                 static const GInterfaceInfo modest_zoomable_info = 
284                 {
285                   (GInterfaceInitFunc) modest_zoomable_init, /* interface_init */
286                   NULL,         /* interface_finalize */
287                   NULL          /* interface_data */
288                 };
289
290                 static const GInterfaceInfo modest_isearch_view_info = 
291                 {
292                   (GInterfaceInitFunc) modest_isearch_view_init, /* interface_init */
293                   NULL,         /* interface_finalize */
294                   NULL          /* interface_data */
295                 };
296
297                 static const GInterfaceInfo modest_msg_view_info = 
298                 {
299                   (GInterfaceInitFunc) modest_msg_view_init, /* interface_init */
300                   NULL,         /* interface_finalize */
301                   NULL          /* interface_data */
302                 };
303
304                 my_type = g_type_register_static (GTK_TYPE_CONTAINER,
305                                                   "ModestGtkhtmlMsgView",
306                                                   &my_info, 0);
307
308                 g_type_add_interface_static (my_type, TNY_TYPE_MIME_PART_VIEW, 
309                         &tny_mime_part_view_info);
310
311                 g_type_add_interface_static (my_type, MODEST_TYPE_MIME_PART_VIEW, 
312                         &modest_mime_part_view_info);
313
314                 g_type_add_interface_static (my_type, TNY_TYPE_MSG_VIEW, 
315                         &tny_msg_view_info);
316
317                 g_type_add_interface_static (my_type, MODEST_TYPE_ZOOMABLE, 
318                         &modest_zoomable_info);
319
320                 g_type_add_interface_static (my_type, MODEST_TYPE_ISEARCH_VIEW, 
321                         &modest_isearch_view_info);
322
323                 g_type_add_interface_static (my_type, MODEST_TYPE_MSG_VIEW, 
324                         &modest_msg_view_info);
325         }
326         return my_type;
327 }
328
329 static void
330 modest_gtkhtml_msg_view_class_init (ModestGtkhtmlMsgViewClass *klass)
331 {
332         GObjectClass *gobject_class;
333         GtkWidgetClass *widget_class;
334         GtkObjectClass *gtkobject_class;
335         GtkContainerClass *container_class;
336         gobject_class = (GObjectClass*) klass;
337         widget_class = (GtkWidgetClass *) klass;
338         gtkobject_class = (GtkObjectClass *) klass;
339         container_class = (GtkContainerClass *) klass;
340
341         parent_class            = g_type_class_peek_parent (klass);
342         gobject_class->finalize = modest_gtkhtml_msg_view_finalize;
343         gobject_class->set_property = set_property;
344         gobject_class->get_property = get_property;
345         gtkobject_class->destroy = modest_gtkhtml_msg_view_destroy;
346
347         widget_class->realize = realize;
348         widget_class->unrealize = unrealize;
349         widget_class->expose_event = expose;
350         widget_class->size_request = size_request;
351         widget_class->size_allocate = size_allocate;
352
353         container_class->forall = forall;
354         container_class->remove = container_remove;
355
356         klass->set_scroll_adjustments = set_scroll_adjustments;
357         klass->get_part_func = modest_msg_view_mp_get_part_default;
358         klass->set_part_func = modest_msg_view_mp_set_part_default;
359         klass->is_empty_func = modest_msg_view_mp_is_empty_default;
360         klass->get_msg_func = modest_msg_view_get_msg_default;
361         klass->set_msg_func = modest_msg_view_set_msg_default;
362         klass->set_unavailable_func = modest_msg_view_set_unavailable_default;
363         klass->clear_func = modest_msg_view_clear_default;
364         klass->create_mime_part_view_for_func = modest_msg_view_create_mime_part_view_for_default;
365         klass->create_new_inline_viewer_func = modest_msg_view_create_new_inline_viewer_default;
366         klass->get_zoom_func = modest_msg_view_get_zoom_default;
367         klass->set_zoom_func = modest_msg_view_set_zoom_default;
368         klass->zoom_minus_func = modest_msg_view_zoom_minus_default;
369         klass->zoom_plus_func = modest_msg_view_zoom_plus_default;
370         klass->search_func = modest_msg_view_search_default;
371         klass->search_next_func = modest_msg_view_search_next_default;
372         klass->get_vadjustment_func = modest_gtkhtml_msg_view_get_vadjustment_default;
373         klass->get_hadjustment_func = modest_gtkhtml_msg_view_get_hadjustment_default;
374         klass->set_vadjustment_func = modest_gtkhtml_msg_view_set_vadjustment_default;
375         klass->set_hadjustment_func = modest_gtkhtml_msg_view_set_hadjustment_default;
376         klass->get_shadow_type_func = modest_gtkhtml_msg_view_get_shadow_type_default;
377         klass->set_shadow_type_func = modest_gtkhtml_msg_view_set_shadow_type_default;
378         klass->get_priority_func = modest_gtkhtml_msg_view_get_priority_default;
379         klass->set_priority_func = modest_gtkhtml_msg_view_set_priority_default;
380         klass->get_selected_attachments_func = modest_gtkhtml_msg_view_get_selected_attachments_default;
381         klass->get_attachments_func = modest_gtkhtml_msg_view_get_attachments_default;
382         klass->grab_focus_func = modest_gtkhtml_msg_view_grab_focus_default;
383         klass->remove_attachment_func = modest_gtkhtml_msg_view_remove_attachment_default;
384
385         g_type_class_add_private (gobject_class, sizeof(ModestGtkhtmlMsgViewPrivate));
386
387         g_object_class_install_property (gobject_class,
388                                          PROP_HADJUSTMENT,
389                                          g_param_spec_object ("hadjustment", 
390                                                               _("Horizontal adjustment"),
391                                                               _("GtkAdjustment with information of the horizontal visible position"),
392                                                               GTK_TYPE_ADJUSTMENT,
393                                                               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
394
395         g_object_class_install_property (gobject_class,
396                                          PROP_VADJUSTMENT,
397                                          g_param_spec_object ("vadjustment", 
398                                                               _("Vertical adjustment"),
399                                                               _("GtkAdjustment with information of the vertical visible position"),
400                                                               GTK_TYPE_ADJUSTMENT,
401                                                               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
402
403         g_object_class_install_property (gobject_class,
404                                          PROP_SHADOW_TYPE,
405                                          g_param_spec_enum ("shadow_type", 
406                                                             _("Shadow type"),
407                                                             _("Kind of shadow that's shown around the view"),
408                                                             GTK_TYPE_SHADOW_TYPE,
409                                                             GTK_SHADOW_IN,
410                                                             G_PARAM_READABLE | G_PARAM_WRITABLE ));
411
412         widget_class->set_scroll_adjustments_signal =
413                 g_signal_new ("set_scroll_adjustments",
414                               G_OBJECT_CLASS_TYPE (gobject_class),
415                               G_SIGNAL_RUN_LAST|G_SIGNAL_ACTION,
416                               G_STRUCT_OFFSET (ModestGtkhtmlMsgViewClass, set_scroll_adjustments),
417                               NULL, NULL,
418                               modest_marshal_VOID__POINTER_POINTER,
419                               G_TYPE_NONE, 2,
420                               GTK_TYPE_ADJUSTMENT,
421                               GTK_TYPE_ADJUSTMENT);
422 }
423
424 static void
425 set_property (GObject *object, 
426               guint prop_id, 
427               const GValue *value, 
428               GParamSpec *pspec)
429 {
430         ModestGtkhtmlMsgView *self = MODEST_GTKHTML_MSG_VIEW (object);
431
432         switch (prop_id) {
433         case PROP_HADJUSTMENT:
434                 set_hadjustment (self, g_value_get_object (value));
435                 break;
436         case PROP_VADJUSTMENT:
437                 set_vadjustment (self, g_value_get_object (value));
438                 break;
439         case PROP_SHADOW_TYPE:
440                 set_shadow_type (self, g_value_get_enum (value));
441                 break;
442         default:
443                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
444                 break;
445         }
446 }
447
448 static void
449 get_property (GObject *object, 
450               guint prop_id, 
451               GValue *value, 
452               GParamSpec *pspec)
453 {
454         ModestGtkhtmlMsgView *self = MODEST_GTKHTML_MSG_VIEW (object);
455         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
456
457         switch (prop_id) {
458         case PROP_HADJUSTMENT:
459                 g_value_set_object (value, priv->hadj);
460                 break;
461         case PROP_VADJUSTMENT:
462                 g_value_set_object (value, priv->vadj);
463                 break;
464         case PROP_SHADOW_TYPE:
465                 g_value_set_enum (value, priv->shadow_type);
466                 break;
467         default:
468                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
469                 break;
470         }
471 }
472
473 static void
474 disconnect_hadjustment (ModestGtkhtmlMsgView *self)
475 {
476         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
477
478         if (priv->hadj) {
479                 g_signal_handlers_disconnect_by_func(priv->hadj, adjustment_value_changed, self);
480                 g_object_unref (priv->hadj);
481                 priv->hadj = NULL;
482         }
483 }
484
485 static void
486 disconnect_vadjustment (ModestGtkhtmlMsgView *self)
487 {
488         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
489
490         if (priv->vadj) {
491                 g_signal_handlers_disconnect_by_func(priv->vadj, adjustment_value_changed, self);
492                 g_object_unref (priv->vadj);
493                 priv->vadj = NULL;
494         }
495 }
496
497 static void 
498 get_view_allocation (ModestGtkhtmlMsgView *self, GtkAllocation *allocation)
499 {
500         /* This method gets the allocation of the widget in parent widget. It's the
501            real position and size of the widget */
502         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
503         
504         allocation->x = 0;
505         allocation->y = 0;
506
507         if (priv->shadow_type != GTK_SHADOW_NONE) {
508                 allocation->x = GTK_WIDGET (self)->style->xthickness;
509                 allocation->y = GTK_WIDGET (self)->style->ythickness;
510         }
511
512         allocation->width = MAX (1, (GTK_WIDGET (self)->allocation.width) - allocation->x * 2);
513         allocation->height = MAX (1, (GTK_WIDGET (self)->allocation.height) - allocation->y * 2);
514
515 }
516
517 static void 
518 reclamp_adjustment (GtkAdjustment *adj, 
519                     gboolean *value_changed)
520 {
521         gdouble value = adj->value;
522
523         /* Correct value to be inside the expected values of a scroll */
524
525         value = CLAMP (value, 0, adj->upper - adj->page_size);
526
527         if (value != adj->value) {
528                 adj->value = value;
529                 if (value_changed)
530                         *value_changed = TRUE;
531         } else if (value_changed) {
532                 *value_changed = FALSE;
533         }
534 }
535
536 static void 
537 set_hadjustment_values (ModestGtkhtmlMsgView *self,
538                         gboolean *value_changed)
539 {
540         GtkAllocation view_allocation;
541         GtkAdjustment *hadj = get_hadjustment (self);
542
543         get_view_allocation (self, &view_allocation);
544         hadj->page_size = view_allocation.width;
545         hadj->step_increment = view_allocation.width * 0.1;
546         hadj->page_increment = view_allocation.width * 0.9;
547
548         hadj->lower = 0;
549         hadj->upper = view_allocation.width;
550
551         reclamp_adjustment (hadj, value_changed);
552
553 }
554
555 static void 
556 set_vadjustment_values (ModestGtkhtmlMsgView *self,
557                         gboolean *value_changed)
558 {
559         GtkAllocation view_allocation;
560         GtkAdjustment *vadj = get_vadjustment (self);
561         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
562         gint full_height = 0;
563         GtkAdjustment *html_vadj;
564
565         get_view_allocation (self, &view_allocation);
566         vadj->page_size = view_allocation.height;
567         vadj->step_increment = view_allocation.height * 0.1;
568         vadj->page_increment = view_allocation.height * 0.9;
569
570         vadj->lower = 0;
571
572         if (priv->headers_box && GTK_WIDGET_VISIBLE(priv->headers_box)) {
573                 GtkRequisition child_requisition;
574
575                 gtk_widget_get_child_requisition (priv->headers_box, &child_requisition);
576                 full_height = child_requisition.height;
577         } else {
578                 full_height = 0;
579         }
580         
581         /* Get the real height of the embedded html */
582         if (priv->html_scroll && GTK_WIDGET_VISIBLE(priv->html_scroll)) {
583                 html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->html_scroll));
584                 full_height += html_vadj->upper;
585         }
586
587         vadj->upper = MAX (view_allocation.height, full_height);
588
589         reclamp_adjustment (vadj, value_changed);
590
591 }
592
593 static void
594 set_scroll_adjustments (ModestGtkhtmlMsgView *self,
595                         GtkAdjustment *hadj,
596                         GtkAdjustment *vadj)
597 {
598         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
599         set_hadjustment (self, hadj);
600         set_vadjustment (self, vadj);
601
602         gtk_container_set_focus_vadjustment (GTK_CONTAINER (priv->body_view), vadj);
603 }
604
605 static void
606 realize (GtkWidget *widget)
607 {
608         ModestGtkhtmlMsgView *self = MODEST_GTKHTML_MSG_VIEW (widget);
609         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
610         GtkAdjustment *hadj = get_hadjustment (self);
611         GtkAdjustment *vadj = get_vadjustment (self);
612         GdkWindowAttr attributes;
613         gint event_mask;
614         gint attributes_mask;
615         GtkAllocation view_allocation;
616
617         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
618
619         /* The structure of the GdkWindow's is:
620          *    * widget->window: the shown gdkwindow embedding all the stuff inside
621          *    * view_window: a backing store gdkwindow containing the headers and contents
622          *      being scrolled. This window should have all the visible and non visible
623          *      widgets inside.
624          *    * headers_window: gdk window for headers_box.
625          *    * html_window: gdk window for html_scroll (the scrolled window containing the
626          *      body view showing the contents of the mail).
627          */
628
629         attributes.x = widget->allocation.x;
630         attributes.y = widget->allocation.y;
631         attributes.width = widget->allocation.width;
632         attributes.height = widget->allocation.height;
633         attributes.window_type = GDK_WINDOW_CHILD;
634         attributes.wclass = GDK_INPUT_OUTPUT;
635         attributes.visual = gtk_widget_get_visual (widget);
636         attributes.colormap = gtk_widget_get_colormap (widget);
637
638         event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
639         attributes.event_mask = event_mask | GDK_BUTTON_PRESS_MASK;
640         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
641
642         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
643                                          &attributes, attributes_mask);
644         gdk_window_set_user_data (widget->window, self);
645
646         get_view_allocation (self, &view_allocation);
647
648         attributes.x = view_allocation.x;
649         attributes.y = view_allocation.y;
650         attributes.width = view_allocation.width;
651         attributes.height = view_allocation.height;
652         attributes.event_mask = 0;
653         priv->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
654         gdk_window_set_user_data (priv->view_window, self);
655         gdk_window_set_back_pixmap (priv->view_window, NULL, FALSE);
656
657         attributes.x = -hadj->value;
658         attributes.y = -vadj->value;
659         attributes.width = hadj->upper;
660         if (priv->headers_box)
661                 attributes.height = GTK_WIDGET (priv->headers_box)->allocation.height;
662         else
663                 attributes.height = 1;
664         attributes.event_mask = event_mask;
665
666         priv->headers_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
667         gdk_window_set_user_data (priv->headers_window, self);
668
669         if (priv->headers_box)
670                 gtk_widget_set_parent_window (priv->headers_box, priv->headers_window);
671
672         attributes.x = -hadj->value;
673         if (priv->headers_box)
674                 attributes.y = GTK_WIDGET (priv->headers_box)->allocation.height - vadj->value;
675         else 
676                 attributes.y = -vadj->value;
677         attributes.width = hadj->upper;
678         if (priv->headers_box)
679                 attributes.height = vadj->upper - GTK_WIDGET (priv->headers_box)->allocation.height;
680         else
681                 attributes.height = vadj->upper;
682         attributes.event_mask = event_mask;
683
684         priv->html_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
685         gdk_window_set_user_data (priv->html_window, self);
686
687         if (priv->html_scroll)
688                 gtk_widget_set_parent_window (priv->html_scroll, priv->html_window);
689
690         widget->style = gtk_style_attach (widget->style, widget->window);
691         gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
692         gtk_style_set_background (widget->style, priv->headers_window, GTK_STATE_NORMAL);
693         gtk_style_set_background (widget->style, priv->html_window, GTK_STATE_NORMAL);
694
695         gtk_paint_flat_box(widget->style, priv->headers_window, GTK_STATE_NORMAL,
696                            GTK_SHADOW_NONE, 
697                            NULL, widget, "msgviewheaders",
698                            0,0,-1,-1);
699         gtk_paint_flat_box(widget->style, priv->html_window, GTK_STATE_NORMAL,
700                            GTK_SHADOW_NONE, 
701                            NULL, widget, "msgviewcontents",
702                            0,0,-1,-1);
703
704         gdk_window_show (priv->view_window);
705         gdk_window_show (priv->headers_window);
706         gdk_window_show (priv->html_window);
707
708 }
709
710 static void
711 unrealize (GtkWidget *widget)
712 {
713         ModestGtkhtmlMsgView *self = MODEST_GTKHTML_MSG_VIEW (widget);
714         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
715
716         gdk_window_set_user_data (priv->view_window, NULL);
717         gdk_window_destroy (priv->view_window);
718         priv->view_window = NULL;
719
720         gdk_window_set_user_data (priv->headers_window, NULL);
721         gdk_window_destroy (priv->headers_window);
722         priv->headers_window = NULL;
723
724         gdk_window_set_user_data (priv->html_window, NULL);
725         gdk_window_destroy (priv->html_window);
726         priv->html_window = NULL;
727
728         if (GTK_WIDGET_CLASS (parent_class)->unrealize)
729                 ( * GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
730
731 }
732
733 static gint
734 expose (GtkWidget *widget, 
735         GdkEventExpose *event)
736 {
737         ModestGtkhtmlMsgView *self;
738         ModestGtkhtmlMsgViewPrivate *priv;
739
740         if (GTK_WIDGET_DRAWABLE (widget)) {
741                 self = MODEST_GTKHTML_MSG_VIEW (widget);
742                 priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
743                 if (event->window == widget->window) {
744                         gtk_paint_shadow (widget->style, widget->window,
745                                           GTK_STATE_NORMAL, priv->shadow_type,
746                                           &event->area, widget, "msgview",
747                                           0,0,-1,-1);
748                 } else if (event->window == priv->headers_window) {
749                         gtk_paint_flat_box(widget->style, priv->headers_window, GTK_STATE_NORMAL,
750                                            GTK_SHADOW_NONE, 
751                                            &event->area, widget, "msgviewheaders",
752                                            0,0,-1,-1);
753                 } else if (event->window == priv->html_window) {
754                         gtk_paint_flat_box(widget->style, priv->html_window, GTK_STATE_NORMAL,
755                                            GTK_SHADOW_NONE, 
756                                            &event->area, widget, "msgviewcontents",
757                                            0,0,-1,-1);
758                 }
759                 if (priv->headers_box)
760                         gtk_container_propagate_expose (GTK_CONTAINER (self), priv->headers_box, event);
761                 if (priv->html_scroll)
762                         gtk_container_propagate_expose (GTK_CONTAINER (self), priv->html_scroll, event);
763                 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
764         }
765
766         return FALSE;
767 }
768
769 static void 
770 forall (GtkContainer *container, 
771         gboolean include_internals,
772         GtkCallback callback,
773         gpointer userdata)
774 {
775         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (container);
776         g_return_if_fail (callback != NULL);
777
778         if (priv->headers_box)
779                 (*callback) (priv->headers_box, userdata);
780         if (priv->html_scroll)
781                 (*callback) (priv->html_scroll, userdata);
782 }
783
784 static void
785 container_remove (GtkContainer *container,
786                   GtkWidget *widget)
787 {
788         gboolean was_visible = FALSE;
789         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (container);
790         was_visible = GTK_WIDGET_VISIBLE (widget);
791         if (widget == priv->headers_box) {
792                 gtk_widget_unparent (priv->headers_box);
793                 priv->headers_box = NULL;
794         } else if (widget == priv->html_scroll) {
795                 gtk_widget_unparent (priv->html_scroll);
796                 priv->html_scroll = NULL;
797         } else {
798                 return;
799         }
800         if (was_visible)
801                 gtk_widget_queue_resize (GTK_WIDGET(container));
802
803 }
804
805 static void
806 size_request (GtkWidget *widget,
807               GtkRequisition *req)
808 {
809         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (widget);
810         GtkRequisition child_req;
811
812         req->width = 0;
813         req->height = 0;
814
815         gtk_widget_size_request (priv->headers_box, &child_req);
816         req->width = child_req.width;
817         req->height += child_req.height;
818         gtk_widget_size_request (priv->html_scroll, &child_req);
819         req->width = MAX (child_req.width, req->width);
820         req->height += child_req.height;
821
822 }
823
824 static void
825 size_allocate (GtkWidget *widget,
826                GtkAllocation *allocation)
827 {
828         ModestGtkhtmlMsgView *self = MODEST_GTKHTML_MSG_VIEW (widget);
829         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
830         gboolean hadj_value_changed, vadj_value_changed;
831         GtkAllocation headers_allocation, html_allocation, view_allocation;
832         GtkAdjustment *html_vadj;
833
834         if (GTK_WIDGET_MAPPED (widget) &&
835             priv->shadow_type != GTK_SHADOW_NONE && 
836             (allocation->width != widget->allocation.width ||
837              allocation->height != widget->allocation.height))
838                 gdk_window_invalidate_rect (widget->window, NULL, FALSE);
839
840         widget->allocation = *allocation;
841         set_hadjustment_values (self, &hadj_value_changed);
842         set_vadjustment_values (self, &vadj_value_changed);
843
844         get_view_allocation (self, &view_allocation);
845
846         headers_allocation.x = 0;
847         headers_allocation.y = 0;
848         headers_allocation.width = view_allocation.width;
849         if (priv->headers_box)
850                 headers_allocation.height = GTK_WIDGET (priv->headers_box)->requisition.height;
851         else
852                 headers_allocation.height = 0;
853
854         html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->html_scroll));
855
856         html_allocation.x = 0;
857         html_allocation.y = headers_allocation.height;
858         html_allocation.width = view_allocation.width;
859         html_allocation.height = MAX ((gint) html_vadj->upper, (gint)(priv->vadj->upper - headers_allocation.height));
860
861         if (GTK_WIDGET_REALIZED (widget)) {
862                 gdk_window_move_resize (widget->window,
863                                         allocation->x,
864                                         allocation->y,
865                                         allocation->width,
866                                         allocation->height);
867
868                 gdk_window_move_resize (priv->view_window,
869                                         view_allocation.x,
870                                         view_allocation.y,
871                                         view_allocation.width,
872                                         view_allocation.height);
873                 gdk_window_move_resize (priv->headers_window,
874                                         0,
875                                         (gint) (- priv->vadj->value),
876                                         allocation->width,
877                                         headers_allocation.height);
878                 gdk_window_move_resize (priv->html_window,
879                                         (gint) (- priv->hadj->value),
880                                         (gint) (html_allocation.y - priv->vadj->value),
881                                         (gint) priv->hadj->upper,
882                                         html_allocation.height);
883         }
884
885         if (priv->headers_box && GTK_WIDGET_VISIBLE (priv->headers_box)) {
886                 gtk_widget_size_allocate (priv->headers_box, &headers_allocation);
887         }
888         if (priv->html_scroll && GTK_WIDGET_VISIBLE (priv->html_scroll)) {
889                 html_allocation.x = 0;
890                 html_allocation.y = 0;
891                 html_allocation.width = (gint) priv->hadj->upper;
892                 html_allocation.height = (gint) priv->vadj->upper - headers_allocation.height;
893                 gtk_widget_size_allocate (priv->html_scroll, &html_allocation);
894         }
895         gtk_adjustment_changed (priv->hadj);
896         gtk_adjustment_changed (priv->vadj);
897
898         if (hadj_value_changed)
899                 gtk_adjustment_value_changed (priv->hadj);
900         if (vadj_value_changed)
901                 gtk_adjustment_value_changed (priv->vadj);
902
903 }
904
905 static void 
906 adjustment_value_changed (GtkAdjustment *adj, gpointer data)
907 {
908         ModestGtkhtmlMsgView *self = NULL;
909         ModestGtkhtmlMsgViewPrivate *priv = NULL;
910
911         g_return_if_fail (GTK_IS_ADJUSTMENT (adj));
912         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (data));
913
914         self = MODEST_GTKHTML_MSG_VIEW (data);
915         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
916
917         if (GTK_WIDGET_REALIZED (self)) {
918                 GtkAdjustment *hadj = get_hadjustment (self);
919                 GtkAdjustment *vadj = get_vadjustment (self);
920                 gint headers_offset = 0;
921
922                 gtk_widget_queue_resize (priv->html_scroll);
923
924                 if (priv->headers_box && GTK_WIDGET_VISIBLE (priv->headers_box)) {
925                         gint old_x, old_y;
926                         gint new_x, new_y;
927                         gdk_window_get_position (priv->headers_window, &old_x, &old_y);
928                         new_x = 0;
929                         new_y = -vadj->value;
930
931                         headers_offset = GTK_WIDGET(priv->headers_box)->allocation.height;
932
933                         if (new_x != old_x || new_y != old_y) {
934                                 gdk_window_move (priv->headers_window, new_x, new_y);
935                                 gdk_window_process_updates (priv->headers_window, TRUE);
936                         }
937                 }
938                 
939                 if (priv->html_scroll && GTK_WIDGET_VISIBLE (priv->html_scroll)) {
940                         gint old_x, old_y;
941                         gint new_x, new_y;
942                         gdk_window_get_position (priv->html_window, &old_x, &old_y);
943                         new_x = -hadj->value;
944                         new_y = headers_offset - vadj->value;
945
946                         if (new_x != old_x || new_y != old_y) {
947                                 gdk_window_move (priv->html_window, new_x, new_y);
948                                 gdk_window_process_updates (priv->html_window, TRUE);
949                         }
950                 }
951                 
952         }
953 }
954
955 static void
956 html_adjustment_changed (GtkAdjustment *adj,
957                          gpointer userdata)
958 {
959         ModestGtkhtmlMsgView *self = MODEST_GTKHTML_MSG_VIEW (userdata);
960         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
961         GtkAdjustment *html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(priv->html_scroll));
962         gboolean vadj_changed;
963         gint new_height;
964
965         g_signal_stop_emission_by_name (G_OBJECT (adj), "changed");
966
967         priv->html_scroll->requisition.height = html_vadj->upper;
968         if (html_vadj->upper == priv->html_scroll->allocation.height)
969                 return;
970         priv->html_scroll->allocation.height = html_vadj->upper;
971
972         set_vadjustment_values (self, &vadj_changed);
973
974         new_height = MAX ((gint) html_vadj->upper, (gint) (priv->vadj->upper - priv->headers_box->allocation.height));
975         
976         gtk_adjustment_changed (priv->vadj);
977         if (GTK_WIDGET_DRAWABLE (priv->html_scroll)) {
978                 gdk_window_resize (priv->html_window, (gint) priv->hadj->upper, (gint) new_height);
979                 gdk_window_process_updates (priv->view_window, TRUE);
980                 gtk_container_resize_children (GTK_CONTAINER (self));
981         }
982         
983 }
984
985 gboolean
986 idle_readjust_scroll (ModestGtkhtmlMsgView *self)
987 {
988
989         /* We're out the main lock */
990         gdk_threads_enter ();
991
992         if (GTK_WIDGET_DRAWABLE (self)) {
993                 ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
994                 GtkAdjustment *html_vadj;
995                 html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->html_scroll));
996                 html_vadj->page_size = html_vadj->upper;
997                 gtk_adjustment_changed (html_vadj);
998                 gtk_widget_queue_resize (GTK_WIDGET (self));
999                 gtk_widget_queue_draw (GTK_WIDGET (self));
1000
1001                 /* Just another hack for making readjust really work. This forces an update
1002                  * of the scroll, and then, make the scroll really update properly the
1003                  * the size and not corrupt scrollable area */
1004                 gtk_adjustment_set_value (priv->vadj, 1.0);
1005                 gtk_adjustment_set_value (priv->vadj, 0.0);
1006         }
1007         g_object_unref (G_OBJECT (self));
1008
1009         gdk_threads_leave ();
1010
1011         return FALSE;
1012 }
1013
1014 static void
1015 modest_gtkhtml_msg_view_init (ModestGtkhtmlMsgView *obj)
1016 {
1017         ModestGtkhtmlMsgViewPrivate *priv;
1018         GtkAdjustment *html_vadj;
1019         GtkWidget *separator;
1020
1021         GTK_WIDGET_UNSET_FLAGS (obj, GTK_NO_WINDOW);
1022         gtk_widget_set_redraw_on_allocate (GTK_WIDGET (obj), TRUE);
1023         gtk_container_set_reallocate_redraws (GTK_CONTAINER (obj), TRUE);
1024         gtk_container_set_resize_mode (GTK_CONTAINER (obj), GTK_RESIZE_QUEUE);
1025         
1026         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(obj);
1027
1028         priv->current_zoom = 1.0;
1029
1030         priv->hadj = NULL;
1031         priv->vadj = NULL;
1032         priv->shadow_type = GTK_SHADOW_IN;
1033         priv->view_window = NULL;
1034         priv->headers_window = NULL;
1035         priv->html_window = NULL;
1036
1037         gtk_widget_push_composite_child ();
1038         priv->html_scroll = gtk_scrolled_window_new (NULL, NULL);
1039         gtk_widget_set_composite_name (priv->html_scroll, "contents");
1040         gtk_widget_pop_composite_child ();
1041         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->html_scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
1042
1043         priv->msg                     = NULL;
1044
1045         priv->body_view                 = GTK_WIDGET (g_object_new (MODEST_TYPE_GTKHTML_MIME_PART_VIEW, NULL));
1046         priv->mail_header_view        = GTK_WIDGET(modest_mail_header_view_new (TRUE));
1047         priv->view_images_button = gtk_button_new_with_label (_("mail_bd_external_images"));
1048         gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
1049         gtk_widget_set_no_show_all (priv->view_images_button, TRUE);
1050         priv->attachments_view        = GTK_WIDGET(modest_attachments_view_new (NULL));
1051
1052         g_signal_connect (G_OBJECT(priv->body_view), "activate_link",
1053                                        G_CALLBACK(on_activate_link), obj);
1054         g_signal_connect (G_OBJECT(priv->body_view), "fetch_url",
1055                                        G_CALLBACK(on_fetch_url), obj);
1056         g_signal_connect (G_OBJECT(priv->body_view), "link_hover",
1057                                        G_CALLBACK(on_link_hover), obj);
1058 #ifdef MAEMO_CHANGES
1059         g_signal_connect (G_OBJECT(priv->body_view), "motion-notify-event",
1060                           G_CALLBACK (motion_notify_event), obj);
1061 #endif
1062
1063         g_signal_connect (G_OBJECT (priv->mail_header_view), "recpt-activated", 
1064                           G_CALLBACK (on_recpt_activated), obj);
1065
1066         g_signal_connect (G_OBJECT (priv->attachments_view), "activate",
1067                           G_CALLBACK (on_attachment_activated), obj);
1068
1069         g_signal_connect (G_OBJECT (priv->view_images_button), "clicked",
1070                           G_CALLBACK (on_view_images_clicked), obj);
1071
1072         html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(priv->html_scroll));
1073
1074         g_signal_connect (G_OBJECT (html_vadj), "changed",
1075                           G_CALLBACK (html_adjustment_changed), obj);
1076
1077         gtk_widget_push_composite_child ();
1078         priv->headers_box = gtk_vbox_new (FALSE, MODEST_MARGIN_DEFAULT);
1079         gtk_widget_set_composite_name (priv->headers_box, "headers");
1080         gtk_widget_pop_composite_child ();
1081
1082         if (priv->mail_header_view)
1083                 gtk_box_pack_start (GTK_BOX(priv->headers_box), priv->mail_header_view, FALSE, FALSE, 0);
1084         if (priv->view_images_button) {
1085                 GtkWidget *hbuttonbox;
1086                 
1087                 hbuttonbox = gtk_hbutton_box_new ();
1088                 gtk_container_add (GTK_CONTAINER (hbuttonbox), priv->view_images_button);
1089                 gtk_box_pack_start (GTK_BOX (priv->headers_box), hbuttonbox, FALSE, FALSE, 0);
1090                 gtk_widget_hide (priv->view_images_button);
1091         }
1092         
1093         if (priv->attachments_view) {
1094                 gchar *att_label = g_strconcat (_("mcen_me_viewer_attachments"), ":", NULL);
1095
1096                 priv->attachments_box = (GtkWidget *)
1097                         modest_mail_header_view_add_custom_header (MODEST_MAIL_HEADER_VIEW (priv->mail_header_view),
1098                                                                    att_label,
1099                                                                    priv->attachments_view,
1100                                                                    FALSE, FALSE);
1101                 gtk_widget_hide_all (priv->attachments_box);
1102                 g_free (att_label);
1103         }
1104
1105         separator = gtk_hseparator_new ();
1106         gtk_box_pack_start (GTK_BOX(priv->headers_box), separator, FALSE, FALSE, 0);
1107
1108         gtk_widget_set_parent (priv->headers_box, GTK_WIDGET (obj));
1109
1110         if (priv->body_view) {
1111                 gtk_container_add (GTK_CONTAINER (priv->html_scroll), priv->body_view);
1112                 gtk_widget_set_parent (priv->html_scroll, GTK_WIDGET(obj));
1113 #ifdef MAEMO_CHANGES
1114                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (priv->body_view), NULL, NULL, 0);
1115                 g_signal_connect (G_OBJECT (priv->body_view), "tap-and-hold", G_CALLBACK (on_tap_and_hold), obj);
1116 #endif
1117         }
1118         
1119 }
1120         
1121
1122 static void
1123 modest_gtkhtml_msg_view_finalize (GObject *obj)
1124 {       
1125         ModestGtkhtmlMsgViewPrivate *priv;
1126         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (obj);
1127
1128         if (priv->msg) {
1129                 g_object_unref (G_OBJECT(priv->msg));
1130                 priv->msg = NULL;
1131         }
1132         
1133         /* we cannot disconnect sigs, because priv->body_view is
1134          * already dead */
1135         
1136         disconnect_vadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1137         disconnect_hadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1138
1139         priv->body_view = NULL;
1140         priv->attachments_view = NULL;
1141
1142         G_OBJECT_CLASS(parent_class)->finalize (obj);           
1143 }
1144
1145 static void
1146 modest_gtkhtml_msg_view_destroy (GtkObject *obj)
1147 {       
1148         disconnect_vadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1149         disconnect_hadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1150
1151         GTK_OBJECT_CLASS(parent_class)->destroy (obj);          
1152 }
1153
1154 /* INTERNAL METHODS */
1155
1156 #ifdef MAEMO_CHANGES
1157 static gboolean 
1158 motion_notify_event (GtkWidget *widget,
1159                      GdkEventMotion *event,
1160                      gpointer userdata)
1161 {
1162         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (userdata);
1163
1164         /* Use panning information in gtkhtml widget to support also vertical panning */
1165
1166         if (GTK_HTML (widget)->panning) {
1167                 gint y, dy;
1168                 gdouble value;
1169
1170                 gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, NULL, &y, NULL);
1171                 dy = y - GTK_HTML (widget)->lasty;
1172                 value = priv->vadj->value - (gdouble) dy;
1173
1174                 if (value < priv->vadj->lower)
1175                         value = priv->vadj->lower;
1176                 else if (value > priv->vadj->upper - priv->vadj->page_size)
1177                         value = priv->vadj->upper - priv->vadj->page_size;
1178                 gtk_adjustment_set_value (priv->vadj, value);
1179                 
1180         }
1181         return FALSE;
1182 }
1183 #endif
1184
1185 static GtkAdjustment *
1186 get_vadjustment (ModestGtkhtmlMsgView *self)
1187 {
1188         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1189
1190         if (!priv->vadj)
1191                 set_vadjustment (self, NULL);
1192
1193         return priv->vadj;
1194         
1195 }
1196
1197 static GtkAdjustment *
1198 get_hadjustment (ModestGtkhtmlMsgView *self)
1199 {
1200         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1201
1202         if (!priv->hadj)
1203                 set_hadjustment (self, NULL);
1204
1205         return priv->hadj;
1206         
1207 }
1208
1209 static void
1210 set_hadjustment (ModestGtkhtmlMsgView *self, GtkAdjustment *hadj)
1211 {
1212         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1213         gboolean value_changed;
1214         
1215         if (hadj && hadj == priv->hadj)
1216                 return;
1217
1218         if (!hadj)
1219                 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,0.0,0.0,0.0,0.0,0.0));
1220         disconnect_hadjustment (self);
1221         g_object_ref (G_OBJECT (hadj));
1222         gtk_object_sink (GTK_OBJECT (hadj));
1223         priv->hadj = hadj;
1224         set_hadjustment_values (self, &value_changed);
1225
1226         g_signal_connect (hadj, "value_changed", G_CALLBACK (adjustment_value_changed),
1227                           self);
1228
1229         gtk_adjustment_changed (hadj);
1230         if (value_changed)
1231                 gtk_adjustment_value_changed (hadj);
1232         else
1233                 adjustment_value_changed (hadj, self);
1234
1235         g_object_notify (G_OBJECT (self), "hadjustment");
1236 }
1237
1238 static void
1239 set_vadjustment (ModestGtkhtmlMsgView *self, GtkAdjustment *vadj)
1240 {
1241         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1242         gboolean value_changed;
1243
1244         if (vadj && vadj == priv->vadj)
1245                 return;
1246
1247         if (!vadj)
1248                 vadj = (GtkAdjustment *) gtk_adjustment_new (0.0,0.0,0.0,0.0,0.0,0.0);
1249         disconnect_vadjustment (self);
1250         g_object_ref (G_OBJECT (vadj));
1251         gtk_object_sink (GTK_OBJECT (vadj));
1252         priv->vadj = vadj;
1253         set_vadjustment_values (self, &value_changed);
1254
1255         g_signal_connect (vadj, "value_changed", G_CALLBACK (adjustment_value_changed),
1256                           self);
1257
1258         gtk_adjustment_changed (vadj);
1259         if (value_changed)
1260                 gtk_adjustment_value_changed (vadj);
1261         else
1262                 adjustment_value_changed (vadj, self);
1263
1264         g_object_notify (G_OBJECT (self), "vadjustment");
1265 }
1266
1267 static void
1268 set_shadow_type (ModestGtkhtmlMsgView *self,
1269                  GtkShadowType shadow_type)
1270 {
1271         ModestGtkhtmlMsgViewPrivate *priv;
1272         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1273
1274         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1275         
1276         if (priv->shadow_type != shadow_type) {
1277                 priv->shadow_type = shadow_type;
1278                 
1279                 if (GTK_WIDGET_VISIBLE (self)) {
1280                         gtk_widget_size_allocate (GTK_WIDGET (self), &(GTK_WIDGET (self)->allocation));
1281                         gtk_widget_queue_draw (GTK_WIDGET (self));
1282                 }
1283                 g_object_notify (G_OBJECT (self), "shadow-type");
1284         }
1285 }
1286
1287 static GtkShadowType
1288 get_shadow_type (ModestGtkhtmlMsgView *self)
1289 {
1290         ModestGtkhtmlMsgViewPrivate *priv;
1291         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), GTK_SHADOW_NONE);
1292         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1293         
1294         return priv->shadow_type;
1295 }
1296
1297 GtkWidget*
1298 modest_msg_view_new (TnyMsg *msg)
1299 {
1300         GObject *obj;
1301         ModestGtkhtmlMsgView* self;
1302         
1303         obj  = G_OBJECT(g_object_new(MODEST_TYPE_GTKHTML_MSG_VIEW, NULL));
1304         self = MODEST_GTKHTML_MSG_VIEW(obj);
1305         tny_msg_view_set_msg (TNY_MSG_VIEW (self), msg);
1306
1307         return GTK_WIDGET(self);
1308 }
1309
1310 #ifdef MAEMO_CHANGES
1311 static void
1312 on_tap_and_hold (GtkWidget *widget,
1313                  gpointer data)
1314 {
1315         ModestGtkhtmlMsgView *self = (ModestGtkhtmlMsgView *) data;
1316         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1317
1318         g_signal_emit_by_name (G_OBJECT (self), "link-contextual", priv->last_url);
1319 }
1320 #endif
1321
1322 static void
1323 on_recpt_activated (ModestMailHeaderView *header_view, 
1324                     const gchar *address,
1325                     ModestGtkhtmlMsgView *self)
1326 {
1327         g_signal_emit_by_name (G_OBJECT (self), "recpt-activated", address);
1328 }
1329
1330 static void
1331 on_attachment_activated (ModestAttachmentsView * att_view, TnyMimePart *mime_part, gpointer self)
1332 {
1333
1334         g_signal_emit_by_name (G_OBJECT(self), "attachment_clicked", mime_part);
1335 }
1336
1337 static void
1338 on_view_images_clicked (GtkButton * button, gpointer self)
1339 {
1340         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1341         TnyMimePart *part;
1342
1343         modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), TRUE);
1344         gtk_widget_hide (priv->view_images_button);
1345         part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (priv->body_view));
1346         tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (priv->body_view), part);
1347         tny_msg_set_allow_external_images (TNY_MSG (priv->msg), TRUE);
1348         g_object_unref (part);
1349         
1350
1351 }
1352
1353 static gboolean
1354 on_activate_link (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *self)
1355 {
1356         gboolean result;
1357         g_return_val_if_fail (self, FALSE);
1358         
1359         g_signal_emit_by_name (G_OBJECT(self), "activate-link", uri, &result);
1360
1361         return result;
1362 }
1363
1364
1365 static gboolean
1366 on_link_hover (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *self)
1367 {
1368         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1369         gboolean result;
1370
1371         g_free (priv->last_url);
1372         priv->last_url = g_strdup (uri);
1373
1374         g_signal_emit_by_name (G_OBJECT(self), "link-hover", uri, &result);
1375
1376         return result;
1377 }
1378
1379
1380
1381 static TnyMimePart *
1382 find_cid_image (TnyMsg *msg, const gchar *cid)
1383 {
1384         TnyMimePart *part = NULL;
1385         TnyList *parts;
1386         TnyIterator *iter;
1387         
1388         g_return_val_if_fail (msg, NULL);
1389         g_return_val_if_fail (cid, NULL);
1390         
1391         parts  = TNY_LIST (tny_simple_list_new());
1392
1393         tny_mime_part_get_parts (TNY_MIME_PART (msg), parts); 
1394         iter   = tny_list_create_iterator (parts);
1395         
1396         while (!tny_iterator_is_done(iter)) {
1397                 const gchar *part_cid;
1398
1399                 part = TNY_MIME_PART(tny_iterator_get_current(iter));
1400                 part_cid = tny_mime_part_get_content_id (part);
1401
1402                 /* if there is no content id, try the content location;
1403                  * this is what Outlook seems to use when it converts
1404                  * it's internal richtext to html
1405                  */
1406                 if (!part_cid)
1407                         part_cid = tny_mime_part_get_content_location (part);
1408                 
1409                 if (part_cid && strcmp (cid, part_cid) == 0)
1410                         break;
1411
1412                 if (tny_mime_part_content_type_is (part, "multipart/related")) {
1413                         TnyList *related_parts = TNY_LIST (tny_simple_list_new ());
1414                         TnyIterator *related_iter = NULL;
1415                         TnyMimePart *related_part = NULL;
1416
1417                         tny_mime_part_get_parts (part, related_parts);
1418                         related_iter = tny_list_create_iterator (related_parts);
1419
1420                         while (!tny_iterator_is_done (related_iter)) {
1421                                 related_part = TNY_MIME_PART (tny_iterator_get_current (related_iter));
1422                                 part_cid = tny_mime_part_get_content_id (related_part);
1423                                 if (part_cid && strcmp (cid, part_cid) == 0) {
1424                                         break;
1425                                 }
1426                                 g_object_unref (related_part);
1427                                 related_part = NULL;
1428                                 tny_iterator_next (related_iter);
1429                         }
1430
1431                         g_object_unref (related_iter);
1432                         g_object_unref (related_parts);
1433                         if (related_part != NULL) {
1434                                 g_object_unref (part);
1435                                 part = related_part;
1436                                 break;
1437                         }
1438                 }
1439
1440                 g_object_unref (G_OBJECT(part));
1441         
1442                 part = NULL;
1443                 tny_iterator_next (iter);
1444         }
1445         
1446         g_object_unref (G_OBJECT(iter));        
1447         g_object_unref (G_OBJECT(parts));
1448         
1449         return part;
1450 }
1451
1452
1453 static gboolean
1454 on_fetch_url (GtkWidget *widget, const gchar *uri,
1455               TnyStream *stream, ModestGtkhtmlMsgView *self)
1456 {
1457         ModestGtkhtmlMsgViewPrivate *priv;
1458         const gchar* my_uri;
1459         TnyMimePart *part = NULL;
1460         
1461
1462
1463         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1464
1465         if (modest_mime_part_view_has_external_images (MODEST_MIME_PART_VIEW (priv->body_view)))
1466                 gtk_widget_show (priv->view_images_button);
1467         /*
1468          * we search for either something starting with cid:, or something
1469          * with no prefix at all; this latter case occurs when sending mails
1470          * with MS Outlook in rich-text mode, and 'attach-as-object
1471          */
1472         if (g_str_has_prefix (uri, "cid:"))  
1473                 my_uri = uri + 4;  /* +4 ==> skip "cid:" */
1474         else
1475                 my_uri = uri;
1476         
1477         /* now try to find the embedded image */
1478         part = find_cid_image (priv->msg, my_uri);
1479
1480         if (!part) {
1481                 if (g_str_has_prefix (uri, "http:")) {
1482                         if (modest_mime_part_view_get_view_images (MODEST_MIME_PART_VIEW (priv->body_view))) {
1483                                 gboolean result = FALSE;
1484                                 g_signal_emit_by_name (self, "fetch-image", uri, stream, &result);
1485                                 return result;
1486                         } else {
1487                                 /* we return immediately to get a "image not found" icon */
1488                                 tny_stream_close (stream);
1489                                 return TRUE;
1490                         }
1491                 } else {
1492                         return FALSE;
1493                 }
1494         }
1495
1496         tny_mime_part_decode_to_stream ((TnyMimePart*)part, stream, NULL);
1497         tny_stream_close (stream);
1498         g_object_unref (G_OBJECT(part));
1499         return TRUE;
1500 }
1501
1502 static void
1503 set_message (ModestGtkhtmlMsgView *self, TnyMsg *msg)
1504 {
1505         TnyMimePart *body;
1506         ModestGtkhtmlMsgViewPrivate *priv;
1507         TnyHeader *header;
1508         GtkAdjustment *html_vadj;
1509         
1510         g_return_if_fail (self);
1511         
1512         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(self);
1513         gtk_widget_set_no_show_all (priv->mail_header_view, FALSE);
1514         modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), FALSE);
1515
1516         html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->html_scroll));
1517         html_vadj->upper = 0;
1518         html_vadj->page_size = 0;
1519         g_signal_emit_by_name (G_OBJECT (html_vadj), "changed");
1520
1521
1522         if (msg != priv->msg) {
1523                 if (priv->msg)
1524                         g_object_unref (G_OBJECT(priv->msg));
1525                 if (msg)
1526                         g_object_ref   (G_OBJECT(msg));
1527                 priv->msg = msg;
1528         }
1529         
1530         if (!msg) {
1531                 tny_header_view_clear (TNY_HEADER_VIEW (priv->mail_header_view));
1532                 modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), NULL);
1533                 gtk_widget_hide_all (priv->mail_header_view);
1534                 gtk_widget_hide_all (priv->attachments_box);
1535                 gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
1536                 tny_mime_part_view_clear (TNY_MIME_PART_VIEW (priv->body_view));
1537                 gtk_widget_queue_resize (GTK_WIDGET(self));
1538                 gtk_widget_queue_draw (GTK_WIDGET(self));
1539                 return;
1540         }
1541
1542         header = tny_msg_get_header (msg);
1543         tny_header_view_set_header (TNY_HEADER_VIEW (priv->mail_header_view), header);
1544         g_object_unref (header);
1545
1546         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW(priv->attachments_view),
1547                                              msg);
1548
1549         modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), tny_msg_get_allow_external_images (msg));
1550         
1551         body = modest_tny_msg_find_body_part (msg, TRUE);
1552         if (body) {
1553                 tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (priv->body_view), body);
1554
1555                 if(modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) {
1556                         gtk_widget_show_all (priv->attachments_box);
1557                 } else {
1558                         gtk_widget_hide_all (priv->attachments_box);
1559                 }
1560
1561         } else 
1562                 tny_mime_part_view_clear (TNY_MIME_PART_VIEW (priv->body_view));
1563
1564         if (modest_mime_part_view_has_external_images (MODEST_MIME_PART_VIEW (priv->body_view)) &&
1565             !modest_mime_part_view_get_view_images (MODEST_MIME_PART_VIEW (priv->body_view))) {
1566                 gtk_widget_show (priv->view_images_button);
1567         } else {
1568                 gtk_widget_hide (priv->view_images_button);
1569         }
1570
1571         gtk_widget_show (priv->body_view);
1572         gtk_widget_set_no_show_all (priv->attachments_box, TRUE);
1573         gtk_widget_show_all (priv->mail_header_view);
1574         gtk_widget_set_no_show_all (priv->attachments_box, FALSE);
1575         gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
1576         gtk_widget_queue_resize (GTK_WIDGET(self));
1577         gtk_widget_queue_draw (GTK_WIDGET(self));
1578
1579         if (priv->hadj != NULL)
1580                 priv->hadj->value = 0.0;
1581         if (priv->vadj != NULL)
1582                 priv->vadj->value = 0.0;
1583
1584         g_signal_emit_by_name (G_OBJECT (html_vadj), "changed");
1585
1586         /* This is a hack to force reallocation of scroll after drawing all the stuff. This
1587          * makes the html view get the proper and expected size and prevent being able to scroll
1588          * the buffer when it shouldn't be scrollable */
1589         g_object_ref (self);
1590         g_timeout_add (250, (GSourceFunc) idle_readjust_scroll, self);
1591 }
1592
1593
1594 static TnyMsg*
1595 get_message (ModestGtkhtmlMsgView *self)
1596 {
1597         TnyMsg *msg;
1598
1599         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), NULL);
1600
1601         msg = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(self)->msg;
1602
1603         if (msg)
1604                 g_object_ref (msg);
1605         
1606         return msg;
1607 }
1608
1609 static gboolean 
1610 is_empty (ModestGtkhtmlMsgView *self)
1611 {
1612         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1613
1614         return modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->body_view));
1615 }
1616
1617 static void
1618 set_zoom (ModestGtkhtmlMsgView *self, gdouble zoom)
1619 {
1620         ModestGtkhtmlMsgViewPrivate *priv;
1621
1622         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1623         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1624
1625         modest_zoomable_set_zoom (MODEST_ZOOMABLE(priv->body_view), zoom);
1626
1627         gtk_widget_queue_resize (priv->body_view);
1628 }
1629
1630 static gdouble
1631 get_zoom (ModestGtkhtmlMsgView *self)
1632 {
1633         ModestGtkhtmlMsgViewPrivate *priv;
1634
1635         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), 1.0);
1636         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1637
1638         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->body_view));
1639 }
1640
1641 static TnyHeaderFlags
1642 get_priority (ModestGtkhtmlMsgView *self)
1643 {
1644         ModestGtkhtmlMsgViewPrivate *priv;
1645
1646         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), 0);
1647
1648         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1649
1650         return modest_mail_header_view_get_priority (MODEST_MAIL_HEADER_VIEW (priv->mail_header_view));
1651 }
1652
1653 static void
1654 set_priority (ModestGtkhtmlMsgView *self, TnyHeaderFlags flags)
1655 {
1656         ModestGtkhtmlMsgViewPrivate *priv;
1657
1658         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1659         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1660
1661         modest_mail_header_view_set_priority (MODEST_MAIL_HEADER_VIEW (priv->mail_header_view), flags);
1662 }
1663
1664 /* INCREMENTAL SEARCH IMPLEMENTATION */
1665
1666 static gboolean 
1667 search (ModestGtkhtmlMsgView *self, const gchar *search)
1668 {
1669         ModestGtkhtmlMsgViewPrivate *priv;
1670         gboolean result;
1671         GtkAdjustment *vadj, *tmp_vadj;
1672         gdouble y_offset;
1673
1674         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), FALSE);
1675
1676         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1677         vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (priv->body_view));
1678         g_object_ref (vadj);
1679         tmp_vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, vadj->lower, vadj->upper, vadj->step_increment, 32.0, 32.0));
1680         gtk_layout_set_vadjustment (GTK_LAYOUT (priv->body_view), tmp_vadj);
1681         result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->body_view),
1682                                              search);
1683
1684         if (result) {
1685                 gint x, y, w, h;
1686                 gdouble offset_top, offset_bottom;
1687                 GtkAdjustment *adj;
1688                 if (modest_isearch_view_get_selection_area (MODEST_ISEARCH_VIEW (priv->body_view), &x, &y, &w, &h)) {
1689                         offset_top = (gdouble) (priv->headers_box->requisition.height + y);
1690                         offset_bottom = (gdouble) (priv->headers_box->requisition.height + y + h);
1691                         adj = GTK_ADJUSTMENT (priv->vadj);
1692                         if (offset_top < adj->value)
1693                                 gtk_adjustment_set_value (adj, offset_top + adj->page_increment - adj->page_size);
1694                         else if (offset_bottom > adj->value + adj->page_increment)
1695                                 gtk_adjustment_set_value (adj, offset_bottom - adj->page_increment);
1696                 }
1697         }
1698
1699         y_offset = tmp_vadj->value;
1700         gtk_layout_set_vadjustment (GTK_LAYOUT (priv->body_view), vadj);
1701         g_object_unref (vadj);
1702
1703         return result;
1704 }
1705
1706 static gboolean
1707 search_next (ModestGtkhtmlMsgView *self)
1708 {
1709         ModestGtkhtmlMsgViewPrivate *priv;
1710         gboolean result;
1711
1712         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), FALSE);
1713
1714         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1715         result = modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->body_view));
1716
1717         if (result) {
1718                 gint x, y, w, h;
1719                 gdouble offset_top, offset_bottom;
1720                 GtkAdjustment *adj;
1721
1722                 if (modest_isearch_view_get_selection_area (MODEST_ISEARCH_VIEW (priv->body_view), &x, &y, &w, &h)) {
1723                         offset_top = (gdouble) (priv->headers_box->requisition.height + y);
1724                         offset_bottom = (gdouble) (priv->headers_box->requisition.height + y + h);
1725                         adj = GTK_ADJUSTMENT (priv->vadj);
1726                         if (offset_top < adj->value)
1727                                 gtk_adjustment_set_value (adj, offset_top + adj->page_increment - adj->page_size);
1728                         else if (offset_bottom > adj->value + adj->page_increment)
1729                                 gtk_adjustment_set_value (adj, offset_bottom - adj->page_increment);
1730                 }
1731         }
1732         return result;
1733 }
1734
1735 static TnyList *
1736 get_selected_attachments (ModestGtkhtmlMsgView *self)
1737 {
1738         ModestGtkhtmlMsgViewPrivate *priv;
1739
1740         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), NULL);
1741         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1742
1743         return modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1744         
1745 }
1746
1747 static TnyList *
1748 get_attachments (ModestGtkhtmlMsgView *self)
1749 {
1750         ModestGtkhtmlMsgViewPrivate *priv;
1751
1752         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), NULL);
1753         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1754
1755         return modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1756         
1757 }
1758
1759 static void
1760 grab_focus (ModestGtkhtmlMsgView *self)
1761 {
1762         ModestGtkhtmlMsgViewPrivate *priv = NULL;
1763
1764         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1765         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1766
1767         gtk_widget_grab_focus (priv->body_view);
1768 }
1769
1770 static void
1771 remove_attachment (ModestGtkhtmlMsgView *self, TnyMimePart *attachment)
1772 {
1773         ModestGtkhtmlMsgViewPrivate *priv;
1774
1775         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1776         g_return_if_fail (TNY_IS_MIME_PART (attachment));
1777         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1778
1779         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
1780                                                    attachment);
1781         
1782 }
1783
1784 /* TNY MSG IMPLEMENTATION */
1785
1786 static void
1787 tny_msg_view_init (gpointer g, gpointer iface_data)
1788 {
1789         TnyMsgViewIface *klass = (TnyMsgViewIface *)g;
1790
1791         klass->get_msg = modest_msg_view_get_msg;
1792         klass->set_msg = modest_msg_view_set_msg;
1793         klass->set_unavailable = modest_msg_view_set_unavailable;
1794         klass->clear = modest_msg_view_clear;
1795         klass->create_mime_part_view_for = modest_msg_view_create_mime_part_view_for;
1796         klass->create_new_inline_viewer = modest_msg_view_create_new_inline_viewer;
1797
1798         return;
1799 }
1800
1801 static TnyMsg *
1802 modest_msg_view_get_msg (TnyMsgView *self)
1803 {
1804         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_msg_func (self);
1805 }
1806
1807 static TnyMsg *
1808 modest_msg_view_get_msg_default (TnyMsgView *self)
1809 {
1810         return TNY_MSG (tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (self)));
1811 }
1812
1813 static void
1814 modest_msg_view_set_msg (TnyMsgView *self, TnyMsg *msg)
1815 {
1816         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_msg_func (self, msg);
1817 }
1818
1819 static void 
1820 modest_msg_view_set_msg_default (TnyMsgView *self, TnyMsg *msg)
1821 {
1822
1823         tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (self), TNY_MIME_PART (msg));
1824
1825         return;
1826 }
1827
1828 static void
1829 modest_msg_view_set_unavailable (TnyMsgView *self)
1830 {
1831         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_unavailable_func (self);
1832 }
1833
1834 static void
1835 modest_msg_view_set_unavailable_default (TnyMsgView *self)
1836 {
1837         tny_msg_view_clear (self);
1838
1839         return;
1840 }
1841
1842 static void
1843 modest_msg_view_clear (TnyMsgView *self)
1844 {
1845         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->clear_func (self);
1846 }
1847
1848 static void
1849 modest_msg_view_clear_default (TnyMsgView *self)
1850 {
1851         set_message (MODEST_GTKHTML_MSG_VIEW (self), NULL);
1852 }
1853
1854 static TnyMimePartView*
1855 modest_msg_view_create_mime_part_view_for (TnyMsgView *self, TnyMimePart *part)
1856 {
1857         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->create_mime_part_view_for_func (self, part);
1858 }
1859
1860 static TnyMimePartView*
1861 modest_msg_view_create_mime_part_view_for_default (TnyMsgView *self, TnyMimePart *part)
1862 {
1863         g_warning ("modest_msg_view_create_mime_part_view_for_default is not implemented");
1864         return NULL;
1865 }
1866
1867 static TnyMsgView*
1868 modest_msg_view_create_new_inline_viewer (TnyMsgView *self)
1869 {
1870         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->create_new_inline_viewer_func (self);
1871 }
1872
1873 static TnyMsgView*
1874 modest_msg_view_create_new_inline_viewer_default (TnyMsgView *self)
1875 {
1876         g_warning ("modest_msg_view_create_new_inline_viewer_default is not implemented");
1877
1878         return NULL;
1879 }
1880
1881 /* TNY MIME PART IMPLEMENTATION */
1882
1883 static void
1884 tny_mime_part_view_init (gpointer g, gpointer iface_data)
1885 {
1886         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
1887
1888         klass->get_part = modest_msg_view_mp_get_part;
1889         klass->set_part = modest_msg_view_mp_set_part;
1890         klass->clear = modest_msg_view_mp_clear;
1891
1892         return;
1893 }
1894
1895 static TnyMimePart* 
1896 modest_msg_view_mp_get_part (TnyMimePartView *self)
1897 {
1898         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_part_func (self);
1899 }
1900
1901
1902 static TnyMimePart* 
1903 modest_msg_view_mp_get_part_default (TnyMimePartView *self)
1904 {
1905         return TNY_MIME_PART (get_message (MODEST_GTKHTML_MSG_VIEW (self)));
1906 }
1907
1908 static void
1909 modest_msg_view_mp_set_part (TnyMimePartView *self,
1910                              TnyMimePart *part)
1911 {
1912         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_part_func (self, part);
1913 }
1914
1915 static void
1916 modest_msg_view_mp_set_part_default (TnyMimePartView *self,
1917                                      TnyMimePart *part)
1918 {
1919         g_return_if_fail ((part == NULL) || TNY_IS_MSG (part));
1920
1921         set_message (MODEST_GTKHTML_MSG_VIEW (self), TNY_MSG (part));
1922 }
1923
1924 static void
1925 modest_msg_view_mp_clear (TnyMimePartView *self)
1926 {
1927         tny_msg_view_clear (TNY_MSG_VIEW (self));
1928 }
1929
1930 /* MODEST MIME PART VIEW IMPLEMENTATION */
1931
1932 static void
1933 modest_mime_part_view_init (gpointer g, gpointer iface_data)
1934 {
1935         ModestMimePartViewIface *klass = (ModestMimePartViewIface *)g;
1936
1937         klass->is_empty_func = modest_msg_view_mp_is_empty;
1938
1939         return;
1940 }
1941
1942 static gboolean
1943 modest_msg_view_mp_is_empty (ModestMimePartView *self)
1944 {
1945         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->is_empty_func (self);
1946 }
1947
1948 static gboolean
1949 modest_msg_view_mp_is_empty_default (ModestMimePartView *self)
1950 {
1951         return is_empty (MODEST_GTKHTML_MSG_VIEW (self));
1952 }
1953
1954 /* MODEST ZOOMABLE IMPLEMENTATION */
1955 static void
1956 modest_zoomable_init (gpointer g, gpointer iface_data)
1957 {
1958         ModestZoomableIface *klass = (ModestZoomableIface *)g;
1959
1960         klass->get_zoom_func = modest_msg_view_get_zoom;
1961         klass->set_zoom_func = modest_msg_view_set_zoom;
1962         klass->zoom_minus_func = modest_msg_view_zoom_minus;
1963         klass->zoom_plus_func = modest_msg_view_zoom_plus;
1964
1965         return;
1966 }
1967
1968 static gdouble
1969 modest_msg_view_get_zoom (ModestZoomable *self)
1970 {
1971         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_zoom_func (self);
1972 }
1973
1974 static gdouble
1975 modest_msg_view_get_zoom_default (ModestZoomable *self)
1976 {
1977         return get_zoom (MODEST_GTKHTML_MSG_VIEW (self));
1978 }
1979
1980 static void
1981 modest_msg_view_set_zoom (ModestZoomable *self, gdouble value)
1982 {
1983         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_zoom_func (self, value);
1984 }
1985
1986 static void
1987 modest_msg_view_set_zoom_default (ModestZoomable *self, gdouble value)
1988 {
1989         set_zoom (MODEST_GTKHTML_MSG_VIEW (self), value);
1990 }
1991
1992 static gboolean
1993 modest_msg_view_zoom_minus (ModestZoomable *self)
1994 {
1995         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->zoom_minus_func (self);
1996 }
1997
1998 static gboolean
1999 modest_msg_view_zoom_minus_default (ModestZoomable *self)
2000 {
2001         /* operation not supported in ModestMsgView */
2002         return FALSE;
2003 }
2004
2005 static gboolean
2006 modest_msg_view_zoom_plus (ModestZoomable *self)
2007 {
2008         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->zoom_plus_func (self);
2009 }
2010
2011 static gboolean
2012 modest_msg_view_zoom_plus_default (ModestZoomable *self)
2013 {
2014         /* operation not supported in ModestMsgView */
2015         return FALSE;
2016 }
2017
2018 /* MODEST ISEARCH VIEW IMPLEMENTATION */
2019 static void
2020 modest_isearch_view_init (gpointer g, gpointer iface_data)
2021 {
2022         ModestISearchViewIface *klass = (ModestISearchViewIface *)g;
2023
2024         klass->search_func = modest_msg_view_search;
2025         klass->search_next_func = modest_msg_view_search_next;
2026
2027         return;
2028 }
2029
2030 static gboolean
2031 modest_msg_view_search (ModestISearchView *self, const gchar *string)
2032 {
2033         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->search_func (self, string);
2034 }
2035
2036 static gboolean
2037 modest_msg_view_search_default (ModestISearchView *self, const gchar *string)
2038 {
2039         return search (MODEST_GTKHTML_MSG_VIEW (self), string);
2040 }
2041
2042 static gboolean
2043 modest_msg_view_search_next (ModestISearchView *self)
2044 {
2045         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->search_next_func (self);
2046 }
2047
2048 static gboolean
2049 modest_msg_view_search_next_default (ModestISearchView *self)
2050 {
2051         return search_next (MODEST_GTKHTML_MSG_VIEW (self));
2052 }
2053
2054 /* MODEST MSG VIEW IMPLEMENTATION */
2055 static void
2056 modest_msg_view_init (gpointer g, gpointer iface_data)
2057 {
2058         ModestMsgViewIface *klass = (ModestMsgViewIface *)g;
2059
2060         klass->get_vadjustment_func = modest_gtkhtml_msg_view_get_vadjustment;
2061         klass->get_hadjustment_func = modest_gtkhtml_msg_view_get_hadjustment;
2062         klass->set_vadjustment_func = modest_gtkhtml_msg_view_set_vadjustment;
2063         klass->set_hadjustment_func = modest_gtkhtml_msg_view_set_hadjustment;
2064         klass->set_shadow_type_func = modest_gtkhtml_msg_view_set_shadow_type;
2065         klass->get_shadow_type_func = modest_gtkhtml_msg_view_get_shadow_type;
2066         klass->get_priority_func = modest_gtkhtml_msg_view_get_priority;
2067         klass->set_priority_func = modest_gtkhtml_msg_view_set_priority;
2068         klass->get_selected_attachments_func = modest_gtkhtml_msg_view_get_selected_attachments;
2069         klass->get_attachments_func = modest_gtkhtml_msg_view_get_attachments;
2070         klass->grab_focus_func = modest_gtkhtml_msg_view_grab_focus;
2071         klass->remove_attachment_func = modest_gtkhtml_msg_view_remove_attachment;
2072
2073         return;
2074 }
2075
2076 static GtkAdjustment*
2077 modest_gtkhtml_msg_view_get_vadjustment (ModestMsgView *self)
2078 {
2079         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_vadjustment_func (self);
2080 }
2081
2082 static GtkAdjustment*
2083 modest_gtkhtml_msg_view_get_vadjustment_default (ModestMsgView *self)
2084 {
2085         return get_vadjustment (MODEST_GTKHTML_MSG_VIEW (self));
2086 }
2087
2088 static GtkAdjustment*
2089 modest_gtkhtml_msg_view_get_hadjustment (ModestMsgView *self)
2090 {
2091         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_hadjustment_func (self);
2092 }
2093
2094 static GtkAdjustment*
2095 modest_gtkhtml_msg_view_get_hadjustment_default (ModestMsgView *self)
2096 {
2097         return get_hadjustment (MODEST_GTKHTML_MSG_VIEW (self));
2098 }
2099
2100 static void
2101 modest_gtkhtml_msg_view_set_vadjustment (ModestMsgView *self, GtkAdjustment *adj)
2102 {
2103         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_vadjustment_func (self, adj);
2104 }
2105
2106 static void
2107 modest_gtkhtml_msg_view_set_vadjustment_default (ModestMsgView *self, GtkAdjustment *adj)
2108 {
2109         set_vadjustment (MODEST_GTKHTML_MSG_VIEW (self), adj);
2110 }
2111
2112 static void
2113 modest_gtkhtml_msg_view_set_hadjustment (ModestMsgView *self, GtkAdjustment *adj)
2114 {
2115         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_hadjustment_func (self, adj);
2116 }
2117
2118 static void
2119 modest_gtkhtml_msg_view_set_hadjustment_default (ModestMsgView *self, GtkAdjustment *adj)
2120 {
2121         set_hadjustment (MODEST_GTKHTML_MSG_VIEW (self), adj);
2122 }
2123
2124 static void
2125 modest_gtkhtml_msg_view_set_shadow_type (ModestMsgView *self, GtkShadowType type)
2126 {
2127         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_shadow_type_func (self, type);
2128 }
2129
2130 static void
2131 modest_gtkhtml_msg_view_set_shadow_type_default (ModestMsgView *self, GtkShadowType type)
2132 {
2133         set_shadow_type (MODEST_GTKHTML_MSG_VIEW (self), type);
2134 }
2135
2136 static GtkShadowType
2137 modest_gtkhtml_msg_view_get_shadow_type (ModestMsgView *self)
2138 {
2139         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_shadow_type_func (self);
2140 }
2141
2142 static GtkShadowType
2143 modest_gtkhtml_msg_view_get_shadow_type_default (ModestMsgView *self)
2144 {
2145         return get_shadow_type (MODEST_GTKHTML_MSG_VIEW (self));
2146 }
2147
2148 static void
2149 modest_gtkhtml_msg_view_set_priority (ModestMsgView *self, TnyHeaderFlags flags)
2150 {
2151         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_priority_func (self, flags);
2152 }
2153
2154 static void
2155 modest_gtkhtml_msg_view_set_priority_default (ModestMsgView *self, TnyHeaderFlags flags)
2156 {
2157         set_priority (MODEST_GTKHTML_MSG_VIEW (self), flags);
2158 }
2159
2160 static TnyHeaderFlags
2161 modest_gtkhtml_msg_view_get_priority (ModestMsgView *self)
2162 {
2163         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_priority_func (self);
2164 }
2165
2166 static TnyHeaderFlags
2167 modest_gtkhtml_msg_view_get_priority_default (ModestMsgView *self)
2168 {
2169         return get_priority (MODEST_GTKHTML_MSG_VIEW (self));
2170 }
2171
2172 static TnyList*
2173 modest_gtkhtml_msg_view_get_selected_attachments (ModestMsgView *self)
2174 {
2175         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_selected_attachments_func (self);
2176 }
2177
2178 static TnyList*
2179 modest_gtkhtml_msg_view_get_selected_attachments_default (ModestMsgView *self)
2180 {
2181         return get_selected_attachments (MODEST_GTKHTML_MSG_VIEW (self));
2182 }
2183
2184 static TnyList*
2185 modest_gtkhtml_msg_view_get_attachments (ModestMsgView *self)
2186 {
2187         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_attachments_func (self);
2188 }
2189
2190 static TnyList*
2191 modest_gtkhtml_msg_view_get_attachments_default (ModestMsgView *self)
2192 {
2193         return get_attachments (MODEST_GTKHTML_MSG_VIEW (self));
2194 }
2195
2196 static void
2197 modest_gtkhtml_msg_view_grab_focus (ModestMsgView *self)
2198 {
2199         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->grab_focus_func (self);
2200 }
2201
2202 static void
2203 modest_gtkhtml_msg_view_grab_focus_default (ModestMsgView *self)
2204 {
2205         grab_focus (MODEST_GTKHTML_MSG_VIEW (self));
2206 }
2207
2208 static void
2209 modest_gtkhtml_msg_view_remove_attachment (ModestMsgView *self, TnyMimePart *attachment)
2210 {
2211         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->remove_attachment_func (self, attachment);
2212 }
2213
2214 static void
2215 modest_gtkhtml_msg_view_remove_attachment_default (ModestMsgView *self, TnyMimePart *attachment)
2216 {
2217         remove_attachment (MODEST_GTKHTML_MSG_VIEW (self), attachment);
2218 }