[evolution/webkit: 8/182] Port collapsable To/Cc/Bcc fields and collapsable headers to JavaScript



commit 83042a3e93e1ca1e6437bc4e3add4341bc3eb335
Author: Dan VrÃtil <dvratil redhat com>
Date:   Wed Jul 27 17:04:56 2011 +0200

    Port collapsable To/Cc/Bcc fields and collapsable headers to JavaScript
    
    Use JavaScript to handle collapsing/expanding of To, Cc and Bcc header
    fields and collapsing of the whole headers themselves.
    The headers collapsing must notify "headers-state" property of EMFormatHTML
    when changed. This is done by extending EWebView API by function for
    installing callback functions to JavaScript.
    Finally, the simple_headers property was removed from EMFormatHTML as it
    was not used anymore.

 addressbook/gui/widgets/eab-contact-display.c |    2 +-
 data/webview.css                              |   10 +
 mail/e-mail-display.c                         |   78 ++---
 mail/em-format-html.c                         |  549 ++++++++++++++-----------
 mail/em-format-html.h                         |    2 -
 widgets/misc/e-web-view.c                     |  106 +++++
 widgets/misc/e-web-view.h                     |   12 +
 7 files changed, 453 insertions(+), 306 deletions(-)
---
diff --git a/addressbook/gui/widgets/eab-contact-display.c b/addressbook/gui/widgets/eab-contact-display.c
index f4cd6e9..00fc110 100644
--- a/addressbook/gui/widgets/eab-contact-display.c
+++ b/addressbook/gui/widgets/eab-contact-display.c
@@ -528,7 +528,7 @@ render_contact_list_row (GString *buffer,
 	g_string_append (buffer, "<tr>");
 	if (e_destination_is_evolution_list (destination)) {
 		g_string_append_printf (buffer,
-			"<td width=" IMAGE_COL_WIDTH " valign=\"top\"><img src=\"%s/minus.png\" onClick=\"collapse_list(this, %s);\"></td><td width=\"100%%\">%s",
+			"<td width=" IMAGE_COL_WIDTH " valign=\"top\"><img src=\"%s/minus.png\" onClick=\"collapse_list(this, %s);\" class=\"navigable\"></td><td width=\"100%%\">%s",
 			evolution_imagesdir,
 			e_destination_get_contact_uid (destination),
 			name ? name : email_addr);
diff --git a/data/webview.css b/data/webview.css
index 1f87964..704c745 100644
--- a/data/webview.css
+++ b/data/webview.css
@@ -21,3 +21,13 @@ th {
   font-family: monospace;
   font-size: 0.8em;
 }
+
+span.navigable, div.navigable, p.navigable {
+  cursor: hand;
+  text-decoration: underline;
+  color: #003399;
+}
+
+img.navigable {
+  cursor: hand;
+}
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 3e33bac..7aae7bf 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -260,69 +260,44 @@ mail_display_process_mailto (EWebView *web_view,
 	return FALSE;
 }
 
-static void
-mail_display_link_clicked (GtkHTML *html,
-                           const gchar *uri)
+static gboolean
+mail_display_link_clicked (WebKitWebView *web_view,
+			   WebKitWebFrame *frame,
+			   WebKitNetworkRequest *request,
+			   WebKitWebNavigationAction *navigation_action,
+			   WebKitWebPolicyDecision *policy_decision,
+			   gpointer user_data)
 {
+	EMailDisplay *display = user_data;
 	EMailDisplayPrivate *priv;
+	const gchar *uri = webkit_network_request_get_uri (request);
 
 	priv = E_MAIL_DISPLAY_GET_PRIVATE (html);
-	g_return_if_fail (priv->formatter != NULL);
-
-	if (g_str_has_prefix (uri, "##")) {
-		guint32 flags;
-
-		flags = priv->formatter->header_wrap_flags;
-
-		if (strcmp (uri, "##TO##") == 0) {
-			if (!(flags & EM_FORMAT_HTML_HEADER_TO))
-				flags |= EM_FORMAT_HTML_HEADER_TO;
-			else
-				flags &= ~EM_FORMAT_HTML_HEADER_TO;
-		} else if (strcmp (uri, "##CC##") == 0) {
-			if (!(flags & EM_FORMAT_HTML_HEADER_CC))
-				flags |= EM_FORMAT_HTML_HEADER_CC;
-			else
-				flags &= ~EM_FORMAT_HTML_HEADER_CC;
-		} else if (strcmp (uri, "##BCC##") == 0) {
-			if (!(flags & EM_FORMAT_HTML_HEADER_BCC))
-				flags |= EM_FORMAT_HTML_HEADER_BCC;
-			else
-				flags &= ~EM_FORMAT_HTML_HEADER_BCC;
-		} else if (strcmp (uri, "##HEADERS##") == 0) {
-			EMFormatHTMLHeadersState state;
-
-			state = em_format_html_get_headers_state (
-				priv->formatter);
-
-			if (state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED)
-				state = EM_FORMAT_HTML_HEADERS_STATE_EXPANDED;
-			else
-				state = EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED;
-
-			em_format_html_set_headers_state (
-				priv->formatter, state);
-		}
-
-		priv->formatter->header_wrap_flags = flags;
-		em_format_queue_redraw (EM_FORMAT (priv->formatter));
-
-	} else if (mail_display_process_mailto (E_WEB_VIEW (html), uri)) {
+	g_return_val_if_fail (priv->formatter != NULL, FALSE);
+
+	if (mail_display_process_mailto (E_WEB_VIEW (display), uri)) {
 		/* do nothing, function handled the "mailto:"; uri already */
-	} else if (*uri == '#')
-		gtk_html_jump_to_anchor (html, uri + 1);
+		webkit_web_policy_decision_ignore (policy_decision);
+		return TRUE;
 
-	else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0)
+	} else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
 		/* ignore */ ;
+		webkit_web_policy_decision_ignore (policy_decision);
+		return TRUE;
 
-	else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0)
+	} else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
 		/* ignore */ ;
