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