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