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