+		webkit_web_policy_decision_ignore (policy_decision);
+		return TRUE;
 
 	else {
 		/* Chain up to parent's link_clicked() method. */
 		GTK_HTML_CLASS (e_mail_display_parent_class)->
 			link_clicked (html, uri);
 	}
+
+	/* Let webkit handle it */
+	return FALSE;
 }
 
 static void
@@ -331,7 +306,6 @@ e_mail_display_class_init (EMailDisplayClass *class)
 	GObjectClass *object_class;
 	GtkWidgetClass *widget_class;
 	EWebViewClass *web_view_class;
-	GtkHTMLClass *html_class;
 
 	g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
 
@@ -347,10 +321,6 @@ e_mail_display_class_init (EMailDisplayClass *class)
 	web_view_class = E_WEB_VIEW_CLASS (class);
 	web_view_class->process_mailto = mail_display_process_mailto;
 
-	html_class = GTK_HTML_CLASS (class);
-	html_class->url_requested = mail_display_url_requested;
-	html_class->link_clicked = mail_display_link_clicked;
-
 	g_object_class_install_property (
 		object_class,
 		PROP_FORMATTER,
@@ -371,6 +341,8 @@ e_mail_display_init (EMailDisplay *display)
 	GError *error = NULL;
 
 	web_view = E_WEB_VIEW (display);
+	g_signal_connect (web_view, "navigation-policy-decision-requested",
+		G_CALLBACK (mail_display_link_clicked), display);
 
 	display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
 
diff --git a/mail/em-format-html.c b/mail/em-format-html.c
index f938858..6ef42c3 100644
--- a/mail/em-format-html.c
+++ b/mail/em-format-html.c
@@ -54,8 +54,9 @@
 
 #include <glib/gi18n.h>
 
-#include <libemail-utils/mail-mt.h>
+#include <JavaScriptCore/JavaScript.h>
 
+#include <libemail-utils/mail-mt.h>
 #include <libemail-engine/e-mail-enumtypes.h>
 #include <libemail-engine/e-mail-utils.h>
 #include <libemail-engine/mail-config.h>
@@ -142,7 +143,11 @@ static void	efh_resource_requested		(WebKitWebView *web_view,
 						 WebKitNetworkRequest *request,
 						 WebKitNetworkResponse *reponse,
 						 gpointer user_data);
-
+static void	efh_install_js_callbacks	(WebKitWebView *web_view,
+						 WebKitWebFrame *frame,
+						 gpointer context,
+						 gpointer window_object,
+						 gpointer user_data);
 static void	efh_format_message		(EMFormat *emf,
 						 CamelStream *stream,
 						 CamelMimePart *part,
@@ -1010,6 +1015,9 @@ efh_init (EMFormatHTML *efh,
 	g_signal_connect (
 		web_view, "frame-created",
 		G_CALLBACK (efh_webview_frame_created), efh);
+	g_signal_connect (
+		web_view, "window-object-cleared",
+		G_CALLBACK (efh_install_js_callbacks), efh);
 
 	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY];
 	gdk_color_parse ("#eeeeee", color);
@@ -1805,10 +1813,10 @@ efh_webview_frame_loaded (GObject *object,
 	frame_name = webkit_web_frame_get_name (frame);
 
 	/* Get total height of the document inside the frame */
-	e_web_view_frame_exec_script (E_WEB_VIEW (web_view), frame_name, "document.body.offsetHeight;", &val);
+	e_web_view_frame_exec_script (E_WEB_VIEW (web_view), frame_name, "document.body.scrollHeight;", &val);
 
 	/* Change height of the frame so that entire content is visible */
-	script = g_strdup_printf ("window.document.getElementById(\"%s\").height=%d;", frame_name, (int)g_value_get_double (&val));
+	script = g_strdup_printf ("window.document.getElementById(\"%s\").height=%d;", frame_name, (int)(g_value_get_double (&val) + 10));
 	e_web_view_exec_script (E_WEB_VIEW (web_view), script, NULL);
 	g_free (script);
 }
@@ -1827,6 +1835,31 @@ efh_webview_frame_created (WebKitWebView *web_view,
 	}
 }
 
+static void
+efh_headers_collapsed_state_changed (EWebView *web_view, size_t arg_count, const JSValueRef args[], gpointer user_data)
+{
+	EMFormatHTML *efh = user_data;
+	JSGlobalContextRef ctx = e_web_view_get_global_context (web_view);
+
+	gboolean collapsed = JSValueToBoolean (ctx, args[0]);
+
+	if (collapsed) {
+		em_format_html_set_headers_state (efh, EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED);
+	} else {
+		em_format_html_set_headers_state (efh, EM_FORMAT_HTML_HEADERS_STATE_EXPANDED);
+	}
+}
+
+static void
+efh_install_js_callbacks (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer context, gpointer window_object, gpointer user_data)
+{
+	if (frame != webkit_web_view_get_main_frame (web_view))
+		return;
+
+	e_web_view_install_js_callback (E_WEB_VIEW (web_view), "headers_collapsed",
+		(EWebViewJSFunctionCallback) efh_headers_collapsed_state_changed, user_data);
+}
+
 /* ********************************************************************** */
 #include "em-format/em-inline-filter.h"
 
@@ -2642,33 +2675,29 @@ efh_format_text_header (EMFormatHTML *emfh,
 		html = value;
 
 	is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
-	if (emfh->simple_headers) {
-		fmt = "<b>%s</b>: %s<br>";
+
+	if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) {
+		if (flags & EM_FORMAT_HEADER_BOLD) {
+			fmt = "<tr><td><b>%s:</b> %s</td></tr>";
+		} else {
+			fmt = "<tr><td>%s: %s</td></tr>";
+		}
+	} else if (flags & EM_FORMAT_HTML_HEADER_NODEC) {
+		if (is_rtl)
+			fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+		else
+			fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
 	} else {
-		if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) {
-			if (flags & EM_FORMAT_HEADER_BOLD) {
-				fmt = "<tr><td><b>%s:</b> %s</td></tr>";
-			} else {
-				fmt = "<tr><td>%s: %s</td></tr>";
-			}
-		} else if (flags & EM_FORMAT_HTML_HEADER_NODEC) {
+		if (flags & EM_FORMAT_HEADER_BOLD) {
 			if (is_rtl)
-				fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+				fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
 			else
-				fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
+				fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
 		} else {
-
-			if (flags & EM_FORMAT_HEADER_BOLD) {
-				if (is_rtl)
-					fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
-				else
-					fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
-			} else {
-				if (is_rtl)
-					fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
-				else
-					fmt = "<tr><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
-			}
+			if (is_rtl)
+				fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
+			else
+				fmt = "<tr><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
 		}
 	}
 
@@ -2692,17 +2721,9 @@ efh_format_address (EMFormatHTML *efh,
 	guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
 	gchar *name, *mailto, *addr;
 	gint i = 0;
-	gboolean wrap = FALSE;
 	gchar *str = NULL;
 	gint limit = mail_config_get_address_count ();
 
-	if (field ) {
-		if ((!strcmp (field, _("To")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_TO))
-		    || (!strcmp (field, _("Cc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_CC))
-		    || (!strcmp (field, _("Bcc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_BCC)))
-		    wrap = TRUE;
-	}
-
 	while (a) {
 		if (a->name)
 			name = camel_text_to_html (a->name, flags, 0);
@@ -2759,48 +2780,47 @@ efh_format_address (EMFormatHTML *efh,
 			g_string_append (out, ", ");
 
 		/* Let us add a '...' if we have more addresses */
-		if (limit > 0 && wrap && a && (i > (limit - 1))) {
+		if (limit > 0 && (i == limit - 1)) {
 			gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
-
-			if (!strcmp (field, _("To"))) {
-				g_string_append (out, "<a href=\"##TO##\">...</a>");
-				str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/plus.png\"></a>  ", evolution_imagesdir);
-			}
-			else if (!strcmp (field, _("Cc"))) {
-				g_string_append (out, "<a href=\"##CC##\">...</a>");
-				str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/plus.png\"></a>  ", evolution_imagesdir);
+			const gchar *id = NULL;
+
+			if (strcmp (field, _("To")) == 0) {
+				id = "to";
+			} else if (strcmp (field, _("Cc")) == 0) {
+				id = "cc";
+			} else if (strcmp (field, _("Bcc")) == 0) {
+				id = "bcc";
 			}
-			else if (!strcmp (field, _("Bcc"))) {
-				g_string_append (out, "<a href=\"##BCC##\">...</a>");
-				str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/plus.png\"></a>  ", evolution_imagesdir);
+
+			if (id) {
+				g_string_append_printf (out, "<span id=\"moreaddr-%s\" style=\"display: none;\">", id);
+				str = g_strdup_printf ("<img src=\"%s/plus.png\" onClick=\"collapse_addresses('%s');\" id=\"moreaddr-img-%s\" class=\"navigable\">  ",
+					evolution_imagesdir, id, id);
 			}
 
 			g_free (evolution_imagesdir);
-
-			if (str)
-				return str;
 		}
-
 	}
 
-	if (limit > 0 && i > (limit)) {
-		gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+	if (str) {
+		const gchar *id = NULL;
 
-		if (!strcmp (field, _("To"))) {
-			str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/minus.png\"></a>  ", evolution_imagesdir);
-		}
-		else if (!strcmp (field, _("Cc"))) {
-			str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/minus.png\"></a>  ", evolution_imagesdir);
-		}
-		else if (!strcmp (field, _("Bcc"))) {
-			str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/minus.png\"></a>  ", evolution_imagesdir);
+		if (strcmp (field, _("To")) == 0) {
+			id = "to";
+		} else if (strcmp (field, _("Cc")) == 0) {
+			id = "cc";
+		} else if (strcmp (field, _("Bcc")) == 0) {
+			id = "bcc";
 		}
 
-		g_free (evolution_imagesdir);
+		if (id) {
+			g_string_append_printf (out, "</span><span class=\"navigable\" onClick=\"collapse_addresses('%s');\" " \
+				"id=\"moreaddr-ellipsis-%s\" style=\"display: inline;\">...</span>",
+				id, id);
+		}
 	}
 
 	return str;
-
 }
 
 static void
@@ -2835,7 +2855,7 @@ efh_format_header (EMFormat *emf,
                    guint32 flags,
                    const gchar *charset)
 {
-	EMFormatHTML *efh = (EMFormatHTML *) emf;
+	EMFormatHTML *efh = EM_FORMAT_HTML (emf);
 	gchar *name, *buf, *value = NULL;
 	const gchar *label, *txt;
 	gboolean addrspec = FALSE;
@@ -2983,100 +3003,111 @@ efh_format_header (EMFormat *emf,
 }
 
 static void
-efh_format_headers (EMFormatHTML *efh,
-                    GString *buffer,
-                    CamelMedium *part,
-                    GCancellable *cancellable)
+efh_format_short_headers (EMFormatHTML *efh,
+			  GString *buffer,
+			  CamelMedium *part,
+			  gboolean visible,
+			  GCancellable *cancellable)
 {
-	EMFormat *emf = (EMFormat *) efh;
+	EMFormat *emf = EM_FORMAT (efh);
 	const gchar *charset;
 	CamelContentType *ct;
-	struct _camel_header_raw *header;
-	gboolean have_icon = FALSE;
-	const gchar *photo_name = NULL;
-	CamelInternetAddress *cia = NULL;
-	gboolean face_decoded  = FALSE, contact_has_photo = FALSE;
-	guchar *face_header_value = NULL;
-	gsize face_header_len = 0;
-	gchar *header_sender = NULL, *header_from = NULL, *name;
-	gboolean mail_from_delegate = FALSE;
 	const gchar *hdr_charset;
 	gchar *evolution_imagesdir;
+	gchar *subject = NULL;
+	struct _camel_header_address *addrs = NULL;
+	struct _camel_header_raw *header;
+	GString *from;
 
-	if (!part)
+	if (cancellable && g_cancellable_is_cancelled (cancellable))
 		return;
 
 	ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
 	charset = camel_content_type_param (ct, "charset");
 	charset = camel_iconv_charset_name (charset);
-
-	if (!efh->simple_headers)
-		g_string_append_printf (
-			buffer, "<font color=\"#%06x\">\n"
-			"<table cellpadding=\"0\" width=\"100%%\">",
-			e_color_to_value (
-				&efh->priv->colors[
-				EM_FORMAT_HTML_COLOR_HEADER]));
-
 	hdr_charset = emf->charset ? emf->charset : emf->default_charset;
+
 	evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+	from = g_string_new ("");
 
-	/* If the header is collapsed, display just subject and sender in one row and leave */
-	if (efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED && efh->priv->headers_collapsable) {
-		gchar *subject = NULL;
-		struct _camel_header_address *addrs = NULL;
-		GString *from = g_string_new ("");
+	g_string_append_printf (buffer, "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" id=\"short-headers\" style=\"display: %s\">",
+		visible ? "block" : "none");
 
-		header = ((CamelMimePart *) part)->headers;
-		while (header) {
-			if (!g_ascii_strcasecmp (header->name, "From")) {
-				GString *tmp;
-				if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) {
-					header = header->next;
-					continue;
-				}
-				tmp = g_string_new ("");
-				efh_format_address (efh, tmp, addrs, header->name);
-
-				if (tmp->len)
-					g_string_printf (from, _("From: %s"), tmp->str);
-				g_string_free (tmp, TRUE);
-			} else if (!g_ascii_strcasecmp (header->name, "Subject")) {
-				gchar *buf = NULL;
-				buf = camel_header_unfold (header->value);
-				g_free (subject);
-				subject = camel_header_decode_string (buf, hdr_charset);
-				g_free (buf);
+	header = ((CamelMimePart *) part)->headers;
+	while (header) {
+		if (!g_ascii_strcasecmp (header->name, "From")) {
+			GString *tmp;
+			if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) {
+				header = header->next;
+				continue;
 			}
-			header = header->next;
+			tmp = g_string_new ("");
+			efh_format_address (efh, tmp, addrs, header->name);
+
+			if (tmp->len)
+				g_string_printf (from, _("From: %s"), tmp->str);
+			g_string_free (tmp, TRUE);
+
+		} else if (!g_ascii_strcasecmp (header->name, "Subject")) {
+			gchar *buf = NULL;
+			buf = camel_header_unfold (header->value);
+			g_free (subject);
+			subject = camel_header_decode_string (buf, hdr_charset);
+			g_free (buf);
 		}
+		header = header->next;
+	}
 
-		g_string_append_printf (
-			buffer,
-			"<tr>"
-			"<td width=\"20\" valign=\"top\">"
-			"<a href=\"##HEADERS##\">"
-			"<img src=\"%s/plus.png\">"
-			"</a></td>"
-			"<td><strong>%s</strong> %s%s%s</td>"
-			"</tr>",
-			evolution_imagesdir,
-			subject ? subject : _("(no subject)"),
-			from->len ? "(" : "",
-			from->str,
-			from->len ? ")" : "");
+	g_string_append_printf (
+		buffer,
+		"<tr><td><strong>%s</strong> %s%s%s</td></tr>",
+		subject ? subject : _("(no subject)"),
+		from->len ? "(" : "", from->str, from->len ? ")" : "");
 
-		g_free (subject);
-		if (addrs)
-			camel_header_address_list_clear (&addrs);
-		g_string_free (from, TRUE);
+	g_string_append (buffer, "</table>");
 
-		g_string_append (buffer, "</table>");
+	g_free (subject);
+	if (addrs)
+		camel_header_address_list_clear (&addrs);
 
-		g_free (evolution_imagesdir);
+	g_string_free (from, TRUE);
+	g_free (evolution_imagesdir);
+}
+
+static void
+efh_format_full_headers (EMFormatHTML *efh,
+			 GString *buffer,
+			 CamelMedium *part,
+			 gboolean visible,
+			 GCancellable *cancellable)
+{
+	EMFormat *emf = EM_FORMAT (efh);
+	const gchar *charset;
+	CamelContentType *ct;
+	struct _camel_header_raw *header;
+	gboolean have_icon = FALSE;
+	const gchar *photo_name = NULL;
+	CamelInternetAddress *cia = NULL;
+	gboolean face_decoded  = FALSE, contact_has_photo = FALSE;
+	guchar *face_header_value = NULL;
+	gsize face_header_len = 0;
+	gchar *header_sender = NULL, *header_from = NULL, *name;
+	gboolean mail_from_delegate = FALSE;
+	const gchar *hdr_charset;
+	gchar *evolution_imagesdir;
 
+	if (cancellable && g_cancellable_is_cancelled (cancellable))
 		return;
-	}
+
+	ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
+	charset = camel_content_type_param (ct, "charset");
+	charset = camel_iconv_charset_name (charset);
+	hdr_charset = emf->charset ? emf->charset : emf->default_charset;
+
+	evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+
+	g_string_append_printf (buffer, "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" id=\"full-headers\" style=\"display: %s\" width=\"100%%\">",
+		visible ? "block" : "none");
 
 	header = ((CamelMimePart *) part)->headers;
 	while (header) {
@@ -3147,42 +3178,7 @@ efh_format_headers (EMFormatHTML *efh,
 	g_free (header_sender);
 	g_free (header_from);
 
-	if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) {
-		if (efh->priv->headers_collapsable)
-			g_string_append_printf (
-				buffer,
-				"<tr>"
-				"<td valign=\"top\" width=\"20\">"
-				"<a href=\"##HEADERS##\">"
-				"<img src=\"%s/minus.png\">"
-				"</a></td>"
-				"<td><table width=\"100%%\" border=0 "
-				"cellpadding=\"0\">\n",
-				evolution_imagesdir);
-		else
-			g_string_append (
-				buffer,
-				"<tr><td>"
-				"<table width=\"100%%\" border=0 "
-				"cellpadding=\"0\">\n");
-
-	} else {
-		if (efh->priv->headers_collapsable)
-			g_string_append_printf (
-				buffer,
-				"<tr>"
-				"<td valign=\"top\" width=\"20\">"
-				"<a href=\"##HEADERS##\">"
-				"<img src=\"%s/minus.png\">"
-				"</a></td>"
-				"<td><table border=0 cellpadding=\"0\">\n",
-				evolution_imagesdir);
-		else
-			g_string_append (
-				buffer,
- 				"<tr><td>"
-				"<table border=0 cellpadding=\"0\">\n");
-	}
+	g_string_append (buffer, "<tr><td><table border=0 cellpadding=\"0\">\n");
 
 	g_free (evolution_imagesdir);
 
@@ -3272,98 +3268,136 @@ efh_format_headers (EMFormatHTML *efh,
 		}
 	}
 
-	if (!efh->simple_headers) {
-		g_string_append (buffer, "</table></td>");
-
-		if (photo_name) {
-			gchar *classid;
-			CamelMimePart *photopart;
-			gboolean only_local_photo;
-
-			cia = camel_internet_address_new ();
-			camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name);
-			only_local_photo = em_format_html_get_only_local_photos (efh);
-			photopart = em_utils_contact_photo (cia, only_local_photo);
-
-			if (photopart) {
-				contact_has_photo = TRUE;
-				classid = g_strdup_printf (
-					"icon:///em-format-html/%s/photo/header",
-					emf->part_id->str);
-				g_string_append_printf (
-					buffer,
-					"<td align=\"right\" valign=\"top\">"
-					"<img width=64 src=\"%s\"></td>",
-					classid);
-				em_format_add_puri (emf, sizeof (EMFormatPURI), classid,
-					photopart, efh_write_image);
-				g_object_unref (photopart);
-
-				g_free (classid);
-			}
-			g_object_unref (cia);
-		}
+	g_string_append (buffer, "</table></td>");
+
+	if (photo_name) {
+		gchar *classid;
+		CamelMimePart *photopart;
+		gboolean only_local_photo;
 
-		if (!contact_has_photo && face_decoded) {
-			gchar *classid;
-			CamelMimePart *part;
+		cia = camel_internet_address_new ();
+		camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name);
+		only_local_photo = em_format_html_get_only_local_photos (efh);
+		photopart = em_utils_contact_photo (cia, only_local_photo);
 
-			part = camel_mime_part_new ();
-			camel_mime_part_set_content (
-				(CamelMimePart *) part,
-				(const gchar *) face_header_value,
-				face_header_len, "image/png");
+		if (photopart) {
+			contact_has_photo = TRUE;
 			classid = g_strdup_printf (
-				"icon:///em-format-html/face/photo/header");
+				"icon:///em-format-html/%s/photo/header",
+				emf->part_id->str);
 			g_string_append_printf (
 				buffer,
 				"<td align=\"right\" valign=\"top\">"
-				"<img width=48 src=\"%s\"></td>",
+				"<img width=64 src=\"%s\"></td>",
 				classid);
-			em_format_add_puri (
-				emf, sizeof (EMFormatPURI),
-				classid, part, efh_write_image);
-			g_object_unref (part);
+			em_format_add_puri (emf, sizeof (EMFormatPURI), classid,
+				photopart, efh_write_image);
+			g_object_unref (photopart);
+
 			g_free (classid);
-			g_free (face_header_value);
 		}
+		g_object_unref (cia);
+	}
 
-		if (have_icon && efh->show_icon) {
-			GtkIconInfo *icon_info;
-			gchar *classid;
-			CamelMimePart *iconpart = NULL;
+	if (!contact_has_photo && face_decoded) {
+		gchar *classid;
+		CamelMimePart *part;
 
-			classid = g_strdup_printf (
-				"icon:///em-format-html/%s/icon/header",
-				emf->part_id->str);
-			g_string_append_printf (
-				buffer,
-				"<td align=\"right\" valign=\"top\">"
-				"<img width=16 height=16 src=\"%s\"></td>",
-				classid);
+		part = camel_mime_part_new ();
+		camel_mime_part_set_content (
+			(CamelMimePart *) part,
+			(const gchar *) face_header_value,
+			face_header_len, "image/png");
+		classid = g_strdup_printf (
+			"icon:///em-format-html/face/photo/header");
+		g_string_append_printf (
+			buffer,
+			"<td align=\"right\" valign=\"top\">"
+			"<img width=48 src=\"%s\"></td>",
+			classid);
+		em_format_add_puri (
+			emf, sizeof (EMFormatPURI),
+			classid, part, efh_write_image);
+		g_object_unref (part);
+		g_free (classid);
+		g_free (face_header_value);
+	}
 
-			icon_info = gtk_icon_theme_lookup_icon (
-				gtk_icon_theme_get_default (),
-				"evolution", 16, GTK_ICON_LOOKUP_NO_SVG);
-			if (icon_info != NULL) {
-				iconpart = em_format_html_file_part (
-					(EMFormatHTML *) emf, "image/png",
-					gtk_icon_info_get_filename (icon_info),
-					cancellable);
-				gtk_icon_info_free (icon_info);
-			}
+	if (have_icon && efh->show_icon) {
+		GtkIconInfo *icon_info;
+		gchar *classid;
+		CamelMimePart *iconpart = NULL;
 
-			if (iconpart) {
-				em_format_add_puri (
-					emf, sizeof (EMFormatPURI),
-					classid, iconpart, efh_write_image);
-				g_object_unref (iconpart);
-			}
-			g_free (classid);
+		classid = g_strdup_printf (
+			"icon:///em-format-html/%s/icon/header",
+			emf->part_id->str);
+		g_string_append_printf (
+			buffer,
+			"<td align=\"right\" valign=\"top\">"
+			"<img width=16 height=16 src=\"%s\"></td>",
+			classid);
+			icon_info = gtk_icon_theme_lookup_icon (
+			gtk_icon_theme_get_default (),
+			"evolution", 16, GTK_ICON_LOOKUP_NO_SVG);
+		if (icon_info != NULL) {
+			iconpart = em_format_html_file_part (
+				(EMFormatHTML *) emf, "image/png",
+				gtk_icon_info_get_filename (icon_info),
+				cancellable);
+			gtk_icon_info_free (icon_info);
 		}
+		if (iconpart) {
+			em_format_add_puri (
+				emf, sizeof (EMFormatPURI),
+				classid, iconpart, efh_write_image);
+			g_object_unref (iconpart);
+		}
+		g_free (classid);
+	}
+
+	g_string_append (buffer, "</tr></table>");
+}
+
+static void
+efh_format_headers (EMFormatHTML *efh,
+                    GString *buffer,
+                    CamelMedium *part,
+                    GCancellable *cancellable)
+{
+	gchar *evolution_imagesdir;
+
+	if (!part)
+		return;
+
+
+	evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
 
-		g_string_append (buffer, "</tr></table>\n</font>\n");
+	g_string_append_printf (
+		buffer, "<font color=\"#%06x\">\n"
+		"<table border=\"0\" width=\"100%%\">"
+		"<tr><td valign=\"top\" width=\"20\">",
+		e_color_to_value (
+			&efh->priv->colors[
+			EM_FORMAT_HTML_COLOR_HEADER]));
+
+	if (efh->priv->headers_collapsable) {
+		g_string_append_printf (buffer,
+			"<img src=\"%s/%s\" onClick=\"collapse_headers();\" class=\"navigable\" id=\"collapse-headers-img\" /></td><td>",
+			evolution_imagesdir,
+			(efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED) ? "plus.png" : "minus.png");
+
+		efh_format_short_headers (efh, buffer, part,
+			(efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED),
+			cancellable);
 	}
+
+	efh_format_full_headers (efh, buffer, part,
+		(efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_EXPANDED),
+		cancellable);
+
+	g_string_append (buffer, "</td></tr></table></font>");
+
+	g_free (evolution_imagesdir);
 }
 
 static void
@@ -3385,7 +3419,6 @@ efh_format_message (EMFormat *emf,
 	emf->valid_parent = NULL;
 
 	buffer = g_string_sized_new (1024);
-
 	g_string_append_printf (buffer,
 		"<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"  \
 		"<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n" \
@@ -3394,9 +3427,25 @@ efh_format_message (EMFormat *emf,
 		"  table th { color: #000; font-weight: bold; }\n" \
 		"</style>\n" \
 		"<script type=\"text/javascript\">\n" \
-		"function body_loaded() { window.location.hash=\"" EFM_MESSAGE_START_ANAME "\"; }" \
+		"function body_loaded() { window.location.hash='" EFM_MESSAGE_START_ANAME "'; }\n" \
+		"function collapse_addresses(field) {\n" \
+		"  var e=window.document.getElementById(\"moreaddr-\"+field).style;\n" \
+		"  var f=window.document.getElementById(\"moreaddr-ellipsis-\"+field).style;\n" \
+		"  var g=window.document.getElementById(\"moreaddr-img-\"+field);\n" \
+		"  if (e.display==\"inline\") { e.display=\"none\"; f.display=\"inline\"; g.src=g.src.substr(0,g.src.lastIndexOf(\"/\"))+\"/plus.png\"; }\n" \
+		"  else { e.display=\"inline\"; f.display=\"none\"; g.src=g.src.substr(0,g.src.lastIndexOf(\"/\"))+\"/minus.png\"; }\n" \
+		"}\n" \
+		"function collapse_headers() {\n" \
+		"  var f=window.document.getElementById(\"full-headers\").style;\n" \
+		"  var s=window.document.getElementById(\"short-headers\").style;\n" \
+		"  var i=window.document.getElementById(\"collapse-headers-img\");\n" \
+		"  if (f.display==\"block\") { f.display=\"none\"; s.display=\"block\";" \
+		"	i.src=i.src.substr(0,i.src.lastIndexOf(\"/\"))+\"/plus.png\"; window.headers_collapsed(true, window.em_format_html); }\n" \
+		"  else { f.display=\"block\"; s.display=\"none\";" \
+		"	 i.src=i.src.substr(0,i.src.lastIndexOf(\"/\"))+\"/minus.png\"; window.headers_collapsed(false, window.em_format_html); }\n" \
+		"}\n" \
 		"</script>\n" \
-		"</body>\n" \
+		"</head>\n" \
 		"<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6 onLoad=\"body_loaded();\">",
 		e_color_to_value (
 			&efh->priv->colors[
diff --git a/mail/em-format-html.h b/mail/em-format-html.h
index d6dd4f9..1b95797 100644
--- a/mail/em-format-html.h
+++ b/mail/em-format-html.h
@@ -195,7 +195,6 @@ struct _EMFormatHTMLPObject {
  * @load_http:2:
  * @load_http_now:1:
  * @mark_citations:1:
- * @simple_headers:1:
  * @hide_headers:1:
  * @show_icon:1:
  *
@@ -214,7 +213,6 @@ struct _EMFormatHTML {
 	GSList *headers;
 
 	guint32 text_html_flags; /* default flags for text to html conversion */
-	guint simple_headers:1; /* simple header format, no box/table */
 	guint hide_headers:1; /* no headers at all */
 	guint show_icon:1; /* show an icon when the sender used Evo */
 	guint32 header_wrap_flags;
diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c
index 077cffe..640066f 100644
--- a/widgets/misc/e-web-view.c
+++ b/widgets/misc/e-web-view.c
@@ -51,6 +51,8 @@ struct _EWebViewPrivate {
 	GdkPixbufAnimation *cursor_image;
 	gchar *cursor_image_src;
 
+	GHashTable *js_callbacks;
+
 	GtkAction *open_proxy;
 	GtkAction *print_proxy;
 	GtkAction *save_as_proxy;
@@ -806,6 +808,11 @@ web_view_dispose (GObject *object)
 		priv->cursor_image_src = NULL;
 	}
 
+	if (priv->js_callbacks != NULL) {
+		g_hash_table_destroy (priv->js_callbacks);
+		priv->js_callbacks = NULL;
+	}
+
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -1662,6 +1669,9 @@ e_web_view_init (EWebView *web_view)
 	ui_manager = gtk_ui_manager_new ();
 	web_view->priv->ui_manager = ui_manager;
 
+	web_view->priv->js_callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
+		(GDestroyNotify) g_free, NULL);
+
 	g_signal_connect_swapped (
 		ui_manager, "connect-proxy",
 		G_CALLBACK (web_view_connect_proxy_cb), web_view);
@@ -1900,6 +1910,17 @@ e_web_view_get_html (EWebView *web_view)
 		return NULL;
 }
 
+JSGlobalContextRef
+e_web_view_get_global_context (EWebView *web_view)
+{
+	WebKitWebFrame *main_frame;
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0);
+
+	main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+	return webkit_web_frame_get_global_context (main_frame);
+}
+
 GType
 e_web_view_exec_script (EWebView *web_view, const gchar *script, GValue *value)
 {
@@ -1992,6 +2013,91 @@ e_web_view_frame_exec_script (EWebView *web_view, const gchar *frame_name, const
 	return G_VALUE_TYPE (value);
 }
 
+static JSValueRef
+web_view_handle_js_callback (JSContextRef ctx, JSObjectRef function, JSObjectRef this_object,
+			     size_t argument_count, const JSValueRef arguments[], JSValueRef *exception)
+{
+	gpointer web_view;
+	gpointer user_data;
+	gchar *fnc_name;
+	size_t fnc_name_len;
+
+	EWebViewJSFunctionCallback callback;
+
+	JSStringRef js_webview_prop = JSStringCreateWithUTF8CString ("webview");
+	JSStringRef js_userdata_prop = JSStringCreateWithUTF8CString ("user-data");
+	JSStringRef js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name");
+	JSStringRef js_fncname_str;
+
+	JSValueRef js_webview = JSObjectGetProperty (ctx, function, js_webview_prop, NULL);
+	JSValueRef js_userdata = JSObjectGetProperty (ctx, function, js_userdata_prop, NULL);
+	JSValueRef js_fncname = JSObjectGetProperty (ctx, function, js_fncname_prop, NULL);
+
+	web_view = GINT_TO_POINTER ((int) JSValueToNumber (ctx, js_webview, NULL));
+	user_data = GINT_TO_POINTER ((int) JSValueToNumber (ctx, js_userdata, NULL));
+	js_fncname_str = JSValueToStringCopy (ctx, js_fncname, NULL);
+	fnc_name_len = JSStringGetLength (js_fncname_str);
+
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0);
+
+	/* Convert fncname to gchar* and lookup the callback in hashtable */
+	fnc_name = g_malloc (fnc_name_len + 1);
+	JSStringGetUTF8CString (js_fncname_str, fnc_name, fnc_name_len+1);
+	callback = g_hash_table_lookup (E_WEB_VIEW (web_view)->priv->js_callbacks, fnc_name);
+
+	g_return_val_if_fail (callback != NULL, 0);
+
+	/* Call the callback function */
+	callback (E_WEB_VIEW (web_view), argument_count, arguments, user_data);
+
+	JSStringRelease (js_fncname_str);
+	JSStringRelease (js_fncname_prop);
+	JSStringRelease (js_webview_prop);
+	JSStringRelease (js_userdata_prop);
+
+	g_free (fnc_name);
+
+	return 0;
+}
+
+void
+e_web_view_install_js_callback (EWebView *web_view, const gchar *fnc_name, EWebViewJSFunctionCallback callback, gpointer user_data)
+{
+	WebKitWebFrame *frame;
+	JSGlobalContextRef ctx;
+	JSObjectRef global_obj, js_func;
+	JSStringRef js_fnc_name, js_webview_prop, js_userdata_prop, js_fncname_prop;
+
+	g_return_if_fail (E_IS_WEB_VIEW (web_view));
+	g_return_if_fail (fnc_name != NULL);
+	g_return_if_fail (callback != NULL);
+
+	frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+	ctx = webkit_web_frame_get_global_context (frame);
+	global_obj = JSContextGetGlobalObject (ctx);
+	js_fnc_name = JSStringCreateWithUTF8CString (fnc_name);
+	js_func = JSObjectMakeFunctionWithCallback (ctx, NULL,
+		(JSObjectCallAsFunctionCallback) web_view_handle_js_callback);
+	js_webview_prop = JSStringCreateWithUTF8CString ("webview");
+	js_userdata_prop = JSStringCreateWithUTF8CString ("user-data");
+	js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name");
+
+	/* Set some properties to the function */
+	JSObjectSetProperty (ctx, js_func, js_webview_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (web_view)), 0, NULL);
+	JSObjectSetProperty (ctx, js_func, js_userdata_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (user_data)), 0, NULL);
+	JSObjectSetProperty (ctx, js_func, js_fncname_prop, JSValueMakeString (ctx, js_fnc_name), 0, NULL);
+
+	/* Set the function as a property of global object */
+	JSObjectSetProperty (ctx, global_obj, js_fnc_name, js_func, 0, NULL);
+
+	JSStringRelease (js_fncname_prop);
+	JSStringRelease (js_userdata_prop);
+	JSStringRelease (js_webview_prop);
+	JSStringRelease (js_fnc_name);
+
+	g_hash_table_insert (web_view->priv->js_callbacks, g_strdup (fnc_name), callback);
+}
+
 gboolean
 e_web_view_get_animate (EWebView *web_view)
 {
diff --git a/widgets/misc/e-web-view.h b/widgets/misc/e-web-view.h
index 48a7cc2..d43b3eb 100644
--- a/widgets/misc/e-web-view.h
+++ b/widgets/misc/e-web-view.h
@@ -28,6 +28,7 @@
 #define E_WEB_VIEW_H
 
 #include <webkit/webkit.h>
+#include <JavaScriptCore/JavaScript.h>
 
 /* Standard GObject macros */
 #define E_TYPE_WEB_VIEW \
@@ -59,6 +60,11 @@ struct _EWebView {
 	EWebViewPrivate *priv;
 };
 
+typedef void (*EWebViewJSFunctionCallback) 	(EWebView *web_view,
+						 size_t arg_count,
+						 const JSValueRef args[],
+						 gpointer user_data);
+
 struct _EWebViewClass {
 	WebKitWebViewClass parent_class;
 
@@ -112,6 +118,8 @@ void		e_web_view_frame_load_uri	(EWebView *web_view,
 						 const gchar *uri);
 const gchar*	e_web_view_frame_get_uri	(EWebView *web_view,
 						 const gchar *frame_name);
+JSGlobalContextRef
+		e_web_view_get_global_context	(EWebView *web_view);
 GType		e_web_view_exec_script		(EWebView *web_view,
 						 const gchar *script,
 						 GValue *value);
@@ -119,6 +127,10 @@ GType		e_web_view_frame_exec_script	(EWebView *web_view,
 						 const gchar *frame_name,
 						 const gchar *script,
 						 GValue *value);
+void		e_web_view_install_js_callback  (EWebView *web_view,
+						 const gchar *fnc_name,
+						 EWebViewJSFunctionCallback callback,
+						 gpointer user_data);
 gchar *		e_web_view_get_html		(EWebView *web_view);
 gboolean	e_web_view_get_animate		(EWebView *web_view);
 void		e_web_view_set_animate		(EWebView *web_view,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]