1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
30 #include <tny-gtk-text-buffer-stream.h>
35 #include <glib/gi18n.h>
36 #include <gtkhtml/gtkhtml.h>
37 #include <gtkhtml/gtkhtml-stream.h>
39 #include <tny-simple-list.h>
41 #include <modest-tny-msg.h>
42 #include <modest-text-utils.h>
43 #include "modest-msg-view.h"
44 #include "modest-tny-stream-gtkhtml.h"
45 #include <modest-mail-header-view.h>
46 #include <modest-attachments-view.h>
49 /* 'private'/'protected' functions */
50 static void modest_msg_view_class_init (ModestMsgViewClass *klass);
51 static void modest_msg_view_init (ModestMsgView *obj);
52 static void modest_msg_view_finalize (GObject *obj);
54 static void on_recpt_activated (ModestMailHeaderView *header_view, const gchar *address, ModestMsgView *msg_view);
55 static gboolean on_link_clicked (GtkWidget *widget, const gchar *uri, ModestMsgView *msg_view);
56 static gboolean on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream,
57 ModestMsgView *msg_view);
58 static gboolean on_link_hover (GtkWidget *widget, const gchar *uri, ModestMsgView *msg_view);
60 #define ATT_PREFIX "att:"
66 ATTACHMENT_CLICKED_SIGNAL,
67 RECPT_ACTIVATED_SIGNAL,
71 typedef struct _ModestMsgViewPrivate ModestMsgViewPrivate;
72 struct _ModestMsgViewPrivate {
74 GtkWidget *mail_header_view;
75 GtkWidget *attachments_view;
79 gulong sig1, sig2, sig3;
81 #define MODEST_MSG_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
82 MODEST_TYPE_MSG_VIEW, \
83 ModestMsgViewPrivate))
85 static GtkContainerClass *parent_class = NULL;
87 /* uncomment the following if you have defined any signals */
88 static guint signals[LAST_SIGNAL] = {0};
91 modest_msg_view_get_type (void)
93 static GType my_type = 0;
95 static const GTypeInfo my_info = {
96 sizeof(ModestMsgViewClass),
98 NULL, /* base finalize */
99 (GClassInitFunc) modest_msg_view_class_init,
100 NULL, /* class finalize */
101 NULL, /* class data */
102 sizeof(ModestMsgView),
104 (GInstanceInitFunc) modest_msg_view_init,
107 my_type = g_type_register_static (GTK_TYPE_VBOX,
115 modest_msg_view_class_init (ModestMsgViewClass *klass)
117 GObjectClass *gobject_class;
118 gobject_class = (GObjectClass*) klass;
120 parent_class = g_type_class_peek_parent (klass);
121 gobject_class->finalize = modest_msg_view_finalize;
123 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewPrivate));
126 signals[LINK_CLICKED_SIGNAL] =
127 g_signal_new ("link_clicked",
128 G_TYPE_FROM_CLASS (gobject_class),
130 G_STRUCT_OFFSET(ModestMsgViewClass, link_clicked),
132 g_cclosure_marshal_VOID__STRING,
133 G_TYPE_NONE, 1, G_TYPE_STRING);
135 signals[ATTACHMENT_CLICKED_SIGNAL] =
136 g_signal_new ("attachment_clicked",
137 G_TYPE_FROM_CLASS (gobject_class),
139 G_STRUCT_OFFSET(ModestMsgViewClass, attachment_clicked),
141 g_cclosure_marshal_VOID__POINTER,
142 G_TYPE_NONE, 1, G_TYPE_INT);
144 signals[LINK_HOVER_SIGNAL] =
145 g_signal_new ("link_hover",
146 G_TYPE_FROM_CLASS (gobject_class),
148 G_STRUCT_OFFSET(ModestMsgViewClass, link_hover),
150 g_cclosure_marshal_VOID__STRING,
151 G_TYPE_NONE, 1, G_TYPE_STRING);
153 signals[RECPT_ACTIVATED_SIGNAL] =
154 g_signal_new ("recpt_activated",
155 G_TYPE_FROM_CLASS (gobject_class),
157 G_STRUCT_OFFSET(ModestMsgViewClass, recpt_activated),
159 g_cclosure_marshal_VOID__STRING,
160 G_TYPE_NONE, 1, G_TYPE_STRING);
164 modest_msg_view_init (ModestMsgView *obj)
166 ModestMsgViewPrivate *priv;
168 priv = MODEST_MSG_VIEW_GET_PRIVATE(obj);
172 priv->gtkhtml = gtk_html_new();
173 gtk_html_set_editable (GTK_HTML(priv->gtkhtml), FALSE);
174 gtk_html_allow_selection (GTK_HTML(priv->gtkhtml), TRUE);
175 gtk_html_set_caret_mode (GTK_HTML(priv->gtkhtml), FALSE);
176 gtk_html_set_blocking (GTK_HTML(priv->gtkhtml), FALSE);
177 gtk_html_set_images_blocking (GTK_HTML(priv->gtkhtml), FALSE);
179 priv->mail_header_view = GTK_WIDGET(modest_mail_header_view_new ());
180 gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
182 priv->attachments_view = GTK_WIDGET(modest_attachments_view_new (NULL));
183 gtk_widget_set_no_show_all (priv->attachments_view, TRUE);
185 priv->sig1 = g_signal_connect (G_OBJECT(priv->gtkhtml), "link_clicked",
186 G_CALLBACK(on_link_clicked), obj);
187 priv->sig2 = g_signal_connect (G_OBJECT(priv->gtkhtml), "url_requested",
188 G_CALLBACK(on_url_requested), obj);
189 priv->sig3 = g_signal_connect (G_OBJECT(priv->gtkhtml), "on_url",
190 G_CALLBACK(on_link_hover), obj);
192 g_signal_connect (G_OBJECT (priv->mail_header_view), "recpt-activated",
193 G_CALLBACK (on_recpt_activated), obj);
198 modest_msg_view_finalize (GObject *obj)
200 ModestMsgViewPrivate *priv;
201 priv = MODEST_MSG_VIEW_GET_PRIVATE (obj);
204 g_object_unref (G_OBJECT(priv->msg));
208 /* we cannot disconnect sigs, because priv->gtkhtml is
211 priv->gtkhtml = NULL;
212 priv->attachments_view = NULL;
214 G_OBJECT_CLASS(parent_class)->finalize (obj);
219 modest_msg_view_new (TnyMsg *msg)
223 ModestMsgViewPrivate *priv;
224 GtkWidget *scrolled_window;
226 obj = G_OBJECT(g_object_new(MODEST_TYPE_MSG_VIEW, NULL));
227 self = MODEST_MSG_VIEW(obj);
228 priv = MODEST_MSG_VIEW_GET_PRIVATE (self);
230 gtk_box_set_spacing (GTK_BOX (self), 0);
231 gtk_box_set_homogeneous (GTK_BOX (self), FALSE);
233 if (priv->mail_header_view)
234 gtk_box_pack_start (GTK_BOX(self), priv->mail_header_view, FALSE, FALSE, 0);
236 if (priv->attachments_view)
237 gtk_box_pack_start (GTK_BOX(self), priv->attachments_view, FALSE, FALSE, 0);
240 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
241 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
242 GTK_POLICY_AUTOMATIC,
243 GTK_POLICY_AUTOMATIC);
244 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scrolled_window),
246 gtk_container_add (GTK_CONTAINER (scrolled_window), priv->gtkhtml);
247 gtk_box_pack_start (GTK_BOX(self), scrolled_window, TRUE, TRUE, 0);
250 modest_msg_view_set_message (self, msg);
252 return GTK_WIDGET(self);
256 on_recpt_activated (ModestMailHeaderView *header_view,
257 const gchar *address,
258 ModestMsgView * view)
260 g_signal_emit (G_OBJECT (view), signals[RECPT_ACTIVATED_SIGNAL], 0, address);
264 on_link_clicked (GtkWidget *widget, const gchar *uri, ModestMsgView *msg_view)
268 g_return_val_if_fail (msg_view, FALSE);
270 /* is it an attachment? */
271 if (g_str_has_prefix(uri, ATT_PREFIX)) {
273 index = atoi (uri + strlen(ATT_PREFIX));
276 /* index is 1-based, so 0 indicates an error */
277 g_printerr ("modest: invalid attachment id: %s\n", uri);
281 g_signal_emit (G_OBJECT(msg_view), signals[ATTACHMENT_CLICKED_SIGNAL],
286 g_signal_emit (G_OBJECT(msg_view), signals[LINK_CLICKED_SIGNAL],
295 on_link_hover (GtkWidget *widget, const gchar *uri, ModestMsgView *msg_view)
297 if (uri && g_str_has_prefix (uri, ATT_PREFIX))
300 g_signal_emit (G_OBJECT(msg_view), signals[LINK_HOVER_SIGNAL],
309 find_cid_image (TnyMsg *msg, const gchar *cid)
311 TnyMimePart *part = NULL;
315 g_return_val_if_fail (msg, NULL);
316 g_return_val_if_fail (cid, NULL);
318 parts = TNY_LIST (tny_simple_list_new());
320 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
321 iter = tny_list_create_iterator (parts);
323 while (!tny_iterator_is_done(iter)) {
324 const gchar *part_cid;
325 part = TNY_MIME_PART(tny_iterator_get_current(iter));
326 part_cid = tny_mime_part_get_content_id (part);
328 if (part_cid && strcmp (cid, part_cid) == 0)
331 g_object_unref (G_OBJECT(part));
334 tny_iterator_next (iter);
337 g_object_unref (G_OBJECT(iter));
338 g_object_unref (G_OBJECT(parts));
345 on_url_requested (GtkWidget *widget, const gchar *uri,
346 GtkHTMLStream *stream, ModestMsgView *msg_view)
348 ModestMsgViewPrivate *priv;
349 priv = MODEST_MSG_VIEW_GET_PRIVATE (msg_view);
351 if (g_str_has_prefix (uri, "cid:")) {
352 /* +4 ==> skip "cid:" */
353 TnyMimePart *part = find_cid_image (priv->msg, uri + 4);
355 g_printerr ("modest: '%s' not found\n", uri + 4);
356 gtk_html_stream_close (stream, GTK_HTML_STREAM_ERROR);
358 TnyStream *tny_stream =
359 TNY_STREAM(modest_tny_stream_gtkhtml_new(stream));
360 tny_mime_part_decode_to_stream ((TnyMimePart*)part,
362 gtk_html_stream_close (stream, GTK_HTML_STREAM_OK);
364 g_object_unref (G_OBJECT(tny_stream));
365 g_object_unref (G_OBJECT(part));
373 set_html_message (ModestMsgView *self, TnyMimePart *tny_body, TnyMsg *msg)
375 GtkHTMLStream *gtkhtml_stream;
376 TnyStream *tny_stream;
377 ModestMsgViewPrivate *priv;
379 g_return_val_if_fail (self, FALSE);
380 g_return_val_if_fail (tny_body, FALSE);
382 priv = MODEST_MSG_VIEW_GET_PRIVATE(self);
384 gtkhtml_stream = gtk_html_begin(GTK_HTML(priv->gtkhtml));
386 tny_stream = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream));
387 tny_stream_reset (tny_stream);
389 tny_mime_part_decode_to_stream ((TnyMimePart*)tny_body, tny_stream);
390 g_object_unref (G_OBJECT(tny_stream));
392 gtk_html_stream_destroy (gtkhtml_stream);
398 /* FIXME: this is a hack --> we use the tny_text_buffer_stream to
399 * get the message text, then write to gtkhtml 'by hand' */
401 set_text_message (ModestMsgView *self, TnyMimePart *tny_body, TnyMsg *msg)
404 GtkTextIter begin, end;
405 TnyStream* txt_stream, *tny_stream;
406 GtkHTMLStream *gtkhtml_stream;
408 ModestMsgViewPrivate *priv;
410 g_return_val_if_fail (self, FALSE);
411 g_return_val_if_fail (tny_body, FALSE);
413 priv = MODEST_MSG_VIEW_GET_PRIVATE(self);
415 buf = gtk_text_buffer_new (NULL);
416 txt_stream = TNY_STREAM(tny_gtk_text_buffer_stream_new (buf));
418 tny_stream_reset (txt_stream);
420 gtkhtml_stream = gtk_html_begin(GTK_HTML(priv->gtkhtml));
421 tny_stream = TNY_STREAM(modest_tny_stream_gtkhtml_new (gtkhtml_stream));
424 tny_mime_part_decode_to_stream ((TnyMimePart*)tny_body, txt_stream);
425 tny_stream_reset (txt_stream);
427 gtk_text_buffer_get_bounds (buf, &begin, &end);
428 txt = gtk_text_buffer_get_text (buf, &begin, &end, FALSE);
430 gchar *html = modest_text_utils_convert_to_html (txt);
431 tny_stream_write (tny_stream, html, strlen(html));
432 tny_stream_reset (tny_stream);
437 g_object_unref (G_OBJECT(tny_stream));
438 g_object_unref (G_OBJECT(txt_stream));
439 g_object_unref (G_OBJECT(buf));
441 gtk_html_stream_destroy (gtkhtml_stream);
448 set_empty_message (ModestMsgView *self)
450 ModestMsgViewPrivate *priv;
452 g_return_val_if_fail (self, FALSE);
453 priv = MODEST_MSG_VIEW_GET_PRIVATE(self);
455 gtk_html_load_from_string (GTK_HTML(priv->gtkhtml),
463 modest_msg_view_set_message (ModestMsgView *self, TnyMsg *msg)
466 ModestMsgViewPrivate *priv;
469 g_return_if_fail (self);
471 priv = MODEST_MSG_VIEW_GET_PRIVATE(self);
472 gtk_widget_set_no_show_all (priv->mail_header_view, FALSE);
474 if (msg != priv->msg) {
476 g_object_unref (G_OBJECT(priv->msg));
478 g_object_ref (G_OBJECT(msg));
483 tny_header_view_clear (TNY_HEADER_VIEW (priv->mail_header_view));
484 gtk_widget_hide_all (priv->mail_header_view);
485 gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
486 set_empty_message (self);
490 header = tny_msg_get_header (msg);
491 tny_header_view_set_header (TNY_HEADER_VIEW (priv->mail_header_view), header);
492 g_object_unref (header);
494 modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW(priv->attachments_view),
497 body = modest_tny_msg_find_body_part (msg,TRUE);
499 if (tny_mime_part_content_type_is (body, "text/html"))
500 set_html_message (self, body, msg);
502 set_text_message (self, body, msg);
504 set_empty_message (self);
506 gtk_widget_show_all (priv->mail_header_view);
507 gtk_widget_set_no_show_all (priv->mail_header_view, TRUE);
512 modest_msg_view_get_message (ModestMsgView *self)
514 g_return_val_if_fail (self, NULL);
516 return MODEST_MSG_VIEW_GET_PRIVATE(self)->msg;