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