Set dim color in size toolbutton label
[modest] / src / widgets / modest-gtkhtml-mime-part-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 <widgets/modest-gtkhtml-mime-part-view.h>
32 #include <string.h>
33 #include <gtkhtml/gtkhtml-stream.h>
34 #include <gtkhtml/gtkhtml-search.h>
35 #include <tny-stream.h>
36 #include <tny-mime-part-view.h>
37 #include "modest-tny-mime-part.h"
38 #include <modest-stream-text-to-html.h>
39 #include <modest-text-utils.h>
40 #include <modest-conf.h>
41 #include <modest-runtime.h>
42 #include <widgets/modest-mime-part-view.h>
43 #include <widgets/modest-zoomable.h>
44 #include <widgets/modest-tny-stream-gtkhtml.h>
45 #include <libgnomevfs/gnome-vfs.h>
46 #include <gdk/gdkkeysyms.h>
47
48 /* gobject structure methods */
49 static void    modest_gtkhtml_mime_part_view_class_init (ModestGtkhtmlMimePartViewClass *klass);
50 static void    tny_mime_part_view_init                  (gpointer g, gpointer iface_data);
51 static void    modest_mime_part_view_init               (gpointer g, gpointer iface_data);
52 static void    modest_zoomable_init                     (gpointer g, gpointer iface_data);
53 static void    modest_isearch_view_init                 (gpointer g, gpointer iface_data);
54 static void    modest_gtkhtml_mime_part_view_init       (ModestGtkhtmlMimePartView *self);
55 static void    modest_gtkhtml_mime_part_view_finalize   (GObject *self);
56 static void    modest_gtkhtml_mime_part_view_dispose    (GObject *self);
57
58 /* GtkHTML signal handlers */
59 static gboolean  on_link_clicked  (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMimePartView *self);
60 static gboolean  on_url           (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMimePartView *self);
61 static gboolean  on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream,
62                                    ModestGtkhtmlMimePartView *self);
63 static void      on_notify_style  (GObject *obj, GParamSpec *spec, gpointer userdata);
64 static gboolean  update_style     (ModestGtkhtmlMimePartView *self);
65 /* TnyMimePartView implementation */
66 static void modest_gtkhtml_mime_part_view_clear (TnyMimePartView *self);
67 static void modest_gtkhtml_mime_part_view_clear_default (TnyMimePartView *self);
68 static void modest_gtkhtml_mime_part_view_set_part (TnyMimePartView *self, TnyMimePart *part);
69 static void modest_gtkhtml_mime_part_view_set_part_default (TnyMimePartView *self, TnyMimePart *part);
70 static TnyMimePart* modest_gtkhtml_mime_part_view_get_part (TnyMimePartView *self);
71 static TnyMimePart* modest_gtkhtml_mime_part_view_get_part_default (TnyMimePartView *self);
72 /* ModestMimePartView implementation */
73 static gboolean modest_gtkhtml_mime_part_view_is_empty (ModestMimePartView *self);
74 static gboolean modest_gtkhtml_mime_part_view_is_empty_default (ModestMimePartView *self);
75 static gboolean modest_gtkhtml_mime_part_view_get_view_images (ModestMimePartView *self);
76 static gboolean modest_gtkhtml_mime_part_view_get_view_images_default (ModestMimePartView *self);
77 static void     modest_gtkhtml_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images);
78 static void     modest_gtkhtml_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images);
79 static gboolean modest_gtkhtml_mime_part_view_has_external_images (ModestMimePartView *self);
80 static gboolean modest_gtkhtml_mime_part_view_has_external_images_default (ModestMimePartView *self);
81 /* ModestZoomable implementation */
82 static gdouble modest_gtkhtml_mime_part_view_get_zoom (ModestZoomable *self);
83 static void modest_gtkhtml_mime_part_view_set_zoom (ModestZoomable *self, gdouble value);
84 static gboolean modest_gtkhtml_mime_part_view_zoom_minus (ModestZoomable *self);
85 static gboolean modest_gtkhtml_mime_part_view_zoom_plus (ModestZoomable *self);
86 static gdouble modest_gtkhtml_mime_part_view_get_zoom_default (ModestZoomable *self);
87 static void modest_gtkhtml_mime_part_view_set_zoom_default (ModestZoomable *self, gdouble value);
88 static gboolean modest_gtkhtml_mime_part_view_zoom_minus_default (ModestZoomable *self);
89 static gboolean modest_gtkhtml_mime_part_view_zoom_plus_default (ModestZoomable *self);
90 /* ModestISearchView implementation */
91 static gboolean modest_gtkhtml_mime_part_view_search                    (ModestISearchView *self, const gchar *string);
92 static gboolean modest_gtkhtml_mime_part_view_search_next               (ModestISearchView *self);
93 static gboolean modest_gtkhtml_mime_part_view_get_selection_area        (ModestISearchView *self, gint *x, gint *y, 
94                                                                          gint *width, gint *height);
95 static gboolean modest_gtkhtml_mime_part_view_search_default            (ModestISearchView *self, const gchar *string);
96 static gboolean modest_gtkhtml_mime_part_view_search_next_default       (ModestISearchView *self);
97 static gboolean modest_gtkhtml_mime_part_view_get_selection_area_default (ModestISearchView *self, gint *x, gint *y, 
98                                                                           gint *width, gint *height);
99
100
101 /* internal api */
102 static TnyMimePart  *get_part   (ModestGtkhtmlMimePartView *self);
103 static void          set_html_part   (ModestGtkhtmlMimePartView *self, TnyMimePart *part);
104 static void          set_text_part   (ModestGtkhtmlMimePartView *self, TnyMimePart *part);
105 static void          set_empty_part  (ModestGtkhtmlMimePartView *self);
106 static void          set_part   (ModestGtkhtmlMimePartView *self, TnyMimePart *part);
107 static gboolean      is_empty   (ModestGtkhtmlMimePartView *self);
108 static gboolean      get_view_images   (ModestGtkhtmlMimePartView *self);
109 static void          set_view_images   (ModestGtkhtmlMimePartView *self, gboolean view_images);
110 static gboolean      has_external_images   (ModestGtkhtmlMimePartView *self);
111 static void          set_zoom   (ModestGtkhtmlMimePartView *self, gdouble zoom);
112 static gdouble       get_zoom   (ModestGtkhtmlMimePartView *self);
113 static gboolean      has_contents_receiver (gpointer engine, const gchar *data,
114                                             size_t len, gboolean *has_contents);
115 static gboolean      search             (ModestGtkhtmlMimePartView *self, const gchar *string);
116 static gboolean      search_next        (ModestGtkhtmlMimePartView *self);
117 static gboolean      get_selection_area (ModestGtkhtmlMimePartView *self, gint *x, gint *y,
118                                          gint *width, gint *height);
119
120 typedef struct _ModestGtkhtmlMimePartViewPrivate ModestGtkhtmlMimePartViewPrivate;
121 struct _ModestGtkhtmlMimePartViewPrivate {
122         TnyMimePart *part;
123         gdouble current_zoom;
124         gboolean view_images;
125         gboolean has_external_images;
126 };
127
128 #define MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
129                                                                                        MODEST_TYPE_GTKHTML_MIME_PART_VIEW, \
130                                                                                        ModestGtkhtmlMimePartViewPrivate))
131
132 static GtkHTMLClass *parent_class = NULL;
133
134 GtkWidget *
135 modest_gtkhtml_mime_part_view_new ()
136 {
137         return g_object_new (MODEST_TYPE_GTKHTML_MIME_PART_VIEW, NULL);
138 }
139
140 /* GOBJECT IMPLEMENTATION */
141 GType
142 modest_gtkhtml_mime_part_view_get_type (void)
143 {
144         static GType my_type = 0;
145         if (!my_type) {
146                 static const GTypeInfo my_info = {
147                         sizeof(ModestGtkhtmlMimePartViewClass),
148                         NULL,           /* base init */
149                         NULL,           /* base finalize */
150                         (GClassInitFunc) modest_gtkhtml_mime_part_view_class_init,
151                         NULL,           /* class finalize */
152                         NULL,           /* class data */
153                         sizeof(ModestGtkhtmlMimePartView),
154                         1,              /* n_preallocs */
155                         (GInstanceInitFunc) modest_gtkhtml_mime_part_view_init,
156                         NULL
157                 };
158
159                 static const GInterfaceInfo tny_mime_part_view_info = 
160                 {
161                   (GInterfaceInitFunc) tny_mime_part_view_init, /* interface_init */
162                   NULL,         /* interface_finalize */
163                   NULL          /* interface_data */
164                 };
165
166                 static const GInterfaceInfo modest_mime_part_view_info = 
167                 {
168                   (GInterfaceInitFunc) modest_mime_part_view_init, /* interface_init */
169                   NULL,         /* interface_finalize */
170                   NULL          /* interface_data */
171                 };
172
173                 static const GInterfaceInfo modest_zoomable_info = 
174                 {
175                   (GInterfaceInitFunc) modest_zoomable_init, /* interface_init */
176                   NULL,         /* interface_finalize */
177                   NULL          /* interface_data */
178                 };
179
180                 static const GInterfaceInfo modest_isearch_view_info = 
181                 {
182                   (GInterfaceInitFunc) modest_isearch_view_init, /* interface_init */
183                   NULL,         /* interface_finalize */
184                   NULL          /* interface_data */
185                 };
186
187                 my_type = g_type_register_static (GTK_TYPE_HTML,
188                                                   "ModestGtkhtmlMimePartView",
189                                                   &my_info, 0);
190
191                 g_type_add_interface_static (my_type, TNY_TYPE_MIME_PART_VIEW, 
192                         &tny_mime_part_view_info);
193
194                 g_type_add_interface_static (my_type, MODEST_TYPE_MIME_PART_VIEW, 
195                         &modest_mime_part_view_info);
196
197                 g_type_add_interface_static (my_type, MODEST_TYPE_ZOOMABLE, 
198                         &modest_zoomable_info);
199                 g_type_add_interface_static (my_type, MODEST_TYPE_ISEARCH_VIEW, 
200                         &modest_isearch_view_info);
201         }
202         return my_type;
203 }
204
205 static void
206 modest_gtkhtml_mime_part_view_class_init (ModestGtkhtmlMimePartViewClass *klass)
207 {
208         GObjectClass *gobject_class;
209         GtkBindingSet *binding_set;
210
211         gobject_class = (GObjectClass*) klass;
212
213         parent_class            = g_type_class_peek_parent (klass);
214         gobject_class->dispose = modest_gtkhtml_mime_part_view_dispose;
215         gobject_class->finalize = modest_gtkhtml_mime_part_view_finalize;
216
217         klass->get_part_func = modest_gtkhtml_mime_part_view_get_part_default;
218         klass->set_part_func = modest_gtkhtml_mime_part_view_set_part_default;
219         klass->clear_func = modest_gtkhtml_mime_part_view_clear_default;
220         klass->is_empty_func = modest_gtkhtml_mime_part_view_is_empty_default;
221         klass->get_view_images_func = modest_gtkhtml_mime_part_view_get_view_images_default;
222         klass->set_view_images_func = modest_gtkhtml_mime_part_view_set_view_images_default;
223         klass->has_external_images_func = modest_gtkhtml_mime_part_view_has_external_images_default;
224         klass->get_zoom_func = modest_gtkhtml_mime_part_view_get_zoom_default;
225         klass->set_zoom_func = modest_gtkhtml_mime_part_view_set_zoom_default;
226         klass->zoom_minus_func = modest_gtkhtml_mime_part_view_zoom_minus_default;
227         klass->zoom_plus_func = modest_gtkhtml_mime_part_view_zoom_plus_default;
228         klass->search_func = modest_gtkhtml_mime_part_view_search_default;
229         klass->search_next_func = modest_gtkhtml_mime_part_view_search_next_default;
230         klass->get_selection_area_func = modest_gtkhtml_mime_part_view_get_selection_area_default;
231
232         binding_set = gtk_binding_set_by_class (klass);
233         gtk_binding_entry_skip (binding_set, GDK_Down, 0);
234         gtk_binding_entry_skip (binding_set, GDK_Up, 0);
235         gtk_binding_entry_skip (binding_set, GDK_KP_Up, 0);
236         gtk_binding_entry_skip (binding_set, GDK_KP_Down, 0);
237         gtk_binding_entry_skip (binding_set, GDK_Page_Down, 0);
238         gtk_binding_entry_skip (binding_set, GDK_Page_Up, 0);
239         gtk_binding_entry_skip (binding_set, GDK_KP_Page_Up, 0);
240         gtk_binding_entry_skip (binding_set, GDK_KP_Page_Down, 0);
241         gtk_binding_entry_skip (binding_set, GDK_Home, 0);
242         gtk_binding_entry_skip (binding_set, GDK_End, 0);
243         gtk_binding_entry_skip (binding_set, GDK_KP_Home, 0);
244         gtk_binding_entry_skip (binding_set, GDK_KP_End, 0);
245         
246         g_type_class_add_private (gobject_class, sizeof(ModestGtkhtmlMimePartViewPrivate));
247
248 }
249
250 static void    
251 modest_gtkhtml_mime_part_view_init (ModestGtkhtmlMimePartView *self)
252 {
253         ModestGtkhtmlMimePartViewPrivate *priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
254         GdkColor base;
255         GdkColor text;
256
257         gtk_html_set_editable        (GTK_HTML(self), FALSE);
258         gtk_html_allow_selection     (GTK_HTML(self), TRUE);
259         gtk_html_set_caret_mode      (GTK_HTML(self), FALSE);
260         gtk_html_set_blocking        (GTK_HTML(self), TRUE);
261         gtk_html_set_images_blocking (GTK_HTML(self), TRUE);
262
263         gdk_color_parse ("#fff", &base);
264         gdk_color_parse ("#000", &text);
265         gtk_widget_modify_base (GTK_WIDGET (self), GTK_STATE_NORMAL, &base);
266         gtk_widget_modify_text (GTK_WIDGET (self), GTK_STATE_NORMAL, &text);
267
268         g_signal_connect (G_OBJECT(self), "link_clicked",
269                           G_CALLBACK(on_link_clicked), self);
270         g_signal_connect (G_OBJECT(self), "url_requested",
271                           G_CALLBACK(on_url_requested), self);
272         g_signal_connect (G_OBJECT(self), "on_url",
273                           G_CALLBACK(on_url), self);
274         g_signal_connect (G_OBJECT(self), "notify::style",
275                           G_CALLBACK (on_notify_style), (gpointer) self);
276
277         priv->part = NULL;
278         priv->current_zoom = 1.0;
279         priv->view_images = FALSE;
280         priv->has_external_images = FALSE;
281 }
282
283 static void
284 modest_gtkhtml_mime_part_view_finalize (GObject *obj)
285 {
286         G_OBJECT_CLASS (parent_class)->finalize (obj);
287 }
288
289 static void
290 modest_gtkhtml_mime_part_view_dispose (GObject *obj)
291 {
292         ModestGtkhtmlMimePartViewPrivate *priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (obj);
293
294         if (priv->part) {
295                 g_object_unref (priv->part);
296                 priv->part = NULL;
297         }
298
299         G_OBJECT_CLASS (parent_class)->dispose (obj);
300 }
301
302 /* GTKHTML SIGNALS HANDLERS */
303
304 static void 
305 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
306 {
307         if (strcmp ("style", spec->name) == 0) {
308                 g_idle_add ((GSourceFunc) update_style, (gpointer) obj);
309                 gtk_widget_queue_draw (GTK_WIDGET (obj));
310         } 
311 }
312
313 gboolean
314 same_color (GdkColor *a, GdkColor *b)
315 {
316         return ((a->red == b->red) && 
317                 (a->green == b->green) && 
318                 (a->blue == b->blue));
319 }
320
321 static gboolean
322 update_style (ModestGtkhtmlMimePartView *self)
323 {
324         GdkColor base;
325         GdkColor text;
326         GtkRcStyle *rc_style;
327
328         rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (self));
329
330         gdk_color_parse ("#fff", &base);
331         gdk_color_parse ("#000", &text);
332
333         if (!same_color (&(rc_style->base[GTK_STATE_NORMAL]), &base) &&
334             !same_color (&(rc_style->text[GTK_STATE_NORMAL]), &text)) {
335
336                 rc_style->base[GTK_STATE_NORMAL] = base;
337                 rc_style->text[GTK_STATE_NORMAL] = text;
338                 gtk_widget_modify_style (GTK_WIDGET (self), rc_style);
339
340         }
341
342         return FALSE;
343 }
344
345
346
347 static gboolean
348 on_link_clicked (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMimePartView *self)
349 {
350         gboolean result;
351         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE);
352
353         g_signal_emit_by_name (G_OBJECT (self), "activate-link", uri, &result);
354         return result;
355 }
356
357 static gboolean
358 on_url (GtkWidget *widget, const gchar *uri, ModestGtkhtmlMimePartView *self)
359 {
360         gboolean result;
361         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE);
362
363         g_signal_emit_by_name (G_OBJECT (self), "link-hover", uri, &result);
364         return result;
365 }
366
367 typedef struct {
368         gpointer buffer;
369         GtkHTML *html;
370         GtkHTMLStream *stream;
371 } ImageFetcherInfo;
372
373 static gboolean
374 on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream, 
375                   ModestGtkhtmlMimePartView *self)
376 {
377         gboolean result;
378         TnyStream *tny_stream;
379         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE);
380
381         if (g_str_has_prefix (uri, "http:")) {
382                 ModestGtkhtmlMimePartViewPrivate *priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
383
384                 if (!priv->view_images)
385                         priv->has_external_images = TRUE;
386         }
387                         
388         tny_stream = TNY_STREAM (modest_tny_stream_gtkhtml_new (stream, GTK_HTML (widget)));
389         g_signal_emit_by_name (MODEST_MIME_PART_VIEW (self), "fetch-url", uri, tny_stream, &result);
390         g_object_unref (tny_stream);
391         return result;
392 }
393
394 /* INTERNAL API */
395
396 static void
397 set_html_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part)
398 {
399         GtkHTMLStream *gtkhtml_stream;
400         TnyStream *tny_stream;  
401         
402         g_return_if_fail (self);
403         g_return_if_fail (part);
404         
405         gtkhtml_stream = gtk_html_begin(GTK_HTML(self));
406
407         tny_stream     = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream, GTK_HTML (self)));
408         tny_stream_reset (tny_stream);
409
410         tny_mime_part_decode_to_stream ((TnyMimePart*)part, tny_stream, NULL);
411         tny_stream_close (tny_stream);
412         g_object_unref (G_OBJECT(tny_stream));
413 }
414
415 static void
416 set_text_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part)
417 {
418         TnyStream* text_to_html_stream, *tny_stream;
419         GtkHTMLStream *gtkhtml_stream;
420         
421         g_return_if_fail (self);
422         g_return_if_fail (part);
423
424         gtkhtml_stream = gtk_html_begin(GTK_HTML(self)); 
425         tny_stream =  TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream, GTK_HTML (self)));
426         text_to_html_stream = TNY_STREAM (modest_stream_text_to_html_new (tny_stream));
427         modest_stream_text_to_html_set_linkify_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream), 64*1024);
428         modest_stream_text_to_html_set_full_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream), 640*1024);
429         
430         // FIXME: tinymail
431         tny_mime_part_decode_to_stream ((TnyMimePart*)part, text_to_html_stream, NULL);
432         tny_stream_write (text_to_html_stream, "\n", 1);
433         tny_stream_reset (text_to_html_stream);         
434         tny_stream_close (text_to_html_stream);
435         
436         g_object_unref (G_OBJECT(text_to_html_stream));
437         g_object_unref (G_OBJECT(tny_stream));
438         /* gtk_html_stream_destroy (gtkhtml_stream); */
439 }
440
441 static void
442 set_empty_part (ModestGtkhtmlMimePartView *self)
443 {
444         g_return_if_fail (self);
445
446         gtk_html_load_from_string (GTK_HTML(self),
447                                    "", 1);
448 }
449
450 static void
451 set_part (ModestGtkhtmlMimePartView *self, TnyMimePart *part)
452 {
453         ModestGtkhtmlMimePartViewPrivate *priv;
454
455         g_return_if_fail (self);
456         
457         priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE(self);
458         priv->has_external_images = FALSE;
459
460         if (part != priv->part) {
461                 if (priv->part)
462                         g_object_unref (G_OBJECT(priv->part));
463                 if (part)
464                         g_object_ref   (G_OBJECT(part));
465                 priv->part = part;
466         }
467         
468         if (!part) {
469                 set_empty_part (self);
470                 return;
471         }
472
473         if (tny_mime_part_content_type_is (part, "text/html")) {
474                 set_html_part (self, part);
475         } else {
476                 if (tny_mime_part_content_type_is (part, "message/rfc822")) {
477                         gchar *header_content_type, *header_content_type_lower;
478                         header_content_type = modest_tny_mime_part_get_header_value (part, "Content-Type");
479                         if (header_content_type) {
480                                 header_content_type = g_strstrip (header_content_type);
481                                 header_content_type_lower = g_ascii_strdown (header_content_type, -1);
482
483                                 if (!g_ascii_strcasecmp (header_content_type_lower, "text/html"))
484                                         set_html_part (self, part);
485                                 else 
486                                         set_text_part (self, part);
487
488                                 g_free (header_content_type_lower);
489                                 g_free (header_content_type);
490                         } else {
491                                 set_text_part (self, part);
492                         }
493                 } else {
494                         set_text_part (self, part);
495                 }
496         }
497
498 }
499
500 static TnyMimePart*
501 get_part (ModestGtkhtmlMimePartView *self)
502 {
503         TnyMimePart *part;
504
505         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), NULL);
506
507         part = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE(self)->part;
508
509         if (part)
510                 g_object_ref (part);
511         
512         return part;
513 }
514
515 static gboolean
516 has_contents_receiver (gpointer engine, const gchar *data,
517                        size_t len, gboolean *has_contents)
518 {
519         if (len > 1 || ((len == 1)&&(data[0]!='\n'))) {
520                 *has_contents = TRUE;
521                 return FALSE;
522         }
523         return TRUE;
524 }
525
526 static gboolean      
527 is_empty   (ModestGtkhtmlMimePartView *self)
528 {
529         /* TODO: Find some gtkhtml API to check whether there is any (visible, non markup)
530          * text in the message:
531          */
532         gboolean has_contents = FALSE;
533
534         gtk_html_export (GTK_HTML (self), "text/plain", 
535                          (GtkHTMLSaveReceiverFn) has_contents_receiver, &has_contents);
536         
537         return !has_contents;
538 }
539
540 static gboolean      
541 get_view_images   (ModestGtkhtmlMimePartView *self)
542 {
543         ModestGtkhtmlMimePartViewPrivate *priv;
544
545         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE);
546
547         priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
548         return priv->view_images;
549 }
550
551 static void
552 set_view_images   (ModestGtkhtmlMimePartView *self, gboolean view_images)
553 {
554         ModestGtkhtmlMimePartViewPrivate *priv;
555
556         g_return_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self));
557
558         priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
559         priv->view_images = view_images;
560 }
561
562 static gboolean      
563 has_external_images   (ModestGtkhtmlMimePartView *self)
564 {
565         ModestGtkhtmlMimePartViewPrivate *priv;
566
567         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), FALSE);
568
569         priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
570         return priv->has_external_images;
571 }
572
573 static void
574 set_zoom (ModestGtkhtmlMimePartView *self, gdouble zoom)
575 {
576         ModestGtkhtmlMimePartViewPrivate *priv;
577
578         g_return_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self));
579
580         priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
581         priv->current_zoom = zoom;
582         gtk_html_set_magnification (GTK_HTML(self), zoom);
583
584         gtk_widget_queue_resize (GTK_WIDGET (self));
585 }
586
587 static gdouble
588 get_zoom (ModestGtkhtmlMimePartView *self)
589 {
590         ModestGtkhtmlMimePartViewPrivate *priv;
591
592         g_return_val_if_fail (MODEST_IS_GTKHTML_MIME_PART_VIEW (self), 1.0);
593
594         priv = MODEST_GTKHTML_MIME_PART_VIEW_GET_PRIVATE (self);
595
596         return priv->current_zoom;
597 }
598
599 static gboolean
600 search (ModestGtkhtmlMimePartView *self, 
601         const gchar *string)
602 {
603         return gtk_html_engine_search (GTK_HTML (self),
604                                        string,
605                                        FALSE,   /* case sensitive */
606                                        TRUE,    /* forward */
607                                        FALSE);  /* regexp */
608 }
609
610 static gboolean
611 search_next (ModestGtkhtmlMimePartView *self)
612 {
613         return gtk_html_engine_search_next (GTK_HTML (self));
614 }
615
616 static gboolean
617 get_selection_area (ModestGtkhtmlMimePartView *self, 
618                     gint *x, gint *y,
619                     gint *width, gint *height)
620 {
621 #ifdef HAVE_GTK_HTML_GET_SELECTION_AREA
622         gtk_html_get_selection_area (GTK_HTML (self), x, y, width, height);
623         return TRUE;
624 #else
625         return FALSE;
626 #endif
627 }
628
629
630 /* TNY MIME PART IMPLEMENTATION */
631
632 static void
633 tny_mime_part_view_init (gpointer g, gpointer iface_data)
634 {
635         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
636
637         klass->get_part = modest_gtkhtml_mime_part_view_get_part;
638         klass->set_part = modest_gtkhtml_mime_part_view_set_part;
639         klass->clear = modest_gtkhtml_mime_part_view_clear;
640
641         return;
642 }
643
644 static TnyMimePart* 
645 modest_gtkhtml_mime_part_view_get_part (TnyMimePartView *self)
646 {
647         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->get_part_func (self);
648 }
649
650
651 static TnyMimePart* 
652 modest_gtkhtml_mime_part_view_get_part_default (TnyMimePartView *self)
653 {
654         return TNY_MIME_PART (get_part (MODEST_GTKHTML_MIME_PART_VIEW (self)));
655 }
656
657 static void
658 modest_gtkhtml_mime_part_view_set_part (TnyMimePartView *self,
659                                         TnyMimePart *part)
660 {
661         MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->set_part_func (self, part);
662 }
663
664 static void
665 modest_gtkhtml_mime_part_view_set_part_default (TnyMimePartView *self,
666                                                 TnyMimePart *part)
667 {
668         g_return_if_fail ((part == NULL) || TNY_IS_MIME_PART (part));
669
670         set_part (MODEST_GTKHTML_MIME_PART_VIEW (self), part);
671 }
672
673 static void
674 modest_gtkhtml_mime_part_view_clear (TnyMimePartView *self)
675 {
676         MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->clear_func (self);
677 }
678
679 static void
680 modest_gtkhtml_mime_part_view_clear_default (TnyMimePartView *self)
681 {
682         set_part (MODEST_GTKHTML_MIME_PART_VIEW (self), NULL);
683 }
684
685 /* MODEST MIME PART VIEW IMPLEMENTATION */
686
687 static void
688 modest_mime_part_view_init (gpointer g, gpointer iface_data)
689 {
690         ModestMimePartViewIface *klass = (ModestMimePartViewIface *)g;
691
692         klass->is_empty_func = modest_gtkhtml_mime_part_view_is_empty;
693         klass->get_view_images_func = modest_gtkhtml_mime_part_view_get_view_images;
694         klass->set_view_images_func = modest_gtkhtml_mime_part_view_set_view_images;
695         klass->has_external_images_func = modest_gtkhtml_mime_part_view_has_external_images;
696
697         return;
698 }
699
700 static gboolean
701 modest_gtkhtml_mime_part_view_is_empty (ModestMimePartView *self)
702 {
703         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->is_empty_func (self);
704 }
705
706 static gboolean
707 modest_gtkhtml_mime_part_view_get_view_images (ModestMimePartView *self)
708 {
709         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->get_view_images_func (self);
710 }
711
712 static void
713 modest_gtkhtml_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images)
714 {
715         MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->set_view_images_func (self, view_images);
716 }
717
718 static gboolean
719 modest_gtkhtml_mime_part_view_has_external_images (ModestMimePartView *self)
720 {
721         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->has_external_images_func (self);
722 }
723
724 static gboolean
725 modest_gtkhtml_mime_part_view_is_empty_default (ModestMimePartView *self)
726 {
727         return is_empty (MODEST_GTKHTML_MIME_PART_VIEW (self));
728 }
729
730 static gboolean
731 modest_gtkhtml_mime_part_view_get_view_images_default (ModestMimePartView *self)
732 {
733         return get_view_images (MODEST_GTKHTML_MIME_PART_VIEW (self));
734 }
735
736 static void
737 modest_gtkhtml_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images)
738 {
739         set_view_images (MODEST_GTKHTML_MIME_PART_VIEW (self), view_images);
740 }
741
742 static gboolean
743 modest_gtkhtml_mime_part_view_has_external_images_default (ModestMimePartView *self)
744 {
745         return has_external_images (MODEST_GTKHTML_MIME_PART_VIEW (self));
746 }
747
748
749 /* MODEST ZOOMABLE IMPLEMENTATION */
750 static void
751 modest_zoomable_init (gpointer g, gpointer iface_data)
752 {
753         ModestZoomableIface *klass = (ModestZoomableIface *)g;
754         
755         klass->get_zoom_func = modest_gtkhtml_mime_part_view_get_zoom;
756         klass->set_zoom_func = modest_gtkhtml_mime_part_view_set_zoom;
757         klass->zoom_minus_func = modest_gtkhtml_mime_part_view_zoom_minus;
758         klass->zoom_plus_func = modest_gtkhtml_mime_part_view_zoom_plus;
759
760         return;
761 }
762
763 static gdouble
764 modest_gtkhtml_mime_part_view_get_zoom (ModestZoomable *self)
765 {
766         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->get_zoom_func (self);
767 }
768
769 static gdouble
770 modest_gtkhtml_mime_part_view_get_zoom_default (ModestZoomable *self)
771 {
772         return get_zoom (MODEST_GTKHTML_MIME_PART_VIEW (self));
773 }
774
775 static void
776 modest_gtkhtml_mime_part_view_set_zoom (ModestZoomable *self, gdouble value)
777 {
778         MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->set_zoom_func (self, value);
779 }
780
781 static void
782 modest_gtkhtml_mime_part_view_set_zoom_default (ModestZoomable *self, gdouble value)
783 {
784         set_zoom (MODEST_GTKHTML_MIME_PART_VIEW (self), value);
785 }
786
787 static gboolean
788 modest_gtkhtml_mime_part_view_zoom_minus (ModestZoomable *self)
789 {
790         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->zoom_minus_func (self);
791 }
792
793 static gboolean
794 modest_gtkhtml_mime_part_view_zoom_minus_default (ModestZoomable *self)
795 {
796         /* operation not supported in ModestGtkhtmlMimePartView */
797         return FALSE;
798 }
799
800 static gboolean
801 modest_gtkhtml_mime_part_view_zoom_plus (ModestZoomable *self)
802 {
803         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->zoom_plus_func (self);
804 }
805
806 static gboolean
807 modest_gtkhtml_mime_part_view_zoom_plus_default (ModestZoomable *self)
808 {
809         /* operation not supported in ModestGtkhtmlMimePartView */
810         return FALSE;
811 }
812
813 /* ISEARCH VIEW IMPLEMENTATION */
814 static void
815 modest_isearch_view_init (gpointer g, gpointer iface_data)
816 {
817         ModestISearchViewIface *klass = (ModestISearchViewIface *)g;
818         
819         klass->search_func = modest_gtkhtml_mime_part_view_search;
820         klass->search_next_func = modest_gtkhtml_mime_part_view_search_next;
821         klass->get_selection_area_func = modest_gtkhtml_mime_part_view_get_selection_area;
822
823         return;
824 }
825
826 static gboolean 
827 modest_gtkhtml_mime_part_view_search (ModestISearchView *self, const gchar *string)
828 {
829         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->search_func (self, string);
830 }
831
832 static gboolean 
833 modest_gtkhtml_mime_part_view_search_default (ModestISearchView *self, const gchar *string)
834 {
835         return search (MODEST_GTKHTML_MIME_PART_VIEW (self), string);
836 }
837
838 static gboolean 
839 modest_gtkhtml_mime_part_view_search_next(ModestISearchView *self)
840 {
841         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->search_next_func (self);
842 }
843
844 static gboolean 
845 modest_gtkhtml_mime_part_view_search_next_default (ModestISearchView *self)
846 {
847         return search_next (MODEST_GTKHTML_MIME_PART_VIEW (self));
848 }
849
850 static gboolean 
851 modest_gtkhtml_mime_part_view_get_selection_area (ModestISearchView *self, gint *x, gint *y, 
852                                                   gint *width, gint *height)
853 {
854         return MODEST_GTKHTML_MIME_PART_VIEW_GET_CLASS (self)->get_selection_area_func (self, x, y, width, height);
855 }
856
857 static gboolean 
858 modest_gtkhtml_mime_part_view_get_selection_area_default (ModestISearchView *self, gint *x, gint *y, 
859                                                           gint *width, gint *height)
860 {
861         return get_selection_area (MODEST_GTKHTML_MIME_PART_VIEW (self), x, y, width, height);
862 }