Modified webpage: now tinymail repository is in gitorious.
[modest] / src / widgets / modest-webkit-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-webkit-mime-part-view.h>
32 #include <string.h>
33 #include <webkit/webkit.h>
34 #include <tny-stream.h>
35 #include <tny-mime-part-view.h>
36 #include "modest-tny-mime-part.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 <libgnomevfs/gnome-vfs.h>
44 #include <gdk/gdkkeysyms.h>
45 #include <modest-ui-constants.h>
46 #include <modest-tny-stream-webkit.h>
47
48 /* gobject structure methods */
49 static void    modest_webkit_mime_part_view_class_init (ModestWebkitMimePartViewClass *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_webkit_mime_part_view_init       (ModestWebkitMimePartView *self);
55 static void    modest_webkit_mime_part_view_finalize   (GObject *self);
56 static void    modest_webkit_mime_part_view_dispose    (GObject *self);
57
58 /* Webkit signal handlers */
59 static void on_resource_request_starting (WebKitWebView *webview,
60                                           WebKitWebFrame *frame,
61                                           WebKitWebResource *resource,
62                                           WebKitNetworkRequest *request,
63                                           WebKitNetworkResponse *response,
64                                           gpointer userdata);
65 static gboolean on_new_window_policy_decision_requested (WebKitWebView             *web_view,
66                                                          WebKitWebFrame            *frame,
67                                                          WebKitNetworkRequest      *request,
68                                                          WebKitWebNavigationAction *navigation_action,
69                                                          WebKitWebPolicyDecision   *policy_decision,
70                                                          gpointer                   user_data);
71 static gboolean on_navigation_policy_decision_requested (WebKitWebView             *web_view,
72                                                          WebKitWebFrame            *frame,
73                                                          WebKitNetworkRequest      *request,
74                                                          WebKitWebNavigationAction *navigation_action,
75                                                          WebKitWebPolicyDecision   *policy_decision,
76                                                          gpointer                   user_data);
77 static WebKitNavigationResponse on_navigation_requested                      (WebKitWebView        *web_view,
78                                                                               WebKitWebFrame       *frame,
79                                                                               WebKitNetworkRequest *request,
80                                                                               gpointer              user_data);
81 static void      on_notify_style  (GObject *obj, GParamSpec *spec, gpointer userdata);
82 static gboolean  update_style     (ModestWebkitMimePartView *self);
83 /* TnyMimePartView implementation */
84 static void modest_webkit_mime_part_view_clear (TnyMimePartView *self);
85 static void modest_webkit_mime_part_view_clear_default (TnyMimePartView *self);
86 static void modest_webkit_mime_part_view_set_part (TnyMimePartView *self, TnyMimePart *part);
87 static void modest_webkit_mime_part_view_set_part_default (TnyMimePartView *self, TnyMimePart *part);
88 static TnyMimePart* modest_webkit_mime_part_view_get_part (TnyMimePartView *self);
89 static TnyMimePart* modest_webkit_mime_part_view_get_part_default (TnyMimePartView *self);
90 /* ModestMimePartView implementation */
91 static gboolean modest_webkit_mime_part_view_is_empty (ModestMimePartView *self);
92 static gboolean modest_webkit_mime_part_view_is_empty_default (ModestMimePartView *self);
93 static gboolean modest_webkit_mime_part_view_get_view_images (ModestMimePartView *self);
94 static gboolean modest_webkit_mime_part_view_get_view_images_default (ModestMimePartView *self);
95 static void     modest_webkit_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images);
96 static void     modest_webkit_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images);
97 static gboolean modest_webkit_mime_part_view_has_external_images (ModestMimePartView *self);
98 static gboolean modest_webkit_mime_part_view_has_external_images_default (ModestMimePartView *self);
99 /* ModestZoomable implementation */
100 static gdouble modest_webkit_mime_part_view_get_zoom (ModestZoomable *self);
101 static void modest_webkit_mime_part_view_set_zoom (ModestZoomable *self, gdouble value);
102 static gboolean modest_webkit_mime_part_view_zoom_minus (ModestZoomable *self);
103 static gboolean modest_webkit_mime_part_view_zoom_plus (ModestZoomable *self);
104 static gdouble modest_webkit_mime_part_view_get_zoom_default (ModestZoomable *self);
105 static void modest_webkit_mime_part_view_set_zoom_default (ModestZoomable *self, gdouble value);
106 static gboolean modest_webkit_mime_part_view_zoom_minus_default (ModestZoomable *self);
107 static gboolean modest_webkit_mime_part_view_zoom_plus_default (ModestZoomable *self);
108 /* ModestISearchView implementation */
109 static gboolean modest_webkit_mime_part_view_search                    (ModestISearchView *self, const gchar *string);
110 static gboolean modest_webkit_mime_part_view_search_next               (ModestISearchView *self);
111 static gboolean modest_webkit_mime_part_view_get_selection_area        (ModestISearchView *self, gint *x, gint *y, 
112                                                                          gint *width, gint *height);
113 static gboolean modest_webkit_mime_part_view_search_default            (ModestISearchView *self, const gchar *string);
114 static gboolean modest_webkit_mime_part_view_search_next_default       (ModestISearchView *self);
115 static gboolean modest_webkit_mime_part_view_get_selection_area_default (ModestISearchView *self, gint *x, gint *y, 
116                                                                           gint *width, gint *height);
117
118
119 /* internal api */
120 static TnyMimePart  *get_part   (ModestWebkitMimePartView *self);
121 static void          set_html_part   (ModestWebkitMimePartView *self, TnyMimePart *part, const gchar *encoding);
122 static void          set_text_part   (ModestWebkitMimePartView *self, TnyMimePart *part);
123 static void          set_empty_part  (ModestWebkitMimePartView *self);
124 static void          set_part   (ModestWebkitMimePartView *self, TnyMimePart *part);
125 static gboolean      is_empty   (ModestWebkitMimePartView *self);
126 static gboolean      get_view_images   (ModestWebkitMimePartView *self);
127 static void          set_view_images   (ModestWebkitMimePartView *self, gboolean view_images);
128 static gboolean      has_external_images   (ModestWebkitMimePartView *self);
129 static void          set_zoom   (ModestWebkitMimePartView *self, gdouble zoom);
130 static gdouble       get_zoom   (ModestWebkitMimePartView *self);
131 static gboolean      search             (ModestWebkitMimePartView *self, const gchar *string);
132 static gboolean      search_next        (ModestWebkitMimePartView *self);
133 static gboolean      get_selection_area (ModestWebkitMimePartView *self, gint *x, gint *y,
134                                          gint *width, gint *height);
135
136 typedef struct _ModestWebkitMimePartViewPrivate ModestWebkitMimePartViewPrivate;
137 struct _ModestWebkitMimePartViewPrivate {
138         TnyMimePart *part;
139         gdouble current_zoom;
140         gboolean view_images;
141         gboolean has_external_images;
142         GSList *sighandlers;
143         gchar *last_search;
144 };
145
146 #define MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
147                                                                                        MODEST_TYPE_WEBKIT_MIME_PART_VIEW, \
148                                                                                        ModestWebkitMimePartViewPrivate))
149
150 enum {
151         STOP_STREAMS_SIGNAL,
152         LIMIT_ERROR_SIGNAL,
153         LAST_SIGNAL
154 };
155
156 static WebKitWebViewClass *parent_class = NULL;
157
158 static guint signals[LAST_SIGNAL] = {0};
159
160 GtkWidget *
161 modest_webkit_mime_part_view_new ()
162 {
163         return g_object_new (MODEST_TYPE_WEBKIT_MIME_PART_VIEW, NULL);
164 }
165
166 /* GOBJECT IMPLEMENTATION */
167 GType
168 modest_webkit_mime_part_view_get_type (void)
169 {
170         static GType my_type = 0;
171         if (!my_type) {
172                 static const GTypeInfo my_info = {
173                         sizeof(ModestWebkitMimePartViewClass),
174                         NULL,           /* base init */
175                         NULL,           /* base finalize */
176                         (GClassInitFunc) modest_webkit_mime_part_view_class_init,
177                         NULL,           /* class finalize */
178                         NULL,           /* class data */
179                         sizeof(ModestWebkitMimePartView),
180                         1,              /* n_preallocs */
181                         (GInstanceInitFunc) modest_webkit_mime_part_view_init,
182                         NULL
183                 };
184
185                 static const GInterfaceInfo tny_mime_part_view_info = 
186                 {
187                   (GInterfaceInitFunc) tny_mime_part_view_init, /* interface_init */
188                   NULL,         /* interface_finalize */
189                   NULL          /* interface_data */
190                 };
191
192                 static const GInterfaceInfo modest_mime_part_view_info = 
193                 {
194                   (GInterfaceInitFunc) modest_mime_part_view_init, /* interface_init */
195                   NULL,         /* interface_finalize */
196                   NULL          /* interface_data */
197                 };
198
199                 static const GInterfaceInfo modest_zoomable_info = 
200                 {
201                   (GInterfaceInitFunc) modest_zoomable_init, /* interface_init */
202                   NULL,         /* interface_finalize */
203                   NULL          /* interface_data */
204                 };
205
206                 static const GInterfaceInfo modest_isearch_view_info = 
207                 {
208                   (GInterfaceInitFunc) modest_isearch_view_init, /* interface_init */
209                   NULL,         /* interface_finalize */
210                   NULL          /* interface_data */
211                 };
212
213                 my_type = g_type_register_static (WEBKIT_TYPE_WEB_VIEW,
214                                                   "ModestWebkitMimePartView",
215                                                   &my_info, 0);
216
217                 g_type_add_interface_static (my_type, TNY_TYPE_MIME_PART_VIEW, 
218                         &tny_mime_part_view_info);
219
220                 g_type_add_interface_static (my_type, MODEST_TYPE_MIME_PART_VIEW, 
221                         &modest_mime_part_view_info);
222
223                 g_type_add_interface_static (my_type, MODEST_TYPE_ZOOMABLE, 
224                         &modest_zoomable_info);
225                 g_type_add_interface_static (my_type, MODEST_TYPE_ISEARCH_VIEW, 
226                         &modest_isearch_view_info);
227         }
228         return my_type;
229 }
230
231 static void
232 modest_webkit_mime_part_view_class_init (ModestWebkitMimePartViewClass *klass)
233 {
234         GObjectClass *gobject_class;
235         GtkBindingSet *binding_set;
236
237         gobject_class = (GObjectClass*) klass;
238
239         parent_class            = g_type_class_peek_parent (klass);
240         gobject_class->dispose = modest_webkit_mime_part_view_dispose;
241         gobject_class->finalize = modest_webkit_mime_part_view_finalize;
242
243         klass->get_part_func = modest_webkit_mime_part_view_get_part_default;
244         klass->set_part_func = modest_webkit_mime_part_view_set_part_default;
245         klass->clear_func = modest_webkit_mime_part_view_clear_default;
246         klass->is_empty_func = modest_webkit_mime_part_view_is_empty_default;
247         klass->get_view_images_func = modest_webkit_mime_part_view_get_view_images_default;
248         klass->set_view_images_func = modest_webkit_mime_part_view_set_view_images_default;
249         klass->has_external_images_func = modest_webkit_mime_part_view_has_external_images_default;
250         klass->get_zoom_func = modest_webkit_mime_part_view_get_zoom_default;
251         klass->set_zoom_func = modest_webkit_mime_part_view_set_zoom_default;
252         klass->zoom_minus_func = modest_webkit_mime_part_view_zoom_minus_default;
253         klass->zoom_plus_func = modest_webkit_mime_part_view_zoom_plus_default;
254         klass->search_func = modest_webkit_mime_part_view_search_default;
255         klass->search_next_func = modest_webkit_mime_part_view_search_next_default;
256         klass->get_selection_area_func = modest_webkit_mime_part_view_get_selection_area_default;
257
258         binding_set = gtk_binding_set_by_class (klass);
259         gtk_binding_entry_skip (binding_set, GDK_Down, 0);
260         gtk_binding_entry_skip (binding_set, GDK_Up, 0);
261         gtk_binding_entry_skip (binding_set, GDK_KP_Up, 0);
262         gtk_binding_entry_skip (binding_set, GDK_KP_Down, 0);
263         gtk_binding_entry_skip (binding_set, GDK_Page_Down, 0);
264         gtk_binding_entry_skip (binding_set, GDK_Page_Up, 0);
265         gtk_binding_entry_skip (binding_set, GDK_KP_Page_Up, 0);
266         gtk_binding_entry_skip (binding_set, GDK_KP_Page_Down, 0);
267         gtk_binding_entry_skip (binding_set, GDK_Home, 0);
268         gtk_binding_entry_skip (binding_set, GDK_End, 0);
269         gtk_binding_entry_skip (binding_set, GDK_KP_Home, 0);
270         gtk_binding_entry_skip (binding_set, GDK_KP_End, 0);
271
272         g_type_class_add_private (gobject_class, sizeof(ModestWebkitMimePartViewPrivate));
273
274         signals[STOP_STREAMS_SIGNAL] = 
275                 g_signal_new ("stop-streams",
276                               G_TYPE_FROM_CLASS (gobject_class),
277                               G_SIGNAL_RUN_FIRST,
278                               G_STRUCT_OFFSET (ModestWebkitMimePartViewClass,stop_streams),
279                               NULL, NULL,
280                               g_cclosure_marshal_VOID__VOID,
281                               G_TYPE_NONE, 0);
282
283         signals[LIMIT_ERROR_SIGNAL] = 
284                 g_signal_new ("limit-error",
285                               G_TYPE_FROM_CLASS (gobject_class),
286                               G_SIGNAL_RUN_FIRST,
287                               G_STRUCT_OFFSET (ModestWebkitMimePartViewClass,limit_error),
288                               NULL, NULL,
289                               g_cclosure_marshal_VOID__VOID,
290                               G_TYPE_NONE, 0);
291
292 }
293
294 static void
295 modest_webkit_mime_part_view_init (ModestWebkitMimePartView *self)
296 {
297         ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
298         GdkColor base;
299         GdkColor text;
300         WebKitWebSettings *settings;
301
302         gdk_color_parse ("#fff", &base);
303         gdk_color_parse ("#000", &text);
304         gtk_widget_modify_base (GTK_WIDGET (self), GTK_STATE_NORMAL, &base);
305         gtk_widget_modify_text (GTK_WIDGET (self), GTK_STATE_NORMAL, &text);
306
307         priv->sighandlers = NULL;
308
309         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
310                                                        G_OBJECT(self), "notify::style",
311                                                        G_CALLBACK (on_notify_style), (gpointer) self);
312         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
313                                                        G_OBJECT (self), "resource-request-starting",
314                                                        G_CALLBACK (on_resource_request_starting), (gpointer) self);
315         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
316                                                        G_OBJECT (self), "navigation-policy-decision-requested",
317                                                        G_CALLBACK (on_navigation_policy_decision_requested), (gpointer) self);
318         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
319                                                        G_OBJECT (self), "new-window-policy-decision-requested",
320                                                        G_CALLBACK (on_new_window_policy_decision_requested), (gpointer) self);
321         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
322                                                        G_OBJECT (self), "navigation-requested",
323                                                        G_CALLBACK (on_navigation_requested), (gpointer) self);
324
325         priv->part = NULL;
326         priv->last_search = NULL;
327         priv->current_zoom = 1.0;
328         priv->view_images = FALSE;
329         priv->has_external_images = FALSE;
330
331         settings = webkit_web_settings_new ();
332         g_object_set (G_OBJECT (settings),
333                       "auto-load-images", FALSE,
334                       "enable-html5-database", FALSE,
335                       "enable-html5-local-storage", FALSE, 
336                       "enable-offline-web-application-cache", FALSE,
337                       "enable-plugins", FALSE,
338                       "enable-private-browsing", TRUE,
339                       "enable-scripts", FALSE,
340                       NULL);
341         webkit_web_view_set_settings (WEBKIT_WEB_VIEW (self), settings);
342         g_object_unref (settings);
343         g_object_set (G_OBJECT (self), 
344                       "editable", FALSE,
345                       NULL);
346 }
347
348 static void
349 modest_webkit_mime_part_view_finalize (GObject *obj)
350 {
351         ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (obj);
352
353         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
354         priv->sighandlers = NULL;
355         g_free (priv->last_search);
356
357         G_OBJECT_CLASS (parent_class)->finalize (obj);
358 }
359
360 static void
361 modest_webkit_mime_part_view_dispose (GObject *obj)
362 {
363         ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (obj);
364
365         g_signal_emit (G_OBJECT (obj), signals[STOP_STREAMS_SIGNAL], 0);
366
367         if (priv->part) {
368                 g_object_unref (priv->part);
369                 priv->part = NULL;
370         }
371
372         G_OBJECT_CLASS (parent_class)->dispose (obj);
373 }
374
375 /* WEBKIT SIGNALS HANDLERS */
376
377 static WebKitNavigationResponse
378 on_navigation_requested                      (WebKitWebView        *web_view,
379                                               WebKitWebFrame       *frame,
380                                               WebKitNetworkRequest *request,
381                                               gpointer              user_data)
382 {
383         const gchar *uri;
384
385         uri = webkit_network_request_get_uri (request);
386         if (g_strcmp0 (uri, "about:blank") == 0) {
387                 return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
388         } else if (g_str_has_prefix (uri, "cid:") == 0) {
389                 return WEBKIT_NAVIGATION_RESPONSE_DOWNLOAD;
390         } else {
391                 return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
392         }
393 }
394
395 static void
396 on_resource_request_starting (WebKitWebView *webview,
397                               WebKitWebFrame *frame,
398                               WebKitWebResource *resource,
399                               WebKitNetworkRequest *request,
400                               WebKitNetworkResponse *response,
401                               gpointer userdata)
402 {
403         ModestWebkitMimePartView *self = (ModestWebkitMimePartView *) userdata;
404         g_return_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self));
405
406         if (g_str_has_prefix (webkit_network_request_get_uri (request), "http:")) {
407                 ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
408
409                 if (!priv->view_images)
410                         priv->has_external_images = TRUE;
411         }
412
413         webkit_network_request_set_uri (request, "about:blank");
414 }
415
416 static gboolean
417 on_navigation_policy_decision_requested (WebKitWebView             *web_view,
418                                          WebKitWebFrame            *frame,
419                                          WebKitNetworkRequest      *request,
420                                          WebKitWebNavigationAction *navigation_action,
421                                          WebKitWebPolicyDecision   *policy_decision,
422                                          gpointer                   user_data)
423 {
424         WebKitWebNavigationReason reason;
425         reason = webkit_web_navigation_action_get_reason (navigation_action);
426         if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
427                 const gchar *uri;
428                 gboolean result;
429
430                 webkit_web_policy_decision_ignore (policy_decision);
431                 uri = webkit_network_request_get_uri (WEBKIT_NETWORK_REQUEST (request));
432                 g_signal_emit_by_name (G_OBJECT (user_data), "activate-link", uri, &result);
433
434                 return TRUE;
435         } else if (reason != WEBKIT_WEB_NAVIGATION_REASON_OTHER) {
436                 webkit_web_policy_decision_ignore (policy_decision);
437                 return TRUE;
438         }
439         return FALSE;
440 }
441
442 static gboolean
443 on_new_window_policy_decision_requested (WebKitWebView             *web_view,
444                                          WebKitWebFrame            *frame,
445                                          WebKitNetworkRequest      *request,
446                                          WebKitWebNavigationAction *navigation_action,
447                                          WebKitWebPolicyDecision   *policy_decision,
448                                          gpointer                   user_data)
449 {
450         WebKitWebNavigationReason reason;
451         reason = webkit_web_navigation_action_get_reason (navigation_action);
452         if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
453                 const gchar *uri;
454                 gboolean result;
455
456                 webkit_web_policy_decision_ignore (policy_decision);
457                 uri = webkit_network_request_get_uri (WEBKIT_NETWORK_REQUEST (request));
458                 g_signal_emit_by_name (G_OBJECT (user_data), "activate-link", uri, &result);
459
460                 return TRUE;
461         } else if (reason != WEBKIT_WEB_NAVIGATION_REASON_OTHER) {
462                 webkit_web_policy_decision_ignore (policy_decision);
463                 return TRUE;
464         }
465         return FALSE;
466 }
467
468 static void 
469 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
470 {
471         if (strcmp ("style", spec->name) == 0) {
472                 g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) update_style, 
473                                  g_object_ref (obj), g_object_unref);
474                 gtk_widget_queue_draw (GTK_WIDGET (obj));
475         }
476 }
477
478 gboolean
479 same_color (GdkColor *a, GdkColor *b)
480 {
481         return ((a->red == b->red) && 
482                 (a->green == b->green) && 
483                 (a->blue == b->blue));
484 }
485
486 static gboolean
487 update_style (ModestWebkitMimePartView *self)
488 {
489         GdkColor base;
490         GdkColor text;
491         GtkRcStyle *rc_style;
492
493         gdk_threads_enter ();
494
495         if (GTK_WIDGET_VISIBLE (self)) {
496                 rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (self));
497
498                 gdk_color_parse ("#fff", &base);
499                 gdk_color_parse ("#000", &text);
500
501                 if (!same_color (&(rc_style->base[GTK_STATE_NORMAL]), &base) &&
502                     !same_color (&(rc_style->text[GTK_STATE_NORMAL]), &text)) {
503
504                         rc_style->base[GTK_STATE_NORMAL] = base;
505                         rc_style->text[GTK_STATE_NORMAL] = text;
506                         gtk_widget_modify_style (GTK_WIDGET (self), rc_style);
507                 }
508         }
509
510         gdk_threads_leave ();
511
512         return FALSE;
513 }
514
515
516 /* INTERNAL API */
517 static void
518 decode_to_stream_cb (TnyMimePart *self,
519                      gboolean cancelled,
520                      TnyStream *stream,
521                      GError *err,
522                      gpointer user_data)
523 {
524         ModestWebkitMimePartView *view = (ModestWebkitMimePartView *) user_data;
525
526         if (MODEST_IS_STREAM_TEXT_TO_HTML (stream)) {
527                 if (tny_stream_write (stream, "\n", 1) == -1) {
528                         g_warning ("failed to write CR in %s", __FUNCTION__);
529                 }
530                 if (modest_stream_text_to_html_limit_reached (MODEST_STREAM_TEXT_TO_HTML (stream))) {
531                         g_signal_emit (G_OBJECT (view), signals[LIMIT_ERROR_SIGNAL], 0);
532                 }
533                 tny_stream_reset (stream);
534         } else {
535                 if (modest_tny_stream_webkit_limit_reached (MODEST_TNY_STREAM_WEBKIT (stream))) {
536                         g_signal_emit (G_OBJECT (view), signals[LIMIT_ERROR_SIGNAL], 0);
537                 }
538         }
539         tny_stream_close (stream);
540 }
541
542 static void
543 set_html_part (ModestWebkitMimePartView *self, TnyMimePart *part, const gchar *encoding)
544 {
545         TnyStream *tny_stream;
546
547         g_return_if_fail (self);
548         g_return_if_fail (part);
549
550         g_signal_emit (G_OBJECT (self), signals[STOP_STREAMS_SIGNAL], 0);
551
552         tny_stream     = TNY_STREAM(modest_tny_stream_webkit_new (WEBKIT_WEB_VIEW (self), "text/html", encoding));
553         modest_tny_stream_webkit_set_max_size (MODEST_TNY_STREAM_WEBKIT (tny_stream), 128*1024);
554         tny_stream_reset (tny_stream);
555
556         tny_mime_part_decode_to_stream_async (TNY_MIME_PART (part),
557                                               tny_stream, decode_to_stream_cb,
558                                               NULL, self);
559         g_object_unref (tny_stream);
560 }
561
562 static void
563 set_text_part (ModestWebkitMimePartView *self, TnyMimePart *part)
564 {
565         TnyStream* text_to_html_stream, *tny_stream;
566
567         g_return_if_fail (self);
568         g_return_if_fail (part);
569
570         g_signal_emit (G_OBJECT (self), signals[STOP_STREAMS_SIGNAL], 0);
571
572         tny_stream =  TNY_STREAM(modest_tny_stream_webkit_new (WEBKIT_WEB_VIEW (self), "text/html", "utf-8"));
573         modest_tny_stream_webkit_set_max_size (MODEST_TNY_STREAM_WEBKIT (tny_stream), 128*1024);
574         text_to_html_stream = TNY_STREAM (modest_stream_text_to_html_new (tny_stream));
575         modest_stream_text_to_html_set_linkify_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream),
576                                                       64*1024);
577         modest_stream_text_to_html_set_full_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream),
578                                                    128*1024);
579         modest_stream_text_to_html_set_line_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream),
580                                                    1024);
581
582         tny_mime_part_decode_to_stream_async (TNY_MIME_PART (part),
583                                               text_to_html_stream, decode_to_stream_cb,
584                                               NULL, self);
585
586         g_object_unref (G_OBJECT(text_to_html_stream));
587         g_object_unref (G_OBJECT(tny_stream));
588 }
589
590 static void
591 set_empty_part (ModestWebkitMimePartView *self)
592 {
593         g_return_if_fail (self);
594
595         g_signal_emit (G_OBJECT (self), signals[STOP_STREAMS_SIGNAL], 0);
596         webkit_web_view_load_string (WEBKIT_WEB_VIEW (self), "", "text/plain", "utf-8", NULL);
597 }
598
599 static void
600 set_part (ModestWebkitMimePartView *self, TnyMimePart *part)
601 {
602         ModestWebkitMimePartViewPrivate *priv;
603         gchar *header_content_type, *header_content_type_lower;
604         const gchar *tmp;
605         gchar *charset = NULL;
606
607         g_return_if_fail (self);
608         
609         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE(self);
610         priv->has_external_images = FALSE;
611
612         if (part != priv->part) {
613                 if (priv->part)
614                         g_object_unref (G_OBJECT(priv->part));
615                 if (part)
616                         g_object_ref   (G_OBJECT(part));
617                 priv->part = part;
618         }
619         
620         if (!part) {
621                 set_empty_part (self);
622                 return;
623         }
624
625         header_content_type = modest_tny_mime_part_get_header_value (part, "Content-Type");
626         if (header_content_type) {
627                 header_content_type = g_strstrip (header_content_type);
628                 header_content_type_lower = g_ascii_strdown (header_content_type, -1);
629         } else {
630                 header_content_type_lower = NULL;
631         }
632
633         if (header_content_type_lower) {
634                 tmp = strstr (header_content_type_lower, "charset=");
635                 if (tmp) {
636                         const gchar *tmp2;
637                         tmp = tmp + strlen ("charset=");
638                         
639                         tmp2 = strstr (tmp, ";");
640                         if (tmp2) {
641                                 charset = g_strndup (tmp, tmp2-tmp);
642                         } else {
643                                 charset = g_strdup (tmp);
644                         }
645                 }
646         }
647
648         if (tny_mime_part_content_type_is (part, "text/html")) {
649                 set_html_part (self, part, charset);
650         } else {
651                 if (tny_mime_part_content_type_is (part, "message/rfc822")) {
652                         if (header_content_type) {
653                                 if (g_str_has_prefix (header_content_type_lower, "text/html"))
654                                         set_html_part (self, part, charset);
655                                 else 
656                                         set_text_part (self, part);
657
658                         } else {
659                                 set_text_part (self, part);
660                         }
661                 } else {
662                         set_text_part (self, part);
663                 }
664         }
665         g_free (header_content_type_lower);
666         g_free (header_content_type);
667
668 }
669
670 static TnyMimePart*
671 get_part (ModestWebkitMimePartView *self)
672 {
673         TnyMimePart *part;
674
675         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), NULL);
676
677         part = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE(self)->part;
678
679         if (part)
680                 g_object_ref (part);
681         
682         return part;
683 }
684
685 static gboolean      
686 is_empty   (ModestWebkitMimePartView *self)
687 {
688         return FALSE;
689 }
690
691 static gboolean      
692 get_view_images   (ModestWebkitMimePartView *self)
693 {
694         ModestWebkitMimePartViewPrivate *priv;
695
696         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), FALSE);
697
698         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
699         return priv->view_images;
700 }
701
702 static void
703 set_view_images   (ModestWebkitMimePartView *self, gboolean view_images)
704 {
705         ModestWebkitMimePartViewPrivate *priv;
706
707         g_return_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self));
708
709         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
710         priv->view_images = view_images;
711 }
712
713 static gboolean      
714 has_external_images   (ModestWebkitMimePartView *self)
715 {
716         ModestWebkitMimePartViewPrivate *priv;
717
718         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), FALSE);
719
720         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
721         return priv->has_external_images;
722 }
723
724 static void
725 set_zoom (ModestWebkitMimePartView *self, gdouble zoom)
726 {
727         ModestWebkitMimePartViewPrivate *priv;
728
729         g_return_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self));
730
731         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
732         priv->current_zoom = zoom;
733
734         webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (self), zoom);
735         gtk_widget_queue_resize (GTK_WIDGET (self));
736 }
737
738 static gdouble
739 get_zoom (ModestWebkitMimePartView *self)
740 {
741         ModestWebkitMimePartViewPrivate *priv;
742
743         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), 1.0);
744
745         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
746
747         return priv->current_zoom;
748 }
749
750 static gboolean
751 search (ModestWebkitMimePartView *self, 
752         const gchar *string)
753 {
754         ModestWebkitMimePartViewPrivate *priv;
755
756         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), FALSE);
757
758         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
759         g_free (priv->last_search);
760         priv->last_search = g_strdup (string);
761         webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (self), priv->last_search, FALSE, 0);
762         webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (self), TRUE);
763         return webkit_web_view_search_text (WEBKIT_WEB_VIEW (self), priv->last_search, FALSE, TRUE, TRUE);
764 }
765
766 static gboolean
767 search_next (ModestWebkitMimePartView *self)
768 {
769         ModestWebkitMimePartViewPrivate *priv;
770
771         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), FALSE);
772
773         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
774         webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (self), priv->last_search, FALSE, 0);
775         webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (self), TRUE);
776         return webkit_web_view_search_text (WEBKIT_WEB_VIEW (self), priv->last_search, FALSE, TRUE, FALSE);
777 }
778
779 static gboolean
780 get_selection_area (ModestWebkitMimePartView *self, 
781                     gint *x, gint *y,
782                     gint *width, gint *height)
783 {
784         return FALSE;
785 }
786
787
788 /* TNY MIME PART IMPLEMENTATION */
789
790 static void
791 tny_mime_part_view_init (gpointer g, gpointer iface_data)
792 {
793         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
794
795         klass->get_part = modest_webkit_mime_part_view_get_part;
796         klass->set_part = modest_webkit_mime_part_view_set_part;
797         klass->clear = modest_webkit_mime_part_view_clear;
798
799         return;
800 }
801
802 static TnyMimePart* 
803 modest_webkit_mime_part_view_get_part (TnyMimePartView *self)
804 {
805         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_part_func (self);
806 }
807
808
809 static TnyMimePart* 
810 modest_webkit_mime_part_view_get_part_default (TnyMimePartView *self)
811 {
812         return TNY_MIME_PART (get_part (MODEST_WEBKIT_MIME_PART_VIEW (self)));
813 }
814
815 static void
816 modest_webkit_mime_part_view_set_part (TnyMimePartView *self,
817                                         TnyMimePart *part)
818 {
819         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->set_part_func (self, part);
820 }
821
822 static void
823 modest_webkit_mime_part_view_set_part_default (TnyMimePartView *self,
824                                                 TnyMimePart *part)
825 {
826         g_return_if_fail ((part == NULL) || TNY_IS_MIME_PART (part));
827
828         set_part (MODEST_WEBKIT_MIME_PART_VIEW (self), part);
829 }
830
831 static void
832 modest_webkit_mime_part_view_clear (TnyMimePartView *self)
833 {
834         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->clear_func (self);
835 }
836
837 static void
838 modest_webkit_mime_part_view_clear_default (TnyMimePartView *self)
839 {
840         set_part (MODEST_WEBKIT_MIME_PART_VIEW (self), NULL);
841 }
842
843 /* MODEST MIME PART VIEW IMPLEMENTATION */
844
845 static void
846 modest_mime_part_view_init (gpointer g, gpointer iface_data)
847 {
848         ModestMimePartViewIface *klass = (ModestMimePartViewIface *)g;
849
850         klass->is_empty_func = modest_webkit_mime_part_view_is_empty;
851         klass->get_view_images_func = modest_webkit_mime_part_view_get_view_images;
852         klass->set_view_images_func = modest_webkit_mime_part_view_set_view_images;
853         klass->has_external_images_func = modest_webkit_mime_part_view_has_external_images;
854
855         return;
856 }
857
858 static gboolean
859 modest_webkit_mime_part_view_is_empty (ModestMimePartView *self)
860 {
861         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->is_empty_func (self);
862 }
863
864 static gboolean
865 modest_webkit_mime_part_view_get_view_images (ModestMimePartView *self)
866 {
867         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_view_images_func (self);
868 }
869
870 static void
871 modest_webkit_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images)
872 {
873         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->set_view_images_func (self, view_images);
874 }
875
876 static gboolean
877 modest_webkit_mime_part_view_has_external_images (ModestMimePartView *self)
878 {
879         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->has_external_images_func (self);
880 }
881
882 static gboolean
883 modest_webkit_mime_part_view_is_empty_default (ModestMimePartView *self)
884 {
885         return is_empty (MODEST_WEBKIT_MIME_PART_VIEW (self));
886 }
887
888 static gboolean
889 modest_webkit_mime_part_view_get_view_images_default (ModestMimePartView *self)
890 {
891         return get_view_images (MODEST_WEBKIT_MIME_PART_VIEW (self));
892 }
893
894 static void
895 modest_webkit_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images)
896 {
897         set_view_images (MODEST_WEBKIT_MIME_PART_VIEW (self), view_images);
898 }
899
900 static gboolean
901 modest_webkit_mime_part_view_has_external_images_default (ModestMimePartView *self)
902 {
903         return has_external_images (MODEST_WEBKIT_MIME_PART_VIEW (self));
904 }
905
906
907 /* MODEST ZOOMABLE IMPLEMENTATION */
908 static void
909 modest_zoomable_init (gpointer g, gpointer iface_data)
910 {
911         ModestZoomableIface *klass = (ModestZoomableIface *)g;
912         
913         klass->get_zoom_func = modest_webkit_mime_part_view_get_zoom;
914         klass->set_zoom_func = modest_webkit_mime_part_view_set_zoom;
915         klass->zoom_minus_func = modest_webkit_mime_part_view_zoom_minus;
916         klass->zoom_plus_func = modest_webkit_mime_part_view_zoom_plus;
917
918         return;
919 }
920
921 static gdouble
922 modest_webkit_mime_part_view_get_zoom (ModestZoomable *self)
923 {
924         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_zoom_func (self);
925 }
926
927 static gdouble
928 modest_webkit_mime_part_view_get_zoom_default (ModestZoomable *self)
929 {
930         return get_zoom (MODEST_WEBKIT_MIME_PART_VIEW (self));
931 }
932
933 static void
934 modest_webkit_mime_part_view_set_zoom (ModestZoomable *self, gdouble value)
935 {
936         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->set_zoom_func (self, value);
937 }
938
939 static void
940 modest_webkit_mime_part_view_set_zoom_default (ModestZoomable *self, gdouble value)
941 {
942         set_zoom (MODEST_WEBKIT_MIME_PART_VIEW (self), value);
943 }
944
945 static gboolean
946 modest_webkit_mime_part_view_zoom_minus (ModestZoomable *self)
947 {
948         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->zoom_minus_func (self);
949 }
950
951 static gboolean
952 modest_webkit_mime_part_view_zoom_minus_default (ModestZoomable *self)
953 {
954         /* operation not supported in ModestWebkitMimePartView */
955         return FALSE;
956 }
957
958 static gboolean
959 modest_webkit_mime_part_view_zoom_plus (ModestZoomable *self)
960 {
961         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->zoom_plus_func (self);
962 }
963
964 static gboolean
965 modest_webkit_mime_part_view_zoom_plus_default (ModestZoomable *self)
966 {
967         /* operation not supported in ModestWebkitMimePartView */
968         return FALSE;
969 }
970
971 /* ISEARCH VIEW IMPLEMENTATION */
972 static void
973 modest_isearch_view_init (gpointer g, gpointer iface_data)
974 {
975         ModestISearchViewIface *klass = (ModestISearchViewIface *)g;
976         
977         klass->search_func = modest_webkit_mime_part_view_search;
978         klass->search_next_func = modest_webkit_mime_part_view_search_next;
979         klass->get_selection_area_func = modest_webkit_mime_part_view_get_selection_area;
980
981         return;
982 }
983
984 static gboolean 
985 modest_webkit_mime_part_view_search (ModestISearchView *self, const gchar *string)
986 {
987         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->search_func (self, string);
988 }
989
990 static gboolean 
991 modest_webkit_mime_part_view_search_default (ModestISearchView *self, const gchar *string)
992 {
993         return search (MODEST_WEBKIT_MIME_PART_VIEW (self), string);
994 }
995
996 static gboolean 
997 modest_webkit_mime_part_view_search_next(ModestISearchView *self)
998 {
999         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->search_next_func (self);
1000 }
1001
1002 static gboolean 
1003 modest_webkit_mime_part_view_search_next_default (ModestISearchView *self)
1004 {
1005         return search_next (MODEST_WEBKIT_MIME_PART_VIEW (self));
1006 }
1007
1008 static gboolean 
1009 modest_webkit_mime_part_view_get_selection_area (ModestISearchView *self, gint *x, gint *y, 
1010                                                   gint *width, gint *height)
1011 {
1012         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_selection_area_func (self, x, y, width, height);
1013 }
1014
1015 static gboolean 
1016 modest_webkit_mime_part_view_get_selection_area_default (ModestISearchView *self, gint *x, gint *y, 
1017                                                           gint *width, gint *height)
1018 {
1019         return get_selection_area (MODEST_WEBKIT_MIME_PART_VIEW (self), x, y, width, height);
1020 }