Added dimming rules for folder window
[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                 gchar *att_label = g_strconcat (_("mcen_me_viewer_attachments"), ":", NULL);
1135
1136                 priv->attachments_box = (GtkWidget *)
1137                         modest_mail_header_view_add_custom_header (MODEST_MAIL_HEADER_VIEW (priv->mail_header_view),
1138                                                                    att_label,
1139                                                                    priv->attachments_view,
1140                                                                    FALSE, FALSE);
1141                 gtk_widget_hide_all (priv->attachments_box);
1142                 g_free (att_label);
1143         }
1144
1145         separator = gtk_hseparator_new ();
1146         gtk_box_pack_start (GTK_BOX(priv->headers_box), separator, FALSE, FALSE, 0);
1147
1148         gtk_widget_set_parent (priv->headers_box, GTK_WIDGET (obj));
1149
1150         if (priv->body_view) {
1151                 gtk_container_add (GTK_CONTAINER (priv->html_scroll), priv->body_view);
1152                 gtk_widget_set_parent (priv->html_scroll, GTK_WIDGET(obj));
1153 #ifdef MAEMO_CHANGES
1154                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (priv->body_view), NULL, NULL, 0);
1155                 g_signal_connect (G_OBJECT (priv->body_view), "tap-and-hold", G_CALLBACK (on_tap_and_hold), obj);
1156                 g_signal_connect (G_OBJECT (priv->body_view), "tap-and-hold-query", G_CALLBACK (on_tap_and_hold_query), obj);
1157 #endif
1158         }
1159         
1160 }
1161         
1162
1163 static void
1164 modest_gtkhtml_msg_view_finalize (GObject *obj)
1165 {       
1166         ModestGtkhtmlMsgViewPrivate *priv;
1167         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (obj);
1168
1169         if (priv->msg) {
1170                 g_object_unref (G_OBJECT(priv->msg));
1171                 priv->msg = NULL;
1172         }
1173
1174         if (priv->idle_motion_id > 0) {
1175                 g_source_remove (priv->idle_motion_id);
1176                 priv->idle_motion_id = 0;
1177         }
1178         
1179         /* we cannot disconnect sigs, because priv->body_view is
1180          * already dead */
1181         
1182         disconnect_vadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1183         disconnect_hadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1184
1185         priv->body_view = NULL;
1186         priv->attachments_view = NULL;
1187
1188         G_OBJECT_CLASS(parent_class)->finalize (obj);           
1189 }
1190
1191 static void
1192 modest_gtkhtml_msg_view_destroy (GtkObject *obj)
1193 {       
1194         disconnect_vadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1195         disconnect_hadjustment (MODEST_GTKHTML_MSG_VIEW(obj));
1196
1197         GTK_OBJECT_CLASS(parent_class)->destroy (obj);          
1198 }
1199
1200 /* INTERNAL METHODS */
1201
1202 #ifdef MAEMO_CHANGES
1203 static gboolean 
1204 motion_notify_event (GtkWidget *widget,
1205                      GdkEventMotion *event,
1206                      gpointer userdata)
1207 {
1208         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (userdata);
1209
1210         /* Use panning information in gtkhtml widget to support also vertical panning */
1211
1212         if (GTK_HTML (widget)->panning) {
1213                 gint y, dy;
1214                 gdouble value;
1215
1216                 gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, NULL, &y, NULL);
1217                 dy = y - GTK_HTML (widget)->lasty;
1218                 value = priv->vadj->value - (gdouble) dy;
1219
1220                 if (value < priv->vadj->lower)
1221                         value = priv->vadj->lower;
1222                 else if (value > priv->vadj->upper - priv->vadj->page_size)
1223                         value = priv->vadj->upper - priv->vadj->page_size;
1224                 gtk_adjustment_set_value (priv->vadj, value);
1225                 
1226         } 
1227         return FALSE;
1228 }
1229 #endif
1230
1231 static gboolean
1232 idle_motion (gpointer userdata)
1233 {
1234         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (userdata);
1235         if (GTK_HTML (priv->body_view)->in_selection_drag) {
1236                 gdouble offset;
1237                 GtkAdjustment *adj;
1238                 gint gdk_y;
1239                 gdk_window_get_pointer (gtk_widget_get_parent_window (priv->body_view), NULL, &gdk_y, NULL);
1240                 offset= (gdouble) (priv->headers_box->requisition.height + gdk_y);
1241                 adj = GTK_ADJUSTMENT (priv->vadj);
1242                 if (offset < adj->value + adj->step_increment) {
1243                         gtk_adjustment_set_value (adj, MAX (offset + adj->page_increment - adj->page_size, 0.0));
1244                 } else if (offset > adj->value + adj->page_increment) {
1245                         gtk_adjustment_set_value (adj, MIN (offset - adj->page_increment, adj->upper - adj->page_size));
1246                 }
1247                 gtk_widget_queue_resize (userdata);
1248         }
1249         return TRUE;
1250 }
1251
1252 static gboolean 
1253 button_press_event (GtkWidget *widget,
1254                     GdkEventButton *event,
1255                     gpointer userdata)
1256 {
1257         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (userdata);
1258
1259         if (priv->idle_motion_id == 0) {
1260                 priv->idle_motion_id = g_timeout_add (200, idle_motion, userdata);
1261         }
1262         return FALSE;
1263 }
1264
1265 static gboolean 
1266 button_release_event (GtkWidget *widget,
1267                       GdkEventButton *event,
1268                       gpointer userdata)
1269 {
1270         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (userdata);
1271
1272         if (priv->idle_motion_id > 0) {
1273                 gint gdk_y;
1274                 g_source_remove (priv->idle_motion_id);
1275                 
1276                 priv->idle_motion_id = 0;;
1277                 gdk_window_get_pointer (gtk_widget_get_parent_window (priv->body_view), NULL, &gdk_y, NULL);
1278                 event->y = (gdouble) gdk_y;
1279         }
1280         return FALSE;
1281 }
1282
1283 static GtkAdjustment *
1284 get_vadjustment (ModestGtkhtmlMsgView *self)
1285 {
1286         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1287
1288         if (!priv->vadj)
1289                 set_vadjustment (self, NULL);
1290
1291         return priv->vadj;
1292         
1293 }
1294
1295 static GtkAdjustment *
1296 get_hadjustment (ModestGtkhtmlMsgView *self)
1297 {
1298         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1299
1300         if (!priv->hadj)
1301                 set_hadjustment (self, NULL);
1302
1303         return priv->hadj;
1304         
1305 }
1306
1307 static void
1308 set_hadjustment (ModestGtkhtmlMsgView *self, GtkAdjustment *hadj)
1309 {
1310         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1311         gboolean value_changed;
1312         
1313         if (hadj && hadj == priv->hadj)
1314                 return;
1315
1316         if (!hadj)
1317                 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,0.0,0.0,0.0,0.0,0.0));
1318         disconnect_hadjustment (self);
1319         g_object_ref (G_OBJECT (hadj));
1320         gtk_object_sink (GTK_OBJECT (hadj));
1321         priv->hadj = hadj;
1322         set_hadjustment_values (self, &value_changed);
1323
1324         g_signal_connect (hadj, "value_changed", G_CALLBACK (adjustment_value_changed),
1325                           self);
1326
1327         gtk_adjustment_changed (hadj);
1328         if (value_changed)
1329                 gtk_adjustment_value_changed (hadj);
1330         else
1331                 adjustment_value_changed (hadj, self);
1332
1333         g_object_notify (G_OBJECT (self), "hadjustment");
1334 }
1335
1336 static void
1337 set_vadjustment (ModestGtkhtmlMsgView *self, GtkAdjustment *vadj)
1338 {
1339         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1340         gboolean value_changed;
1341
1342         if (vadj && vadj == priv->vadj)
1343                 return;
1344
1345         if (!vadj)
1346                 vadj = (GtkAdjustment *) gtk_adjustment_new (0.0,0.0,0.0,0.0,0.0,0.0);
1347         disconnect_vadjustment (self);
1348         g_object_ref (G_OBJECT (vadj));
1349         gtk_object_sink (GTK_OBJECT (vadj));
1350         priv->vadj = vadj;
1351         set_vadjustment_values (self, &value_changed);
1352
1353         g_signal_connect (vadj, "value_changed", G_CALLBACK (adjustment_value_changed),
1354                           self);
1355
1356         gtk_adjustment_changed (vadj);
1357         if (value_changed)
1358                 gtk_adjustment_value_changed (vadj);
1359         else
1360                 adjustment_value_changed (vadj, self);
1361
1362         g_object_notify (G_OBJECT (self), "vadjustment");
1363 }
1364
1365 static void
1366 set_shadow_type (ModestGtkhtmlMsgView *self,
1367                  GtkShadowType shadow_type)
1368 {
1369         ModestGtkhtmlMsgViewPrivate *priv;
1370         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1371
1372         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1373         
1374         if (priv->shadow_type != shadow_type) {
1375                 priv->shadow_type = shadow_type;
1376                 
1377                 if (GTK_WIDGET_VISIBLE (self)) {
1378                         gtk_widget_size_allocate (GTK_WIDGET (self), &(GTK_WIDGET (self)->allocation));
1379                         gtk_widget_queue_draw (GTK_WIDGET (self));
1380                 }
1381                 g_object_notify (G_OBJECT (self), "shadow-type");
1382         }
1383 }
1384
1385 static GtkShadowType
1386 get_shadow_type (ModestGtkhtmlMsgView *self)
1387 {
1388         ModestGtkhtmlMsgViewPrivate *priv;
1389         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), GTK_SHADOW_NONE);
1390         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1391         
1392         return priv->shadow_type;
1393 }
1394
1395 GtkWidget*
1396 modest_msg_view_new (TnyMsg *msg)
1397 {
1398         GObject *obj;
1399         ModestGtkhtmlMsgView* self;
1400         
1401         obj  = G_OBJECT(g_object_new(MODEST_TYPE_GTKHTML_MSG_VIEW, NULL));
1402         self = MODEST_GTKHTML_MSG_VIEW(obj);
1403         tny_msg_view_set_msg (TNY_MSG_VIEW (self), msg);
1404
1405         return GTK_WIDGET(self);
1406 }
1407
1408 #ifdef MAEMO_CHANGES
1409 static void
1410 on_tap_and_hold (GtkWidget *widget,
1411                  gpointer data)
1412 {
1413         ModestGtkhtmlMsgView *self = (ModestGtkhtmlMsgView *) data;
1414         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1415
1416         g_signal_emit_by_name (G_OBJECT (self), "link-contextual", priv->last_url);
1417 }
1418
1419 static gboolean
1420 on_tap_and_hold_query (GtkWidget *widget,
1421                        GdkEvent *event,
1422                        gpointer data)
1423 {
1424         ModestGtkhtmlMsgView *self = (ModestGtkhtmlMsgView *) data;
1425         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1426
1427         /* Don't show the tap and hold animation if no url below */
1428         return (priv->last_url == NULL);
1429 }
1430 #endif
1431
1432 static void
1433 on_recpt_activated (ModestMailHeaderView *header_view, 
1434                     const gchar *address,
1435                     ModestGtkhtmlMsgView *self)
1436 {
1437         g_signal_emit_by_name (G_OBJECT (self), "recpt-activated", address);
1438 }
1439
1440 static void
1441 on_show_details (ModestMailHeaderView *header_view, 
1442                  ModestGtkhtmlMsgView *self)
1443 {
1444         g_signal_emit_by_name (G_OBJECT (self), "show-details");
1445 }
1446
1447 static void
1448 on_attachment_activated (ModestAttachmentsView * att_view, TnyMimePart *mime_part, gpointer self)
1449 {
1450
1451         g_signal_emit_by_name (G_OBJECT(self), "attachment_clicked", mime_part);
1452 }
1453
1454 static void
1455 on_view_images_clicked (GtkButton * button, gpointer self)
1456 {
1457         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1458         TnyMimePart *part;
1459
1460         modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), TRUE);
1461         gtk_widget_hide (priv->view_images_button);
1462         part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (priv->body_view));
1463         tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (priv->body_view), part);
1464         tny_msg_set_allow_external_images (TNY_MSG (priv->msg), TRUE);
1465         g_object_unref (part);
1466         
1467
1468 }
1469
1470 static gboolean
1471 on_activate_link (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *self)
1472 {
1473         gboolean result;
1474         g_return_val_if_fail (self, FALSE);
1475         
1476         g_signal_emit_by_name (G_OBJECT(self), "activate-link", uri, &result);
1477
1478         return result;
1479 }
1480
1481
1482 static gboolean
1483 on_link_hover (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMsgView *self)
1484 {
1485         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1486         gboolean result;
1487
1488         g_free (priv->last_url);
1489         priv->last_url = g_strdup (uri);
1490
1491         g_signal_emit_by_name (G_OBJECT(self), "link-hover", uri, &result);
1492
1493         return result;
1494 }
1495
1496
1497
1498 static TnyMimePart *
1499 find_cid_image (TnyMsg *msg, const gchar *cid)
1500 {
1501         TnyMimePart *part = NULL;
1502         TnyList *parts;
1503         TnyIterator *iter;
1504         
1505         g_return_val_if_fail (msg, NULL);
1506         g_return_val_if_fail (cid, NULL);
1507         
1508         parts  = TNY_LIST (tny_simple_list_new());
1509
1510         tny_mime_part_get_parts (TNY_MIME_PART (msg), parts); 
1511         iter   = tny_list_create_iterator (parts);
1512         
1513         while (!tny_iterator_is_done(iter)) {
1514                 const gchar *part_cid;
1515
1516                 part = TNY_MIME_PART(tny_iterator_get_current(iter));
1517                 part_cid = tny_mime_part_get_content_id (part);
1518
1519                 /* if there is no content id, try the content location;
1520                  * this is what Outlook seems to use when it converts
1521                  * it's internal richtext to html
1522                  */
1523                 if (!part_cid)
1524                         part_cid = tny_mime_part_get_content_location (part);
1525                 
1526                 if (part_cid && strcmp (cid, part_cid) == 0)
1527                         break;
1528
1529                 if (tny_mime_part_content_type_is (part, "multipart/related")) {
1530                         TnyList *related_parts = TNY_LIST (tny_simple_list_new ());
1531                         TnyIterator *related_iter = NULL;
1532                         TnyMimePart *related_part = NULL;
1533
1534                         tny_mime_part_get_parts (part, related_parts);
1535                         related_iter = tny_list_create_iterator (related_parts);
1536
1537                         while (!tny_iterator_is_done (related_iter)) {
1538                                 related_part = TNY_MIME_PART (tny_iterator_get_current (related_iter));
1539                                 part_cid = tny_mime_part_get_content_id (related_part);
1540                                 if (part_cid && strcmp (cid, part_cid) == 0) {
1541                                         break;
1542                                 }
1543                                 g_object_unref (related_part);
1544                                 related_part = NULL;
1545                                 tny_iterator_next (related_iter);
1546                         }
1547
1548                         g_object_unref (related_iter);
1549                         g_object_unref (related_parts);
1550                         if (related_part != NULL) {
1551                                 g_object_unref (part);
1552                                 part = related_part;
1553                                 break;
1554                         }
1555                 }
1556
1557                 g_object_unref (G_OBJECT(part));
1558         
1559                 part = NULL;
1560                 tny_iterator_next (iter);
1561         }
1562         
1563         g_object_unref (G_OBJECT(iter));        
1564         g_object_unref (G_OBJECT(parts));
1565         
1566         return part;
1567 }
1568
1569
1570 static gboolean
1571 on_fetch_url (GtkWidget *widget, const gchar *uri,
1572               TnyStream *stream, ModestGtkhtmlMsgView *self)
1573 {
1574         ModestGtkhtmlMsgViewPrivate *priv;
1575         const gchar* my_uri;
1576         TnyMimePart *part = NULL;
1577         
1578
1579
1580         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1581
1582         if (modest_mime_part_view_has_external_images (MODEST_MIME_PART_VIEW (priv->body_view)))
1583                 gtk_widget_show (priv->view_images_button);
1584         /*
1585          * we search for either something starting with cid:, or something
1586          * with no prefix at all; this latter case occurs when sending mails
1587          * with MS Outlook in rich-text mode, and 'attach-as-object
1588          */
1589         if (g_str_has_prefix (uri, "cid:"))  
1590                 my_uri = uri + 4;  /* +4 ==> skip "cid:" */
1591         else
1592                 my_uri = uri;
1593         
1594         /* now try to find the embedded image */
1595         part = find_cid_image (priv->msg, my_uri);
1596
1597         if (!part) {
1598                 if (g_str_has_prefix (uri, "http:")) {
1599                         if (modest_mime_part_view_get_view_images (MODEST_MIME_PART_VIEW (priv->body_view))) {
1600                                 gboolean result = FALSE;
1601                                 g_signal_emit_by_name (self, "fetch-image", uri, stream, &result);
1602                                 return result;
1603                         } else {
1604                                 /* we return immediately to get a "image not found" icon */
1605                                 tny_stream_close (stream);
1606                                 return TRUE;
1607                         }
1608                 } else {
1609                         return FALSE;
1610                 }
1611         }
1612
1613         tny_mime_part_decode_to_stream ((TnyMimePart*)part, stream, NULL);
1614         tny_stream_close (stream);
1615         g_object_unref (G_OBJECT(part));
1616         return TRUE;
1617 }
1618
1619 static void
1620 set_message (ModestGtkhtmlMsgView *self, TnyMsg *msg)
1621 {
1622         TnyMimePart *body;
1623         ModestGtkhtmlMsgViewPrivate *priv;
1624         TnyHeader *header;
1625         GtkAdjustment *html_vadj;
1626         
1627         g_return_if_fail (self);
1628         
1629         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(self);
1630         gtk_widget_set_no_show_all (priv->mail_header_view, FALSE);
1631         modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), FALSE);
1632
1633         html_vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->html_scroll));
1634         html_vadj->upper = 0;
1635         html_vadj->page_size = 0;
1636         g_signal_emit_by_name (G_OBJECT (html_vadj), "changed");
1637
1638
1639         if (msg != priv->msg) {
1640                 if (priv->msg)
1641                         g_object_unref (G_OBJECT(priv->msg));
1642                 if (msg)
1643                         g_object_ref   (G_OBJECT(msg));
1644                 priv->msg = msg;
1645         }
1646         
1647         if (!msg) {
1648                 tny_header_view_clear (TNY_HEADER_VIEW (priv->mail_header_view));
1649                 modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), NULL);
1650                 gtk_widget_hide_all (priv->mail_header_view);
1651                 gtk_widget_hide_all (priv->attachments_box);
1652                 gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
1653                 tny_mime_part_view_clear (TNY_MIME_PART_VIEW (priv->body_view));
1654                 gtk_widget_queue_resize (GTK_WIDGET(self));
1655                 gtk_widget_queue_draw (GTK_WIDGET(self));
1656                 return;
1657         }
1658
1659         header = tny_msg_get_header (msg);
1660         tny_header_view_set_header (TNY_HEADER_VIEW (priv->mail_header_view), header);
1661         g_object_unref (header);
1662
1663         modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW(priv->attachments_view),
1664                                              msg);
1665
1666         modest_mime_part_view_set_view_images (MODEST_MIME_PART_VIEW (priv->body_view), tny_msg_get_allow_external_images (msg));
1667         
1668         body = modest_tny_msg_find_body_part (msg, TRUE);
1669         if (body) {
1670                 tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (priv->body_view), body);
1671                 g_object_unref (body);
1672
1673                 if(modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) {
1674                         gtk_widget_show_all (priv->attachments_box);
1675                 } else {
1676                         gtk_widget_hide_all (priv->attachments_box);
1677                 }
1678
1679         } else 
1680                 tny_mime_part_view_clear (TNY_MIME_PART_VIEW (priv->body_view));
1681
1682         if (modest_mime_part_view_has_external_images (MODEST_MIME_PART_VIEW (priv->body_view)) &&
1683             !modest_mime_part_view_get_view_images (MODEST_MIME_PART_VIEW (priv->body_view))) {
1684                 gtk_widget_show (priv->view_images_button);
1685         } else {
1686                 gtk_widget_hide (priv->view_images_button);
1687         }
1688
1689         gtk_widget_show (priv->body_view);
1690         gtk_widget_set_no_show_all (priv->attachments_box, TRUE);
1691         gtk_widget_show_all (priv->mail_header_view);
1692         gtk_widget_set_no_show_all (priv->attachments_box, FALSE);
1693         gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
1694         gtk_widget_queue_resize (GTK_WIDGET(self));
1695         gtk_widget_queue_draw (GTK_WIDGET(self));
1696
1697         if (priv->hadj != NULL)
1698                 priv->hadj->value = 0.0;
1699         if (priv->vadj != NULL)
1700                 priv->vadj->value = 0.0;
1701
1702         g_signal_emit_by_name (G_OBJECT (html_vadj), "changed");
1703
1704         /* This is a hack to force reallocation of scroll after drawing all the stuff. This
1705          * makes the html view get the proper and expected size and prevent being able to scroll
1706          * the buffer when it shouldn't be scrollable */
1707         g_object_ref (self);
1708         g_timeout_add (250, (GSourceFunc) idle_readjust_scroll, self);
1709 }
1710
1711
1712 static TnyMsg*
1713 get_message (ModestGtkhtmlMsgView *self)
1714 {
1715         TnyMsg *msg;
1716
1717         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), NULL);
1718
1719         msg = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE(self)->msg;
1720
1721         if (msg)
1722                 g_object_ref (msg);
1723         
1724         return msg;
1725 }
1726
1727 static gboolean 
1728 is_empty (ModestGtkhtmlMsgView *self)
1729 {
1730         ModestGtkhtmlMsgViewPrivate *priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1731
1732         return modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->body_view));
1733 }
1734
1735 static void
1736 set_zoom (ModestGtkhtmlMsgView *self, gdouble zoom)
1737 {
1738         ModestGtkhtmlMsgViewPrivate *priv;
1739
1740         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1741         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1742
1743         modest_zoomable_set_zoom (MODEST_ZOOMABLE(priv->body_view), zoom);
1744
1745         gtk_widget_queue_resize (priv->body_view);
1746 }
1747
1748 static gdouble
1749 get_zoom (ModestGtkhtmlMsgView *self)
1750 {
1751         ModestGtkhtmlMsgViewPrivate *priv;
1752
1753         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), 1.0);
1754         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1755
1756         return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->body_view));
1757 }
1758
1759 static TnyHeaderFlags
1760 get_priority (ModestGtkhtmlMsgView *self)
1761 {
1762         ModestGtkhtmlMsgViewPrivate *priv;
1763
1764         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), 0);
1765
1766         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1767
1768         return modest_mail_header_view_get_priority (MODEST_MAIL_HEADER_VIEW (priv->mail_header_view));
1769 }
1770
1771 static void
1772 set_priority (ModestGtkhtmlMsgView *self, TnyHeaderFlags flags)
1773 {
1774         ModestGtkhtmlMsgViewPrivate *priv;
1775
1776         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1777         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1778
1779         modest_mail_header_view_set_priority (MODEST_MAIL_HEADER_VIEW (priv->mail_header_view), flags);
1780 }
1781
1782 /* INCREMENTAL SEARCH IMPLEMENTATION */
1783
1784 static gboolean 
1785 search (ModestGtkhtmlMsgView *self, const gchar *search)
1786 {
1787         ModestGtkhtmlMsgViewPrivate *priv;
1788         gboolean result;
1789         GtkAdjustment *vadj, *tmp_vadj;
1790         gdouble y_offset;
1791
1792         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), FALSE);
1793
1794         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1795         vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (priv->body_view));
1796         g_object_ref (vadj);
1797         tmp_vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, vadj->lower, vadj->upper, vadj->step_increment, 32.0, 32.0));
1798         gtk_layout_set_vadjustment (GTK_LAYOUT (priv->body_view), tmp_vadj);
1799         result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->body_view),
1800                                              search);
1801
1802         if (result) {
1803                 gint x, y, w, h;
1804                 gdouble offset_top, offset_bottom;
1805                 GtkAdjustment *adj;
1806                 if (modest_isearch_view_get_selection_area (MODEST_ISEARCH_VIEW (priv->body_view), &x, &y, &w, &h)) {
1807                         offset_top = (gdouble) (priv->headers_box->requisition.height + y);
1808                         offset_bottom = (gdouble) (priv->headers_box->requisition.height + y + h);
1809                         adj = GTK_ADJUSTMENT (priv->vadj);
1810                         if (offset_top < adj->value)
1811                                 gtk_adjustment_set_value (adj, offset_top + adj->page_increment - adj->page_size);
1812                         else if (offset_bottom > adj->value + adj->page_increment)
1813                                 gtk_adjustment_set_value (adj, offset_bottom - adj->page_increment);
1814                 }
1815         }
1816
1817         y_offset = tmp_vadj->value;
1818         gtk_layout_set_vadjustment (GTK_LAYOUT (priv->body_view), vadj);
1819         g_object_unref (vadj);
1820
1821         return result;
1822 }
1823
1824 static gboolean
1825 search_next (ModestGtkhtmlMsgView *self)
1826 {
1827         ModestGtkhtmlMsgViewPrivate *priv;
1828         gboolean result;
1829
1830         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), FALSE);
1831
1832         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1833         result = modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->body_view));
1834
1835         if (result) {
1836                 gint x, y, w, h;
1837                 gdouble offset_top, offset_bottom;
1838                 GtkAdjustment *adj;
1839
1840                 if (modest_isearch_view_get_selection_area (MODEST_ISEARCH_VIEW (priv->body_view), &x, &y, &w, &h)) {
1841                         offset_top = (gdouble) (priv->headers_box->requisition.height + y);
1842                         offset_bottom = (gdouble) (priv->headers_box->requisition.height + y + h);
1843                         adj = GTK_ADJUSTMENT (priv->vadj);
1844                         if (offset_top < adj->value)
1845                                 gtk_adjustment_set_value (adj, offset_top + adj->page_increment - adj->page_size);
1846                         else if (offset_bottom > adj->value + adj->page_increment)
1847                                 gtk_adjustment_set_value (adj, offset_bottom - adj->page_increment);
1848                 }
1849         }
1850         return result;
1851 }
1852
1853 static TnyList *
1854 get_selected_attachments (ModestGtkhtmlMsgView *self)
1855 {
1856         ModestGtkhtmlMsgViewPrivate *priv;
1857
1858         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), NULL);
1859         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1860
1861         return modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1862         
1863 }
1864
1865 static TnyList *
1866 get_attachments (ModestGtkhtmlMsgView *self)
1867 {
1868         ModestGtkhtmlMsgViewPrivate *priv;
1869
1870         g_return_val_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self), NULL);
1871         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1872
1873         return modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view));
1874         
1875 }
1876
1877 static void
1878 grab_focus (ModestGtkhtmlMsgView *self)
1879 {
1880         ModestGtkhtmlMsgViewPrivate *priv = NULL;
1881
1882         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1883         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1884
1885         gtk_widget_grab_focus (priv->body_view);
1886 }
1887
1888 static void
1889 remove_attachment (ModestGtkhtmlMsgView *self, TnyMimePart *attachment)
1890 {
1891         ModestGtkhtmlMsgViewPrivate *priv;
1892
1893         g_return_if_fail (MODEST_IS_GTKHTML_MSG_VIEW (self));
1894         g_return_if_fail (TNY_IS_MIME_PART (attachment));
1895         priv = MODEST_GTKHTML_MSG_VIEW_GET_PRIVATE (self);
1896
1897         modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view),
1898                                                    attachment);
1899         
1900 }
1901
1902 /* TNY MSG IMPLEMENTATION */
1903
1904 static void
1905 tny_msg_view_init (gpointer g, gpointer iface_data)
1906 {
1907         TnyMsgViewIface *klass = (TnyMsgViewIface *)g;
1908
1909         klass->get_msg = modest_msg_view_get_msg;
1910         klass->set_msg = modest_msg_view_set_msg;
1911         klass->set_unavailable = modest_msg_view_set_unavailable;
1912         klass->clear = modest_msg_view_clear;
1913         klass->create_mime_part_view_for = modest_msg_view_create_mime_part_view_for;
1914         klass->create_new_inline_viewer = modest_msg_view_create_new_inline_viewer;
1915
1916         return;
1917 }
1918
1919 static TnyMsg *
1920 modest_msg_view_get_msg (TnyMsgView *self)
1921 {
1922         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_msg_func (self);
1923 }
1924
1925 static TnyMsg *
1926 modest_msg_view_get_msg_default (TnyMsgView *self)
1927 {
1928         return TNY_MSG (tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (self)));
1929 }
1930
1931 static void
1932 modest_msg_view_set_msg (TnyMsgView *self, TnyMsg *msg)
1933 {
1934         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_msg_func (self, msg);
1935 }
1936
1937 static void 
1938 modest_msg_view_set_msg_default (TnyMsgView *self, TnyMsg *msg)
1939 {
1940
1941         tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (self), TNY_MIME_PART (msg));
1942
1943         return;
1944 }
1945
1946 static void
1947 modest_msg_view_set_unavailable (TnyMsgView *self)
1948 {
1949         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_unavailable_func (self);
1950 }
1951
1952 static void
1953 modest_msg_view_set_unavailable_default (TnyMsgView *self)
1954 {
1955         tny_msg_view_clear (self);
1956
1957         return;
1958 }
1959
1960 static void
1961 modest_msg_view_clear (TnyMsgView *self)
1962 {
1963         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->clear_func (self);
1964 }
1965
1966 static void
1967 modest_msg_view_clear_default (TnyMsgView *self)
1968 {
1969         set_message (MODEST_GTKHTML_MSG_VIEW (self), NULL);
1970 }
1971
1972 static TnyMimePartView*
1973 modest_msg_view_create_mime_part_view_for (TnyMsgView *self, TnyMimePart *part)
1974 {
1975         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->create_mime_part_view_for_func (self, part);
1976 }
1977
1978 static TnyMimePartView*
1979 modest_msg_view_create_mime_part_view_for_default (TnyMsgView *self, TnyMimePart *part)
1980 {
1981         g_warning ("modest_msg_view_create_mime_part_view_for_default is not implemented");
1982         return NULL;
1983 }
1984
1985 static TnyMsgView*
1986 modest_msg_view_create_new_inline_viewer (TnyMsgView *self)
1987 {
1988         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->create_new_inline_viewer_func (self);
1989 }
1990
1991 static TnyMsgView*
1992 modest_msg_view_create_new_inline_viewer_default (TnyMsgView *self)
1993 {
1994         g_warning ("modest_msg_view_create_new_inline_viewer_default is not implemented");
1995
1996         return NULL;
1997 }
1998
1999 /* TNY MIME PART IMPLEMENTATION */
2000
2001 static void
2002 tny_mime_part_view_init (gpointer g, gpointer iface_data)
2003 {
2004         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
2005
2006         klass->get_part = modest_msg_view_mp_get_part;
2007         klass->set_part = modest_msg_view_mp_set_part;
2008         klass->clear = modest_msg_view_mp_clear;
2009
2010         return;
2011 }
2012
2013 static TnyMimePart* 
2014 modest_msg_view_mp_get_part (TnyMimePartView *self)
2015 {
2016         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_part_func (self);
2017 }
2018
2019
2020 static TnyMimePart* 
2021 modest_msg_view_mp_get_part_default (TnyMimePartView *self)
2022 {
2023         return TNY_MIME_PART (get_message (MODEST_GTKHTML_MSG_VIEW (self)));
2024 }
2025
2026 static void
2027 modest_msg_view_mp_set_part (TnyMimePartView *self,
2028                              TnyMimePart *part)
2029 {
2030         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_part_func (self, part);
2031 }
2032
2033 static void
2034 modest_msg_view_mp_set_part_default (TnyMimePartView *self,
2035                                      TnyMimePart *part)
2036 {
2037         g_return_if_fail ((part == NULL) || TNY_IS_MSG (part));
2038
2039         set_message (MODEST_GTKHTML_MSG_VIEW (self), TNY_MSG (part));
2040 }
2041
2042 static void
2043 modest_msg_view_mp_clear (TnyMimePartView *self)
2044 {
2045         tny_msg_view_clear (TNY_MSG_VIEW (self));
2046 }
2047
2048 /* MODEST MIME PART VIEW IMPLEMENTATION */
2049
2050 static void
2051 modest_mime_part_view_init (gpointer g, gpointer iface_data)
2052 {
2053         ModestMimePartViewIface *klass = (ModestMimePartViewIface *)g;
2054
2055         klass->is_empty_func = modest_msg_view_mp_is_empty;
2056
2057         return;
2058 }
2059
2060 static gboolean
2061 modest_msg_view_mp_is_empty (ModestMimePartView *self)
2062 {
2063         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->is_empty_func (self);
2064 }
2065
2066 static gboolean
2067 modest_msg_view_mp_is_empty_default (ModestMimePartView *self)
2068 {
2069         return is_empty (MODEST_GTKHTML_MSG_VIEW (self));
2070 }
2071
2072 /* MODEST ZOOMABLE IMPLEMENTATION */
2073 static void
2074 modest_zoomable_init (gpointer g, gpointer iface_data)
2075 {
2076         ModestZoomableIface *klass = (ModestZoomableIface *)g;
2077
2078         klass->get_zoom_func = modest_msg_view_get_zoom;
2079         klass->set_zoom_func = modest_msg_view_set_zoom;
2080         klass->zoom_minus_func = modest_msg_view_zoom_minus;
2081         klass->zoom_plus_func = modest_msg_view_zoom_plus;
2082
2083         return;
2084 }
2085
2086 static gdouble
2087 modest_msg_view_get_zoom (ModestZoomable *self)
2088 {
2089         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_zoom_func (self);
2090 }
2091
2092 static gdouble
2093 modest_msg_view_get_zoom_default (ModestZoomable *self)
2094 {
2095         return get_zoom (MODEST_GTKHTML_MSG_VIEW (self));
2096 }
2097
2098 static void
2099 modest_msg_view_set_zoom (ModestZoomable *self, gdouble value)
2100 {
2101         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_zoom_func (self, value);
2102 }
2103
2104 static void
2105 modest_msg_view_set_zoom_default (ModestZoomable *self, gdouble value)
2106 {
2107         set_zoom (MODEST_GTKHTML_MSG_VIEW (self), value);
2108 }
2109
2110 static gboolean
2111 modest_msg_view_zoom_minus (ModestZoomable *self)
2112 {
2113         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->zoom_minus_func (self);
2114 }
2115
2116 static gboolean
2117 modest_msg_view_zoom_minus_default (ModestZoomable *self)
2118 {
2119         /* operation not supported in ModestMsgView */
2120         return FALSE;
2121 }
2122
2123 static gboolean
2124 modest_msg_view_zoom_plus (ModestZoomable *self)
2125 {
2126         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->zoom_plus_func (self);
2127 }
2128
2129 static gboolean
2130 modest_msg_view_zoom_plus_default (ModestZoomable *self)
2131 {
2132         /* operation not supported in ModestMsgView */
2133         return FALSE;
2134 }
2135
2136 /* MODEST ISEARCH VIEW IMPLEMENTATION */
2137 static void
2138 modest_isearch_view_init (gpointer g, gpointer iface_data)
2139 {
2140         ModestISearchViewIface *klass = (ModestISearchViewIface *)g;
2141
2142         klass->search_func = modest_msg_view_search;
2143         klass->search_next_func = modest_msg_view_search_next;
2144
2145         return;
2146 }
2147
2148 static gboolean
2149 modest_msg_view_search (ModestISearchView *self, const gchar *string)
2150 {
2151         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->search_func (self, string);
2152 }
2153
2154 static gboolean
2155 modest_msg_view_search_default (ModestISearchView *self, const gchar *string)
2156 {
2157         return search (MODEST_GTKHTML_MSG_VIEW (self), string);
2158 }
2159
2160 static gboolean
2161 modest_msg_view_search_next (ModestISearchView *self)
2162 {
2163         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->search_next_func (self);
2164 }
2165
2166 static gboolean
2167 modest_msg_view_search_next_default (ModestISearchView *self)
2168 {
2169         return search_next (MODEST_GTKHTML_MSG_VIEW (self));
2170 }
2171
2172 /* MODEST MSG VIEW IMPLEMENTATION */
2173 static void
2174 modest_msg_view_init (gpointer g, gpointer iface_data)
2175 {
2176         ModestMsgViewIface *klass = (ModestMsgViewIface *)g;
2177
2178         klass->get_vadjustment_func = modest_gtkhtml_msg_view_get_vadjustment;
2179         klass->get_hadjustment_func = modest_gtkhtml_msg_view_get_hadjustment;
2180         klass->set_vadjustment_func = modest_gtkhtml_msg_view_set_vadjustment;
2181         klass->set_hadjustment_func = modest_gtkhtml_msg_view_set_hadjustment;
2182         klass->set_shadow_type_func = modest_gtkhtml_msg_view_set_shadow_type;
2183         klass->get_shadow_type_func = modest_gtkhtml_msg_view_get_shadow_type;
2184         klass->get_priority_func = modest_gtkhtml_msg_view_get_priority;
2185         klass->set_priority_func = modest_gtkhtml_msg_view_set_priority;
2186         klass->get_selected_attachments_func = modest_gtkhtml_msg_view_get_selected_attachments;
2187         klass->get_attachments_func = modest_gtkhtml_msg_view_get_attachments;
2188         klass->grab_focus_func = modest_gtkhtml_msg_view_grab_focus;
2189         klass->remove_attachment_func = modest_gtkhtml_msg_view_remove_attachment;
2190
2191         return;
2192 }
2193
2194 static GtkAdjustment*
2195 modest_gtkhtml_msg_view_get_vadjustment (ModestMsgView *self)
2196 {
2197         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_vadjustment_func (self);
2198 }
2199
2200 static GtkAdjustment*
2201 modest_gtkhtml_msg_view_get_vadjustment_default (ModestMsgView *self)
2202 {
2203         return get_vadjustment (MODEST_GTKHTML_MSG_VIEW (self));
2204 }
2205
2206 static GtkAdjustment*
2207 modest_gtkhtml_msg_view_get_hadjustment (ModestMsgView *self)
2208 {
2209         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_hadjustment_func (self);
2210 }
2211
2212 static GtkAdjustment*
2213 modest_gtkhtml_msg_view_get_hadjustment_default (ModestMsgView *self)
2214 {
2215         return get_hadjustment (MODEST_GTKHTML_MSG_VIEW (self));
2216 }
2217
2218 static void
2219 modest_gtkhtml_msg_view_set_vadjustment (ModestMsgView *self, GtkAdjustment *adj)
2220 {
2221         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_vadjustment_func (self, adj);
2222 }
2223
2224 static void
2225 modest_gtkhtml_msg_view_set_vadjustment_default (ModestMsgView *self, GtkAdjustment *adj)
2226 {
2227         set_vadjustment (MODEST_GTKHTML_MSG_VIEW (self), adj);
2228 }
2229
2230 static void
2231 modest_gtkhtml_msg_view_set_hadjustment (ModestMsgView *self, GtkAdjustment *adj)
2232 {
2233         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_hadjustment_func (self, adj);
2234 }
2235
2236 static void
2237 modest_gtkhtml_msg_view_set_hadjustment_default (ModestMsgView *self, GtkAdjustment *adj)
2238 {
2239         set_hadjustment (MODEST_GTKHTML_MSG_VIEW (self), adj);
2240 }
2241
2242 static void
2243 modest_gtkhtml_msg_view_set_shadow_type (ModestMsgView *self, GtkShadowType type)
2244 {
2245         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_shadow_type_func (self, type);
2246 }
2247
2248 static void
2249 modest_gtkhtml_msg_view_set_shadow_type_default (ModestMsgView *self, GtkShadowType type)
2250 {
2251         set_shadow_type (MODEST_GTKHTML_MSG_VIEW (self), type);
2252 }
2253
2254 static GtkShadowType
2255 modest_gtkhtml_msg_view_get_shadow_type (ModestMsgView *self)
2256 {
2257         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_shadow_type_func (self);
2258 }
2259
2260 static GtkShadowType
2261 modest_gtkhtml_msg_view_get_shadow_type_default (ModestMsgView *self)
2262 {
2263         return get_shadow_type (MODEST_GTKHTML_MSG_VIEW (self));
2264 }
2265
2266 static void
2267 modest_gtkhtml_msg_view_set_priority (ModestMsgView *self, TnyHeaderFlags flags)
2268 {
2269         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->set_priority_func (self, flags);
2270 }
2271
2272 static void
2273 modest_gtkhtml_msg_view_set_priority_default (ModestMsgView *self, TnyHeaderFlags flags)
2274 {
2275         set_priority (MODEST_GTKHTML_MSG_VIEW (self), flags);
2276 }
2277
2278 static TnyHeaderFlags
2279 modest_gtkhtml_msg_view_get_priority (ModestMsgView *self)
2280 {
2281         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_priority_func (self);
2282 }
2283
2284 static TnyHeaderFlags
2285 modest_gtkhtml_msg_view_get_priority_default (ModestMsgView *self)
2286 {
2287         return get_priority (MODEST_GTKHTML_MSG_VIEW (self));
2288 }
2289
2290 static TnyList*
2291 modest_gtkhtml_msg_view_get_selected_attachments (ModestMsgView *self)
2292 {
2293         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_selected_attachments_func (self);
2294 }
2295
2296 static TnyList*
2297 modest_gtkhtml_msg_view_get_selected_attachments_default (ModestMsgView *self)
2298 {
2299         return get_selected_attachments (MODEST_GTKHTML_MSG_VIEW (self));
2300 }
2301
2302 static TnyList*
2303 modest_gtkhtml_msg_view_get_attachments (ModestMsgView *self)
2304 {
2305         return MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->get_attachments_func (self);
2306 }
2307
2308 static TnyList*
2309 modest_gtkhtml_msg_view_get_attachments_default (ModestMsgView *self)
2310 {
2311         return get_attachments (MODEST_GTKHTML_MSG_VIEW (self));
2312 }
2313
2314 static void
2315 modest_gtkhtml_msg_view_grab_focus (ModestMsgView *self)
2316 {
2317         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->grab_focus_func (self);
2318 }
2319
2320 static void
2321 modest_gtkhtml_msg_view_grab_focus_default (ModestMsgView *self)
2322 {
2323         grab_focus (MODEST_GTKHTML_MSG_VIEW (self));
2324 }
2325
2326 static void
2327 modest_gtkhtml_msg_view_remove_attachment (ModestMsgView *self, TnyMimePart *attachment)
2328 {
2329         MODEST_GTKHTML_MSG_VIEW_GET_CLASS (self)->remove_attachment_func (self, attachment);
2330 }
2331
2332 static void
2333 modest_gtkhtml_msg_view_remove_attachment_default (ModestMsgView *self, TnyMimePart *attachment)
2334 {
2335         remove_attachment (MODEST_GTKHTML_MSG_VIEW (self), attachment);
2336 }