Split the interactive parts of the message display out of EMFormatHTMLDisplay

to a new GtkHTML subclass named EMailDisplay, and have EMFormatHTML create an
instance of that.  EMailDisplay will handle link clicking, mousing over URIs,
popup menus, and interactive search.  This makes EMFormatHTMLDisplay and
EMailReader more lightweight.

Clean up more of the EMFormat stack.

svn path=/branches/kill-bonobo/; revision=37346
This commit is contained in:
Matthew Barnes
2009-03-01 13:16:31 +00:00
parent 24abc4f173
commit 08b1d0ae8e
13 changed files with 1784 additions and 1441 deletions

View File

@ -37,6 +37,8 @@ module_LTLIBRARIES = \
libevolution_module_mail_la_SOURCES = \
e-mail-browser.c \
e-mail-browser.h \
e-mail-display.c \
e-mail-display.h \
e-mail-label-dialog.c \
e-mail-label-dialog.h \
e-mail-label-list-store.c \

View File

@ -217,6 +217,23 @@ mail_browser_message_selected_cb (EMailBrowser *browser,
camel_folder_free_message_info (message_list->folder, info);
}
static void
mail_browser_status_message_cb (EMailBrowser *browser,
const gchar *status_message)
{
GtkStatusbar *statusbar;
guint context_id;
statusbar = GTK_STATUSBAR (browser->priv->statusbar);
context_id = gtk_statusbar_get_context_id (statusbar, G_STRFUNC);
/* Always pop first. This prevents messages from piling up. */
gtk_statusbar_pop (statusbar, context_id);
if (status_message != NULL && *status_message != '\0')
gtk_statusbar_push (statusbar, context_id, status_message);
}
static void
mail_browser_set_shell_module (EMailBrowser *browser,
EShellModule *shell_module)
@ -320,6 +337,7 @@ mail_browser_dispose (GObject *object)
static void
mail_browser_constructed (GObject *object)
{
EMFormatHTMLDisplay *html_display;
EMailBrowserPrivate *priv;
EMailReader *reader;
EShellModule *shell_module;
@ -329,6 +347,7 @@ mail_browser_constructed (GObject *object)
GtkUIManager *ui_manager;
GtkWidget *container;
GtkWidget *widget;
GtkHTML *html;
const gchar *domain;
guint merge_id;
@ -338,10 +357,14 @@ mail_browser_constructed (GObject *object)
ui_manager = priv->ui_manager;
domain = GETTEXT_PACKAGE;
html_display = e_mail_reader_get_html_display (reader);
shell_module = e_mail_reader_get_shell_module (reader);
shell = e_shell_module_get_shell (shell_module);
e_shell_watch_window (shell, GTK_WINDOW (object));
html = EM_FORMAT_HTML (html_display)->html;
/* The message list is a widget, but it is not shown in the browser.
* Unfortunately, the widget is inseparable from its model, and the
* model is all we need. */
@ -352,6 +375,10 @@ mail_browser_constructed (GObject *object)
priv->message_list, "message-selected",
G_CALLBACK (mail_browser_message_selected_cb), object);
g_signal_connect_swapped (
html, "status-message",
G_CALLBACK (mail_browser_status_message_cb), object);
e_mail_reader_init (reader);
action_group = priv->action_group;
@ -409,7 +436,7 @@ mail_browser_constructed (GObject *object)
container = widget;
widget = GTK_WIDGET (((EMFormatHTML *) priv->html_display)->html);
widget = GTK_WIDGET (EM_FORMAT_HTML (html_display)->html);
gtk_container_add (GTK_CONTAINER (container), widget);
gtk_widget_show (widget);
}

627
mail/e-mail-display.c Normal file
View File

@ -0,0 +1,627 @@
/*
* e-mail-display.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#include "e-mail-display.h"
#include <string.h>
#include <glib/gi18n.h>
#include "e-util/e-util.h"
#define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate))
struct _EMailDisplayPrivate {
EMFormatHTML *formatter;
};
enum {
PROP_0,
PROP_ANIMATE,
PROP_CARET_MODE,
PROP_FORMATTER
};
enum {
POPUP_EVENT,
STATUS_MESSAGE,
LAST_SIGNAL
};
static gpointer parent_class;
static guint signals[LAST_SIGNAL];
static gboolean
mail_display_emit_popup_event (EMailDisplay *display,
GdkEventButton *event,
const gchar *uri,
EMFormatPURI *puri)
{
CamelMimePart *mime_part;
gboolean stop_handlers = FALSE;
mime_part = (puri != NULL) ? puri->part : NULL;
g_signal_emit (
display, signals[POPUP_EVENT], 0,
event, uri, mime_part, &stop_handlers);
return stop_handlers;
}
static void
mail_display_emit_status_message (EMailDisplay *display,
const gchar *status_message)
{
g_signal_emit (display, signals[STATUS_MESSAGE], 0, status_message);
}
static void
mail_display_get_uri_puri (EMailDisplay *display,
GdkEventButton *event,
gchar **uri,
EMFormatPURI **puri)
{
EMFormat *formatter;
GtkHTML *html;
gchar *text_uri;
gchar *image_uri;
gboolean is_cid;
html = GTK_HTML (display);
formatter = EM_FORMAT (display->priv->formatter);
if (event != NULL) {
text_uri = gtk_html_get_url_at (html, event->x, event->y);
image_uri = gtk_html_get_image_src_at (html, event->x, event->y);
} else {
text_uri = gtk_html_get_cursor_url (html);
image_uri = gtk_html_get_cursor_image_src (html);
}
is_cid = (image_uri != NULL) &&
(g_ascii_strncasecmp (image_uri, "cid:", 4) == 0);
if (image_uri != NULL) {
if (strstr (image_uri, "://") == NULL && !is_cid) {
gchar *temp;
temp = g_strconcat ("file://", image_uri, NULL);
g_free (image_uri);
temp = image_uri;
}
}
if (puri != NULL) {
if (text_uri != NULL)
*puri = em_format_find_puri (formatter, text_uri);
if (*puri == NULL && image_uri != NULL)
*puri = em_format_find_puri (formatter, image_uri);
}
if (uri != NULL) {
*uri = NULL;
if (is_cid) {
if (text_uri != NULL)
*uri = g_strdup_printf (
"%s\n%s", text_uri, image_uri);
else {
*uri = image_uri;
image_uri = NULL;
}
} else {
*uri = text_uri;
text_uri = NULL;
}
}
g_free (text_uri);
g_free (image_uri);
}
static void
mail_display_update_formatter_colors (EMailDisplay *display)
{
EMFormatHTMLColorType type;
EMFormatHTML *formatter;
GdkColor *color;
GtkStyle *style;
gint state;
state = GTK_WIDGET_STATE (display);
formatter = display->priv->formatter;
style = gtk_widget_get_style (GTK_WIDGET (display));
if (style == NULL)
return;
g_object_freeze_notify (G_OBJECT (formatter));
color = &style->bg[state];
type = EM_FORMAT_HTML_COLOR_BODY;
em_format_html_set_color (formatter, type, color);
color = &style->base[GTK_STATE_NORMAL];
type = EM_FORMAT_HTML_COLOR_CONTENT;
em_format_html_set_color (formatter, type, color);
color = &style->dark[state];
type = EM_FORMAT_HTML_COLOR_FRAME;
em_format_html_set_color (formatter, type, color);
color = &style->fg[state];
type = EM_FORMAT_HTML_COLOR_HEADER;
em_format_html_set_color (formatter, type, color);
color = &style->text[state];
type = EM_FORMAT_HTML_COLOR_TEXT;
em_format_html_set_color (formatter, type, color);
g_object_thaw_notify (G_OBJECT (formatter));
}
static void
mail_display_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_ANIMATE:
e_mail_display_set_animate (
E_MAIL_DISPLAY (object),
g_value_get_boolean (value));
return;
case PROP_CARET_MODE:
e_mail_display_set_caret_mode (
E_MAIL_DISPLAY (object),
g_value_get_boolean (value));
return;
case PROP_FORMATTER:
e_mail_display_set_formatter (
E_MAIL_DISPLAY (object),
g_value_get_object (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
mail_display_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_ANIMATE:
g_value_set_boolean (
value, e_mail_display_get_animate (
E_MAIL_DISPLAY (object)));
return;
case PROP_CARET_MODE:
g_value_set_boolean (
value, e_mail_display_get_caret_mode (
E_MAIL_DISPLAY (object)));
return;
case PROP_FORMATTER:
g_value_set_object (
value, e_mail_display_get_formatter (
E_MAIL_DISPLAY (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
mail_display_dispose (GObject *object)
{
EMailDisplayPrivate *priv;
priv = E_MAIL_DISPLAY_GET_PRIVATE (object);
if (priv->formatter) {
g_object_unref (priv->formatter);
priv->formatter = NULL;
}
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
mail_display_realize (GtkWidget *widget)
{
/* Chain up to parent's realize() method. */
GTK_WIDGET_CLASS (parent_class)->realize (widget);
mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
}
static void
mail_display_style_set (GtkWidget *widget,
GtkStyle *previous_style)
{
EMailDisplayPrivate *priv;
priv = E_MAIL_DISPLAY_GET_PRIVATE (widget);
/* Chain up to parent's style_set() method. */
GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
em_format_redraw (EM_FORMAT (priv->formatter));
}
static gboolean
mail_display_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
if (event->button == 3) {
EMailDisplay *display;
EMFormatPURI *puri = NULL;
gboolean stop_handlers = TRUE;
gchar *uri = NULL;
display = E_MAIL_DISPLAY (widget);
mail_display_get_uri_puri (display, event, &uri, &puri);
if (uri == NULL || !g_str_has_prefix (uri, "##"))
stop_handlers = mail_display_emit_popup_event (
display, event, uri, puri);
g_free (uri);
if (stop_handlers)
return TRUE;
}
/* Chain up to parent's button_press_event() method. */
return GTK_WIDGET_CLASS (parent_class)->
button_press_event (widget, event);
}
static gboolean
mail_display_scroll_event (GtkWidget *widget,
GdkEventScroll *event)
{
if (event->state & GDK_CONTROL_MASK) {
switch (event->direction) {
case GDK_SCROLL_UP:
gtk_html_zoom_in (GTK_HTML (widget));
return TRUE;
case GDK_SCROLL_DOWN:
gtk_html_zoom_out (GTK_HTML (widget));
return TRUE;
default:
break;
}
}
return FALSE;
}
static void
mail_display_link_clicked (GtkHTML *html,
const gchar *uri)
{
EMailDisplayPrivate *priv;
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;
}
priv->formatter->header_wrap_flags = flags;
em_format_redraw (EM_FORMAT (priv->formatter));
} else if (*uri == '#')
gtk_html_jump_to_anchor (html, uri + 1);
else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0)
/* ignore */ ;
else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0)
/* ignore */ ;
else {
gpointer parent;
parent = gtk_widget_get_toplevel (GTK_WIDGET (html));
parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
e_show_uri (parent, uri);
}
}
static void
mail_display_on_url (GtkHTML *html,
const gchar *uri)
{
EMailDisplay *display;
CamelInternetAddress *address;
CamelURL *curl;
const gchar *format = NULL;
gchar *message = NULL;
gchar *who;
display = E_MAIL_DISPLAY (html);
if (uri == NULL || *uri == '\0')
goto exit;
if (g_str_has_prefix (uri, "mailto:"))
format = _("Click to mail %s");
else if (g_str_has_prefix (uri, "callto:"))
format = _("Click to call %s");
else if (g_str_has_prefix (uri, "h323:"))
format = _("Click to call %s");
else if (g_str_has_prefix (uri, "sip:"))
format = _("Click to call %s");
else if (g_str_has_prefix (uri, "##"))
message = g_strdup (_("Click to hide/unhide addresses"));
else
message = g_strdup_printf (_("Click to open %s"), uri);
if (format == NULL)
goto exit;
curl = camel_url_new (uri, NULL);
address = camel_internet_address_new ();
camel_address_decode (CAMEL_ADDRESS (address), curl->path);
who = camel_address_format (CAMEL_ADDRESS (address));
camel_object_unref (address);
camel_url_free (curl);
if (who == NULL)
who = g_strdup (strchr (uri, ':') + 1);
message = g_strdup_printf (format, who);
g_free (who);
exit:
mail_display_emit_status_message (display, message);
g_free (message);
}
static void
mail_display_iframe_created (GtkHTML *html,
GtkHTML *iframe)
{
g_signal_connect_swapped (
iframe, "button-press-event",
G_CALLBACK (mail_display_button_press_event), html);
}
static void
mail_display_class_init (EMailDisplayClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
GtkHTMLClass *html_class;
parent_class = g_type_class_peek_parent (class);
g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = mail_display_set_property;
object_class->get_property = mail_display_get_property;
object_class->dispose = mail_display_dispose;
widget_class = GTK_WIDGET_CLASS (class);
widget_class->realize = mail_display_realize;
widget_class->style_set = mail_display_style_set;
widget_class->button_press_event = mail_display_button_press_event;
widget_class->scroll_event = mail_display_scroll_event;
html_class = GTK_HTML_CLASS (class);
html_class->link_clicked = mail_display_link_clicked;
html_class->on_url = mail_display_on_url;
html_class->iframe_created = mail_display_iframe_created;
g_object_class_install_property (
object_class,
PROP_ANIMATE,
g_param_spec_boolean (
"animate",
"Animate Images",
NULL,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_CARET_MODE,
g_param_spec_boolean (
"caret-mode",
"Caret Mode",
NULL,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_FORMATTER,
g_param_spec_object (
"formatter",
"HTML Formatter",
NULL,
EM_TYPE_FORMAT_HTML,
G_PARAM_READWRITE));
signals[POPUP_EVENT] = g_signal_new (
"popup-event",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EMailDisplayClass, popup_event),
g_signal_accumulator_true_handled, NULL,
e_marshal_BOOLEAN__BOXED_POINTER_POINTER,
G_TYPE_BOOLEAN, 3,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
G_TYPE_POINTER,
G_TYPE_POINTER);
signals[STATUS_MESSAGE] = g_signal_new (
"status-message",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (EMailDisplayClass, status_message),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
}
static void
mail_display_init (EMailDisplay *display)
{
display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
}
GType
e_mail_display_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (type == 0)) {
static const GTypeInfo type_info = {
sizeof (EMailDisplayClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) mail_display_class_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (EMailDisplay),
0, /* n_preallocs */
(GInstanceInitFunc) mail_display_init,
NULL /* value_table */
};
type = g_type_register_static (
GTK_TYPE_HTML, "EMailDisplay", &type_info, 0);
}
return type;
}
gboolean
e_mail_display_get_animate (EMailDisplay *display)
{
/* XXX This is just here to maintain symmetry
* with e_mail_display_set_animate(). */
g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
return gtk_html_get_animate (GTK_HTML (display));
}
void
e_mail_display_set_animate (EMailDisplay *display,
gboolean animate)
{
/* XXX GtkHTML does not utilize GObject properties as well
* as it could. This just wraps gtk_html_set_animate()
* so we can get a "notify::animate" signal. */
g_return_if_fail (E_IS_MAIL_DISPLAY (display));
gtk_html_set_animate (GTK_HTML (display), animate);
g_object_notify (G_OBJECT (display), "animate");
}
gboolean
e_mail_display_get_caret_mode (EMailDisplay *display)
{
/* XXX This is just here to maintain symmetry
* with e_mail_display_get_caret_mode(). */
g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
return gtk_html_get_caret_mode (GTK_HTML (display));
}
void
e_mail_display_set_caret_mode (EMailDisplay *display,
gboolean caret_mode)
{
/* XXX GtkHTML does not utilize GObject properties as well
* as it could. This just wraps gtk_html_set_caret_mode()
* so we can get a "notify::caret-mode" signal. */
g_return_if_fail (E_IS_MAIL_DISPLAY (display));
gtk_html_set_caret_mode (GTK_HTML (display), caret_mode);
g_object_notify (G_OBJECT (display), "caret-mode");
}
EMFormatHTML *
e_mail_display_get_formatter (EMailDisplay *display)
{
g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
return display->priv->formatter;
}
void
e_mail_display_set_formatter (EMailDisplay *display,
EMFormatHTML *formatter)
{
g_return_if_fail (E_IS_MAIL_DISPLAY (display));
g_return_if_fail (EM_IS_FORMAT_HTML (formatter));
if (display->priv->formatter != NULL)
g_object_unref (display->priv->formatter);
display->priv->formatter = g_object_ref (formatter);
g_object_notify (G_OBJECT (display), "formatter");
}

83
mail/e-mail-display.h Normal file
View File

@ -0,0 +1,83 @@
/*
* e-mail-display.h
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifndef E_MAIL_DISPLAY_H
#define E_MAIL_DISPLAY_H
#include <gtkhtml/gtkhtml.h>
#include <mail/em-format-html.h>
/* Standard GObject macros */
#define E_TYPE_MAIL_DISPLAY \
(e_mail_display_get_type ())
#define E_MAIL_DISPLAY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST \
((obj), E_TYPE_MAIL_DISPLAY, EMailDisplay))
#define E_MAIL_DISPLAY_CLASS(cls) \
(G_TYPE_CHECK_CLASS_CAST \
((cls), E_TYPE_MAIL_DISPLAY, EMailDisplayClass))
#define E_IS_MAIL_DISPLAY(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE \
((obj), E_TYPE_MAIL_DISPLAY))
#define E_IS_MAIL_DISPLAY_CLASS(cls) \
(G_TYPE_CHECK_CLASS_TYPE \
((cls), E_TYPE_MAIL_DISPLAY))
#define E_MAIL_DISPLAY_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS \
((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayClass))
G_BEGIN_DECLS
typedef struct _EMailDisplay EMailDisplay;
typedef struct _EMailDisplayClass EMailDisplayClass;
typedef struct _EMailDisplayPrivate EMailDisplayPrivate;
struct _EMailDisplay {
GtkHTML parent;
EMailDisplayPrivate *priv;
};
struct _EMailDisplayClass {
GtkHTMLClass parent_class;
/* Signals */
gboolean (*popup_event) (EMailDisplay *display,
GdkEventButton *event,
const gchar *uri,
EMFormatPURI *puri);
void (*status_message) (EMailDisplay *display,
const gchar *status_message);
};
GType e_mail_display_get_type (void);
gboolean e_mail_display_get_animate (EMailDisplay *display);
void e_mail_display_set_animate (EMailDisplay *display,
gboolean animate);
gboolean e_mail_display_get_caret_mode (EMailDisplay *display);
void e_mail_display_set_caret_mode (EMailDisplay *display,
gboolean caret_mode);
EMFormatHTML * e_mail_display_get_formatter (EMailDisplay *display);
void e_mail_display_set_formatter (EMailDisplay *display,
EMFormatHTML *formatter);
G_END_DECLS
#endif /* E_MAIL_DISPLAY_H */

View File

@ -1620,40 +1620,6 @@ static GtkToggleActionEntry mail_reader_toggle_entries[] = {
FALSE }
};
static void
mail_reader_link_clicked_cb (EMailReader *reader,
const gchar *uri,
EMFormatHTMLDisplay *html_display)
{
GtkHTML *html;
GtkWindow *window;
MessageList *message_list;
const gchar *folder_uri;
html = EM_FORMAT_HTML (html_display)->html;
message_list = e_mail_reader_get_message_list (reader);
window = e_mail_reader_get_window (reader);
folder_uri = message_list->folder_uri;
if (g_str_has_prefix (uri, "##"))
return;
if (g_ascii_strncasecmp (uri, "mailto:", 7) == 0)
em_utils_compose_new_message_with_mailto (uri, folder_uri);
else if (*uri == '#')
gtk_html_jump_to_anchor (html, uri + 1);
else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0)
/* ignore */ ;
else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0)
/* ignore */ ;
else
e_show_uri (window, uri);
}
static gboolean
mail_reader_html_button_release_event_cb (EMailReader *reader,
GdkEventButton *button,
@ -2057,6 +2023,7 @@ e_mail_reader_init (EMailReader *reader)
MessageList *message_list;
GConfBridge *bridge;
GtkAction *action;
GtkHTML *html;
const gchar *action_name;
const gchar *key;
@ -2070,6 +2037,8 @@ e_mail_reader_init (EMailReader *reader)
shell = e_shell_module_get_shell (shell_module);
shell_settings = e_shell_get_shell_settings (shell);
html = EM_FORMAT_HTML (html_display)->html;
gtk_action_group_add_actions (
action_group, mail_reader_entries,
G_N_ELEMENTS (mail_reader_entries), reader);
@ -2136,7 +2105,7 @@ e_mail_reader_init (EMailReader *reader)
e_binding_new (
G_OBJECT (shell_settings), "mail-show-animated-images",
G_OBJECT (html_display), "animate");
G_OBJECT (html), "animate");
e_binding_new (
G_OBJECT (shell_settings), "mail-show-sender-photo",
@ -2147,14 +2116,10 @@ e_mail_reader_init (EMailReader *reader)
e_mutual_binding_new (
G_OBJECT (action), "active",
G_OBJECT (html_display), "caret-mode");
G_OBJECT (html), "caret-mode");
/* Connect signals. */
g_signal_connect_swapped (
html_display, "link-clicked",
G_CALLBACK (mail_reader_link_clicked_cb), reader);
g_signal_connect_swapped (
EM_FORMAT_HTML (html_display)->html, "button-release-event",
G_CALLBACK (mail_reader_html_button_release_event_cb), reader);

View File

@ -88,6 +88,19 @@ mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view)
e_mail_shell_view_update_sidebar (mail_shell_view);
}
static void
mail_shell_view_reader_status_message_cb (EMailShellView *mail_shell_view,
const gchar *status_message)
{
EShellView *shell_view;
EShellTaskbar *shell_taskbar;
shell_view = E_SHELL_VIEW (mail_shell_view);
shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
e_shell_taskbar_set_message (shell_taskbar, status_message);
}
static void
mail_shell_view_load_view_collection (EShellViewClass *shell_view_class)
{
@ -160,6 +173,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
EShellSettings *shell_settings;
EShellSidebar *shell_sidebar;
EShellWindow *shell_window;
EMFormatHTMLDisplay *html_display;
EMFolderTreeModel *folder_tree_model;
EMFolderTree *folder_tree;
RuleContext *context;
@ -168,6 +182,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
GtkUIManager *ui_manager;
MessageList *message_list;
EMailReader *reader;
GtkHTML *html;
const gchar *source;
guint merge_id;
gchar *uri;
@ -197,11 +212,14 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
priv->mail_shell_sidebar = g_object_ref (shell_sidebar);
reader = E_MAIL_READER (shell_content);
html_display = e_mail_reader_get_html_display (reader);
message_list = e_mail_reader_get_message_list (reader);
mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
html = EM_FORMAT_HTML (html_display)->html;
g_signal_connect_swapped (
folder_tree, "folder-selected",
G_CALLBACK (mail_shell_view_folder_tree_selected_cb),
@ -243,6 +261,11 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
G_CALLBACK (e_mail_shell_view_update_search_filter),
mail_shell_view);
g_signal_connect_swapped (
html, "status-message",
G_CALLBACK (mail_shell_view_reader_status_message_cb),
mail_shell_view);
e_mail_shell_view_actions_init (mail_shell_view);
e_mail_shell_view_update_search_filter (mail_shell_view);
e_mail_reader_init (reader);

View File

@ -238,7 +238,7 @@ emfv_init(GObject *o)
// em_format_set_session ((EMFormat *) emfv->preview, session);
// g_signal_connect(emfv->preview, "link_clicked", G_CALLBACK(emfv_format_link_clicked), emfv);
g_signal_connect(emfv->preview, "popup_event", G_CALLBACK(emfv_format_popup_event), emfv);
g_signal_connect (emfv->preview, "on_url", G_CALLBACK (emfv_on_url_cb), emfv);
// g_signal_connect (emfv->preview, "on_url", G_CALLBACK (emfv_on_url_cb), emfv);
// g_signal_connect (((EMFormatHTML *)emfv->preview)->html, "button-release-event", G_CALLBACK (emfv_on_html_button_released_cb), emfv);
//#ifdef ENABLE_PROFILING
// g_signal_connect(emfv->preview, "complete", G_CALLBACK (emfv_format_complete), emfv);
@ -268,7 +268,7 @@ emfv_class_init(GObjectClass *klass)
((EMFolderViewClass *)klass)->set_message = emfv_set_message;
((EMFolderViewClass *)klass)->activate = emfv_activate;
((EMFolderViewClass *)klass)->on_url = emfv_on_url;
// ((EMFolderViewClass *)klass)->on_url = emfv_on_url;
signals[EMFV_ON_URL] = g_signal_new ("on-url",
G_OBJECT_CLASS_TYPE (klass),
@ -1077,58 +1077,3 @@ emfv_setting_notify(GConfClient *gconf, guint cnxn_id, GConfEntry *entry, EMFold
break; }
}
}
static void
emfv_on_url (EMFolderView *emfv, const char *uri, const char *nice_uri)
{
if (emfv->statusbar_active) {
if (emfv->uic) {
bonobo_ui_component_set_status (emfv->uic, nice_uri, NULL);
/* Make sure the node keeps existing if nice_url == NULL */
if (!nice_uri)
bonobo_ui_component_set_translate (
emfv->uic, "/", "<status><item name=\"main\"/></status>", NULL);
}
}
}
static void
emfv_on_url_cb (GObject *emitter, const char *url, EMFolderView *emfv)
{
char *nice_url = NULL;
if (url) {
if (strncmp (url, "mailto:", 7) == 0) {
CamelInternetAddress *cia = camel_internet_address_new();
CamelURL *curl;
char *addr;
curl = camel_url_new(url, NULL);
camel_address_decode((CamelAddress *)cia, curl->path);
addr = camel_address_format((CamelAddress *)cia);
nice_url = g_strdup_printf (_("Click to mail %s"), addr&&addr[0]?addr:(url + 7));
g_free(addr);
camel_url_free(curl);
camel_object_unref(cia);
} else if (strncmp (url, "callto:", 7) == 0 || strncmp (url, "h323:", 5) == 0 || strncmp (url, "sip:", 4) == 0) {
CamelInternetAddress *cia = camel_internet_address_new();
CamelURL *curl;
char *addr;
curl = camel_url_new(url, NULL);
camel_address_decode((CamelAddress *)cia, curl->path);
addr = camel_address_format((CamelAddress *)cia);
nice_url = g_strdup_printf (_("Click to call %s"), addr&&addr[0]?addr:(url + 7));
g_free(addr);
camel_url_free(curl);
camel_object_unref(cia);
} else if (!strncmp (url, "##", 2)) {
nice_url = g_strdup (_("Click to hide/unhide addresses"));
} else
nice_url = g_strdup_printf (_("Click to open %s"), url);
}
g_signal_emit (emfv, signals[EMFV_ON_URL], 0, url, nice_url);
g_free (nice_url);
}

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,9 @@
#ifndef EM_FORMAT_HTML_DISPLAY_H
#define EM_FORMAT_HTML_DISPLAY_H
#include "mail/em-format-html.h"
#include "widgets/misc/e-attachment-bar.h"
#include <camel/camel-mime-part.h>
#include <mail/em-format-html.h>
#include <widgets/misc/e-attachment-bar.h>
/* Standard GObject macros */
#define EM_TYPE_FORMAT_HTML_DISPLAY \
@ -58,41 +59,32 @@ typedef struct _EMFormatHTMLDisplay EMFormatHTMLDisplay;
typedef struct _EMFormatHTMLDisplayClass EMFormatHTMLDisplayClass;
typedef struct _EMFormatHTMLDisplayPrivate EMFormatHTMLDisplayPrivate;
struct _CamelMimePart;
struct _EMFormatHTMLDisplay {
EMFormatHTML parent;
EMFormatHTMLDisplayPrivate *priv;
struct _ESearchingTokenizer *search_tok;
unsigned int nobar:1;
};
struct _EMFormatHTMLDisplayClass {
EMFormatHTMLClass parent_class;
/* a link clicked normally */
void (*link_clicked)(EMFormatHTMLDisplay *efhd, const char *uri);
void (*link_clicked) (EMFormatHTMLDisplay *efhd,
const gchar *uri);
/* a part or a link button pressed event */
int (*popup_event)(EMFormatHTMLDisplay *efhd, GdkEventButton *event, const char *uri, struct _CamelMimePart *part);
gint (*popup_event) (EMFormatHTMLDisplay *efhd,
GdkEventButton *event,
const gchar *uri,
CamelMimePart *part);
/* the mouse is over a link */
void (*on_url)(EMFormatHTMLDisplay *efhd, const char *uri);
void (*on_url) (EMFormatHTMLDisplay *efhd,
const gchar *uri);
};
GType em_format_html_display_get_type (void);
EMFormatHTMLDisplay *
em_format_html_display_new (void);
gboolean em_format_html_display_get_animate
(EMFormatHTMLDisplay *efhd);
void em_format_html_display_set_animate
(EMFormatHTMLDisplay *efhd,
gboolean animate);
gboolean em_format_html_display_get_caret_mode
(EMFormatHTMLDisplay *efhd);
void em_format_html_display_set_caret_mode
(EMFormatHTMLDisplay *efhd,
gboolean caret_mode);
void em_format_html_display_set_search
(EMFormatHTMLDisplay *efhd,
int type,

View File

@ -135,12 +135,7 @@ static void efh_gtkhtml_destroy(GtkHTML *html, EMFormatHTML *efh);
static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
static void efh_format_clone(EMFormat *emf, CamelFolder *folder, const char *uid, CamelMimeMessage *msg, EMFormat *emfsource);
static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt);
static void efh_format_source(EMFormat *, CamelStream *, CamelMimePart *);
static void efh_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *);
static void efh_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid);
static gboolean efh_busy(EMFormat *);
static void efh_builtin_init(EMFormatHTMLClass *efhc);
@ -151,6 +146,230 @@ static CamelDataCache *emfh_http_cache;
#define EMFH_HTTP_CACHE_PATH "http"
/* Sigh, this is so we have a cancellable, async rendering thread */
struct _format_msg {
MailMsg base;
EMFormatHTML *format;
EMFormat *format_source;
EMHTMLStream *estream;
CamelFolder *folder;
char *uid;
CamelMimeMessage *message;
};
static gchar *
efh_format_desc (struct _format_msg *m)
{
return g_strdup(_("Formatting message"));
}
static void
efh_format_exec (struct _format_msg *m)
{
struct _EMFormatHTMLJob *job;
struct _EMFormatPURITree *puri_level;
int cancelled = FALSE;
CamelURL *base;
if (m->format->html == NULL)
return;
camel_stream_printf (
(CamelStream *)m->estream,
"<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
"<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
"<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n",
e_color_to_value (
&m->format->priv->colors[
EM_FORMAT_HTML_COLOR_BODY]),
e_color_to_value (
&m->format->priv->colors[
EM_FORMAT_HTML_COLOR_HEADER]));
/* <insert top-header stuff here> */
if (((EMFormat *)m->format)->mode == EM_FORMAT_SOURCE) {
em_format_format_source((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message);
} else {
const EMFormatHandler *handle;
handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/prefix");
if (handle)
handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/rfc822");
if (handle)
handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/post-header-closure");
if (handle && !((EMFormat *)m->format)->print)
handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
}
camel_stream_flush((CamelStream *)m->estream);
puri_level = ((EMFormat *)m->format)->pending_uri_level;
base = ((EMFormat *)m->format)->base;
do {
/* now dispatch any added tasks ... */
g_mutex_lock(m->format->priv->lock);
while ((job = (struct _EMFormatHTMLJob *)e_dlist_remhead(&m->format->priv->pending_jobs))) {
g_mutex_unlock(m->format->priv->lock);
/* This is an implicit check to see if the gtkhtml has been destroyed */
if (!cancelled)
cancelled = m->format->html == NULL;
/* Now do an explicit check for user cancellation */
if (!cancelled)
cancelled = camel_operation_cancel_check(NULL);
/* call jobs even if cancelled, so they can clean up resources */
((EMFormat *)m->format)->pending_uri_level = job->puri_level;
if (job->base)
((EMFormat *)m->format)->base = job->base;
job->callback(job, cancelled);
((EMFormat *)m->format)->base = base;
/* clean up the job */
camel_object_unref(job->stream);
if (job->base)
camel_url_free(job->base);
g_free(job);
g_mutex_lock(m->format->priv->lock);
}
g_mutex_unlock(m->format->priv->lock);
if (m->estream) {
/* Closing this base stream can queue more jobs, so we need
to check the list again after we've finished */
d(printf("out of jobs, closing root stream\n"));
camel_stream_write_string((CamelStream *)m->estream, "</body>\n</html>\n");
camel_stream_close((CamelStream *)m->estream);
camel_object_unref(m->estream);
m->estream = NULL;
}
/* e_dlist_empty is atomic and doesn't need locking */
} while (!e_dlist_empty(&m->format->priv->pending_jobs));
d(printf("out of jobs, done\n"));
((EMFormat *)m->format)->pending_uri_level = puri_level;
}
static void
efh_format_done (struct _format_msg *m)
{
d(printf("formatting finished\n"));
m->format->priv->format_id = -1;
m->format->priv->load_images_now = FALSE;
m->format->state = EM_FORMAT_HTML_STATE_NONE;
g_signal_emit_by_name(m->format, "complete");
}
static void
efh_format_free (struct _format_msg *m)
{
d(printf("formatter freed\n"));
g_object_unref(m->format);
if (m->estream) {
camel_stream_close((CamelStream *)m->estream);
camel_object_unref(m->estream);
}
if (m->folder)
camel_object_unref(m->folder);
g_free(m->uid);
if (m->message)
camel_object_unref(m->message);
if (m->format_source)
g_object_unref(m->format_source);
}
static MailMsgInfo efh_format_info = {
sizeof (struct _format_msg),
(MailMsgDescFunc) efh_format_desc,
(MailMsgExecFunc) efh_format_exec,
(MailMsgDoneFunc) efh_format_done,
(MailMsgFreeFunc) efh_format_free
};
static gboolean
efh_format_timeout(struct _format_msg *m)
{
GtkHTMLStream *hstream;
EMFormatHTML *efh = m->format;
struct _EMFormatHTMLPrivate *p = efh->priv;
if (m->format->html == NULL) {
mail_msg_unref(m);
return FALSE;
}
d(printf("timeout called ...\n"));
if (p->format_id != -1) {
d(printf(" still waiting for cancellation to take effect, waiting ...\n"));
return TRUE;
}
g_return_val_if_fail (e_dlist_empty(&p->pending_jobs), FALSE);
d(printf(" ready to go, firing off format thread\n"));
/* call super-class to kick it off */
EM_FORMAT_CLASS (parent_class)->format_clone (
EM_FORMAT (efh), m->folder, m->uid,
m->message, m->format_source);
em_format_html_clear_pobject(m->format);
/* FIXME: method off EMFormat? */
if (((EMFormat *)efh)->valid) {
camel_cipher_validity_free(((EMFormat *)efh)->valid);
((EMFormat *)efh)->valid = NULL;
((EMFormat *)efh)->valid_parent = NULL;
}
if (m->message == NULL) {
hstream = gtk_html_begin(efh->html);
gtk_html_stream_close(hstream, GTK_HTML_STREAM_OK);
mail_msg_unref(m);
p->last_part = NULL;
} else {
efh->state = EM_FORMAT_HTML_STATE_RENDERING;
if (p->last_part != m->message) {
hstream = gtk_html_begin (efh->html);
gtk_html_stream_printf (hstream, "<h5>%s</h5>", _("Formatting Message..."));
gtk_html_stream_close (hstream, GTK_HTML_STREAM_OK);
}
hstream = NULL;
m->estream = (EMHTMLStream *)em_html_stream_new(efh->html, hstream);
if (p->last_part == m->message) {
em_html_stream_set_flags (m->estream,
GTK_HTML_BEGIN_KEEP_SCROLL | GTK_HTML_BEGIN_KEEP_IMAGES
| GTK_HTML_BEGIN_BLOCK_UPDATES | GTK_HTML_BEGIN_BLOCK_IMAGES);
} else {
/* clear cache of inline-scanned text parts */
g_hash_table_remove_all(p->text_inline_parts);
p->last_part = m->message;
}
efh->priv->format_id = m->base.seq;
mail_msg_unordered_push (m);
}
efh->priv->format_timeout_id = 0;
efh->priv->format_timeout_msg = NULL;
return FALSE;
}
static void
efh_free_cache(struct _EMFormatHTMLCache *efhc)
{
@ -370,6 +589,143 @@ efh_finalize (GObject *object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
efh_format_clone (EMFormat *emf,
CamelFolder *folder,
const gchar *uid,
CamelMimeMessage *msg,
EMFormat *emfsource)
{
EMFormatHTML *efh = EM_FORMAT_HTML (emf);
struct _format_msg *m;
/* How to sub-class ? Might need to adjust api ... */
if (efh->html == NULL)
return;
d(printf("efh_format called\n"));
if (efh->priv->format_timeout_id != 0) {
d(printf(" timeout for last still active, removing ...\n"));
g_source_remove(efh->priv->format_timeout_id);
efh->priv->format_timeout_id = 0;
mail_msg_unref(efh->priv->format_timeout_msg);
efh->priv->format_timeout_msg = NULL;
}
if (emfsource != NULL)
g_object_ref (emfsource);
if (folder != NULL)
camel_object_ref (folder);
if (msg != NULL)
camel_object_ref (msg);
m = mail_msg_new (&efh_format_info);
m->format = g_object_ref (emf);
m->format_source = emfsource;
m->folder = folder;
m->uid = g_strdup (uid);
m->message = msg;
if (efh->priv->format_id == -1) {
d(printf(" idle, forcing format\n"));
efh_format_timeout (m);
} else {
d(printf(" still busy, cancelling and queuing wait\n"));
/* cancel and poll for completion */
mail_msg_cancel (efh->priv->format_id);
efh->priv->format_timeout_msg = m;
efh->priv->format_timeout_id = g_timeout_add (
100, (GSourceFunc) efh_format_timeout, m);
}
}
static void
efh_format_error (EMFormat *emf,
CamelStream *stream,
const gchar *txt)
{
gchar *html;
html = camel_text_to_html (
txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
camel_stream_printf (
stream, "<em><font color=\"red\">%s</font></em><br>", html);
g_free (html);
}
static void
efh_format_source (EMFormat *emf,
CamelStream *stream,
CamelMimePart *part)
{
CamelStreamFilter *filtered_stream;
CamelMimeFilter *filter;
CamelDataWrapper *dw = (CamelDataWrapper *) part;
filtered_stream = camel_stream_filter_new_with_stream (stream);
filter = camel_mime_filter_tohtml_new (
CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
camel_stream_filter_add (filtered_stream, filter);
camel_object_unref (filter);
camel_stream_write_string (stream, "<table><tr><td><tt>");
em_format_format_text (emf, (CamelStream *) filtered_stream, dw);
camel_object_unref (filtered_stream);
camel_stream_write_string(stream, "</tt></td></tr></table>");
}
static void
efh_format_attachment (EMFormat *emf,
CamelStream *stream,
CamelMimePart *part,
const gchar *mime_type,
const EMFormatHandler *handle)
{
char *text, *html;
/* we display all inlined attachments only */
/* this could probably be cleaned up ... */
camel_stream_write_string (
stream,
"<table border=1 cellspacing=0 cellpadding=0><tr><td>"
"<table width=10 cellspacing=0 cellpadding=0>"
"<tr><td></td></tr></table></td>"
"<td><table width=3 cellspacing=0 cellpadding=0>"
"<tr><td></td></tr></table></td><td><font size=-1>\n");
/* output some info about it */
text = em_format_describe_part(part, mime_type);
html = camel_text_to_html (
text, ((EMFormatHTML *)emf)->text_html_flags &
CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
camel_stream_write_string (stream, html);
g_free (html);
g_free (text);
camel_stream_write_string (stream, "</font></td></tr><tr></table>");
if (handle && em_format_is_inline (emf, emf->part_id->str, part, handle))
handle->handler (emf, stream, part, handle);
}
static gboolean
efh_busy (EMFormat *emf)
{
EMFormatHTMLPrivate *priv;
priv = EM_FORMAT_HTML_GET_PRIVATE (emf);
return (priv->format_id != -1);
}
static void
efh_base_init (EMFormatHTMLClass *class)
{
@ -399,6 +755,8 @@ efh_class_init (EMFormatHTMLClass *class)
format_class->format_secure = efh_format_secure;
format_class->busy = efh_busy;
class->html_widget_type = GTK_TYPE_HTML;
g_object_class_install_property (
object_class,
PROP_BODY_COLOR,
@ -516,8 +874,10 @@ efh_class_init (EMFormatHTMLClass *class)
}
static void
efh_init (EMFormatHTML *efh)
efh_init (EMFormatHTML *efh,
EMFormatHTMLClass *class)
{
GtkHTML *html;
GdkColor *color;
efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh);
@ -531,32 +891,35 @@ efh_init (EMFormatHTML *efh)
(GDestroyNotify) NULL,
(GDestroyNotify) efh_free_cache);
efh->html = (GtkHTML *) gtk_html_new ();
g_object_ref_sink(efh->html);
html = g_object_new (class->html_widget_type, NULL);
efh->html = g_object_ref_sink (html);
gtk_html_set_blocking (efh->html, FALSE);
gtk_html_set_caret_first_focus_anchor (efh->html, EFM_MESSAGE_START_ANAME);
gtk_html_set_blocking (html, FALSE);
gtk_html_set_caret_first_focus_anchor (html, EFM_MESSAGE_START_ANAME);
gtk_html_set_default_content_type (html, "text/html; charset=utf-8");
gtk_html_set_editable (html, FALSE);
gtk_html_set_default_content_type(efh->html, "text/html; charset=utf-8");
gtk_html_set_editable(efh->html, FALSE);
g_signal_connect(efh->html, "url_requested", G_CALLBACK(efh_url_requested), efh);
g_signal_connect(efh->html, "object_requested", G_CALLBACK(efh_object_requested), efh);
g_signal_connect (
html, "url-requested",
G_CALLBACK (efh_url_requested), efh);
g_signal_connect (
html, "object-requested",
G_CALLBACK (efh_object_requested), efh);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY];
gdk_color_parse ("0xeeeeee", color);
gdk_color_parse ("#eeeeee", color);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT];
gdk_color_parse ("0xffffff", color);
gdk_color_parse ("#ffffff", color);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME];
gdk_color_parse ("0x3f3f3f", color);
gdk_color_parse ("#3f3f3f", color);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER];
gdk_color_parse ("0xeeeeee", color);
gdk_color_parse ("#eeeeee", color);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_TEXT];
gdk_color_parse ("0x000000", color);
gdk_color_parse ("#000000", color);
efh->text_html_flags =
CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
@ -590,18 +953,13 @@ em_format_html_get_type (void)
};
type = g_type_register_static (
em_format_get_type(), "EMFormatHTML", &type_info, 0);
em_format_get_type(), "EMFormatHTML",
&type_info, G_TYPE_FLAG_ABSTRACT);
}
return type;
}
EMFormatHTML *
em_format_html_new(void)
{
return g_object_new (EM_TYPE_FORMAT_HTML, NULL);
}
void
em_format_html_load_images (EMFormatHTML *efh)
{
@ -688,7 +1046,7 @@ em_format_html_get_image_loading_policy (EMFormatHTML *efh)
{
g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0);
return efh->priv->image_loading_policy;;
return efh->priv->image_loading_policy;
}
void
@ -1711,284 +2069,6 @@ efh_builtin_init(EMFormatHTMLClass *efhc)
/* ********************************************************************** */
/* Sigh, this is so we have a cancellable, async rendering thread */
struct _format_msg {
MailMsg base;
EMFormatHTML *format;
EMFormat *format_source;
EMHTMLStream *estream;
CamelFolder *folder;
char *uid;
CamelMimeMessage *message;
};
static gchar *
efh_format_desc (struct _format_msg *m)
{
return g_strdup(_("Formatting message"));
}
static void
efh_format_exec (struct _format_msg *m)
{
struct _EMFormatHTMLJob *job;
struct _EMFormatPURITree *puri_level;
int cancelled = FALSE;
CamelURL *base;
if (m->format->html == NULL)
return;
camel_stream_printf (
(CamelStream *)m->estream,
"<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
"<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
"<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n",
e_color_to_value (
&m->format->priv->colors[
EM_FORMAT_HTML_COLOR_BODY]),
e_color_to_value (
&m->format->priv->colors[
EM_FORMAT_HTML_COLOR_HEADER]));
/* <insert top-header stuff here> */
if (((EMFormat *)m->format)->mode == EM_FORMAT_SOURCE) {
em_format_format_source((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message);
} else {
const EMFormatHandler *handle;
handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/prefix");
if (handle)
handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/rfc822");
if (handle)
handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/post-header-closure");
if (handle && !((EMFormat *)m->format)->print)
handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
}
camel_stream_flush((CamelStream *)m->estream);
puri_level = ((EMFormat *)m->format)->pending_uri_level;
base = ((EMFormat *)m->format)->base;
do {
/* now dispatch any added tasks ... */
g_mutex_lock(m->format->priv->lock);
while ((job = (struct _EMFormatHTMLJob *)e_dlist_remhead(&m->format->priv->pending_jobs))) {
g_mutex_unlock(m->format->priv->lock);
/* This is an implicit check to see if the gtkhtml has been destroyed */
if (!cancelled)
cancelled = m->format->html == NULL;
/* Now do an explicit check for user cancellation */
if (!cancelled)
cancelled = camel_operation_cancel_check(NULL);
/* call jobs even if cancelled, so they can clean up resources */
((EMFormat *)m->format)->pending_uri_level = job->puri_level;
if (job->base)
((EMFormat *)m->format)->base = job->base;
job->callback(job, cancelled);
((EMFormat *)m->format)->base = base;
/* clean up the job */
camel_object_unref(job->stream);
if (job->base)
camel_url_free(job->base);
g_free(job);
g_mutex_lock(m->format->priv->lock);
}
g_mutex_unlock(m->format->priv->lock);
if (m->estream) {
/* Closing this base stream can queue more jobs, so we need
to check the list again after we've finished */
d(printf("out of jobs, closing root stream\n"));
camel_stream_write_string((CamelStream *)m->estream, "</body>\n</html>\n");
camel_stream_close((CamelStream *)m->estream);
camel_object_unref(m->estream);
m->estream = NULL;
}
/* e_dlist_empty is atomic and doesn't need locking */
} while (!e_dlist_empty(&m->format->priv->pending_jobs));
d(printf("out of jobs, done\n"));
((EMFormat *)m->format)->pending_uri_level = puri_level;
}
static void
efh_format_done (struct _format_msg *m)
{
d(printf("formatting finished\n"));
m->format->priv->format_id = -1;
m->format->priv->load_images_now = FALSE;
m->format->state = EM_FORMAT_HTML_STATE_NONE;
g_signal_emit_by_name(m->format, "complete");
}
static void
efh_format_free (struct _format_msg *m)
{
d(printf("formatter freed\n"));
g_object_unref(m->format);
if (m->estream) {
camel_stream_close((CamelStream *)m->estream);
camel_object_unref(m->estream);
}
if (m->folder)
camel_object_unref(m->folder);
g_free(m->uid);
if (m->message)
camel_object_unref(m->message);
if (m->format_source)
g_object_unref(m->format_source);
}
static MailMsgInfo efh_format_info = {
sizeof (struct _format_msg),
(MailMsgDescFunc) efh_format_desc,
(MailMsgExecFunc) efh_format_exec,
(MailMsgDoneFunc) efh_format_done,
(MailMsgFreeFunc) efh_format_free
};
static gboolean
efh_format_timeout(struct _format_msg *m)
{
GtkHTMLStream *hstream;
EMFormatHTML *efh = m->format;
struct _EMFormatHTMLPrivate *p = efh->priv;
if (m->format->html == NULL) {
mail_msg_unref(m);
return FALSE;
}
d(printf("timeout called ...\n"));
if (p->format_id != -1) {
d(printf(" still waiting for cancellation to take effect, waiting ...\n"));
return TRUE;
}
g_return_val_if_fail (e_dlist_empty(&p->pending_jobs), FALSE);
d(printf(" ready to go, firing off format thread\n"));
/* call super-class to kick it off */
EM_FORMAT_CLASS (parent_class)->format_clone (
EM_FORMAT (efh), m->folder, m->uid,
m->message, m->format_source);
em_format_html_clear_pobject(m->format);
/* FIXME: method off EMFormat? */
if (((EMFormat *)efh)->valid) {
camel_cipher_validity_free(((EMFormat *)efh)->valid);
((EMFormat *)efh)->valid = NULL;
((EMFormat *)efh)->valid_parent = NULL;
}
if (m->message == NULL) {
hstream = gtk_html_begin(efh->html);
gtk_html_stream_close(hstream, GTK_HTML_STREAM_OK);
mail_msg_unref(m);
p->last_part = NULL;
} else {
efh->state = EM_FORMAT_HTML_STATE_RENDERING;
if (p->last_part != m->message) {
hstream = gtk_html_begin (efh->html);
gtk_html_stream_printf (hstream, "<h5>%s</h5>", _("Formatting Message..."));
gtk_html_stream_close (hstream, GTK_HTML_STREAM_OK);
}
hstream = NULL;
m->estream = (EMHTMLStream *)em_html_stream_new(efh->html, hstream);
if (p->last_part == m->message) {
em_html_stream_set_flags (m->estream,
GTK_HTML_BEGIN_KEEP_SCROLL | GTK_HTML_BEGIN_KEEP_IMAGES
| GTK_HTML_BEGIN_BLOCK_UPDATES | GTK_HTML_BEGIN_BLOCK_IMAGES);
} else {
/* clear cache of inline-scanned text parts */
g_hash_table_remove_all(p->text_inline_parts);
p->last_part = m->message;
}
efh->priv->format_id = m->base.seq;
mail_msg_unordered_push (m);
}
efh->priv->format_timeout_id = 0;
efh->priv->format_timeout_msg = NULL;
return FALSE;
}
static void efh_format_clone(EMFormat *emf, CamelFolder *folder, const char *uid, CamelMimeMessage *msg, EMFormat *emfsource)
{
EMFormatHTML *efh = (EMFormatHTML *)emf;
struct _format_msg *m;
/* How to sub-class ? Might need to adjust api ... */
if (efh->html == NULL)
return;
d(printf("efh_format called\n"));
if (efh->priv->format_timeout_id != 0) {
d(printf(" timeout for last still active, removing ...\n"));
g_source_remove(efh->priv->format_timeout_id);
efh->priv->format_timeout_id = 0;
mail_msg_unref(efh->priv->format_timeout_msg);
efh->priv->format_timeout_msg = NULL;
}
m = mail_msg_new(&efh_format_info);
m->format = (EMFormatHTML *)emf;
g_object_ref(emf);
m->format_source = emfsource;
if (emfsource)
g_object_ref(emfsource);
m->folder = folder;
if (folder)
camel_object_ref(folder);
m->uid = g_strdup(uid);
m->message = msg;
if (msg)
camel_object_ref(msg);
if (efh->priv->format_id == -1) {
d(printf(" idle, forcing format\n"));
efh_format_timeout(m);
} else {
d(printf(" still busy, cancelling and queuing wait\n"));
/* cancel and poll for completion */
mail_msg_cancel(efh->priv->format_id);
efh->priv->format_timeout_msg = m;
efh->priv->format_timeout_id = g_timeout_add(100, (GSourceFunc)efh_format_timeout, m);
}
}
static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt)
{
char *html;
html = camel_text_to_html (txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL|CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
camel_stream_printf(stream, "<em><font color=\"red\">%s</font></em><br>", html);
g_free(html);
}
static void
efh_format_text_header (EMFormatHTML *emfh, CamelStream *stream, const char *label, const char *value, guint32 flags)
{
@ -2563,7 +2643,8 @@ efh_format_headers(EMFormatHTML *efh, CamelStream *stream, CamelMedium *part)
}
}
static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
static void
efh_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
{
const EMFormatHandler *handle;
@ -2595,57 +2676,3 @@ static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart
emf->valid = save;
emf->valid_parent = save_parent;
}
static void efh_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
{
CamelStreamFilter *filtered_stream;
CamelMimeFilter *html_filter;
CamelDataWrapper *dw = (CamelDataWrapper *)part;
filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
html_filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL
| CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
| CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
camel_stream_filter_add(filtered_stream, html_filter);
camel_object_unref(html_filter);
camel_stream_write_string((CamelStream *)stream, "<table><tr><td><tt>");
em_format_format_text(emf, (CamelStream *)filtered_stream, dw);
camel_object_unref(filtered_stream);
camel_stream_write_string(stream, "</tt></td></tr></table>");
}
static void
efh_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type, const EMFormatHandler *handle)
{
char *text, *html;
/* we display all inlined attachments only */
/* this could probably be cleaned up ... */
camel_stream_write_string(stream,
"<table border=1 cellspacing=0 cellpadding=0><tr><td>"
"<table width=10 cellspacing=0 cellpadding=0>"
"<tr><td></td></tr></table></td>"
"<td><table width=3 cellspacing=0 cellpadding=0>"
"<tr><td></td></tr></table></td><td><font size=-1>\n");
/* output some info about it */
text = em_format_describe_part(part, mime_type);
html = camel_text_to_html(text, ((EMFormatHTML *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
camel_stream_write_string(stream, html);
g_free(html);
g_free(text);
camel_stream_write_string(stream, "</font></td></tr><tr></table>");
if (handle && em_format_is_inline(emf, emf->part_id->str, part, handle))
handle->handler(emf, stream, part, handle);
}
static gboolean
efh_busy(EMFormat *emf)
{
return (((EMFormatHTML *)emf)->priv->format_id != -1);
}

View File

@ -22,7 +22,7 @@
*/
/*
Concrete class for formatting mails to html
Abstract class for formatting mails to html
*/
#ifndef EM_FORMAT_HTML_H
@ -226,10 +226,11 @@ struct _EMFormatHTML {
struct _EMFormatHTMLClass {
EMFormatClass parent_class;
GType html_widget_type;
};
GType em_format_html_get_type (void);
EMFormatHTML * em_format_html_new (void);
void em_format_html_load_images (EMFormatHTML *efh);
void em_format_html_get_color (EMFormatHTML *efh,
EMFormatHTMLColorType type,

View File

@ -27,84 +27,39 @@
#endif
#include <stdio.h>
#include <gtkhtml/gtkhtml.h>
#include <gtkhtml/gtkhtml-stream.h>
#include <gtk/gtk.h>
#include "em-html-stream.h"
#define d(x)
static void em_html_stream_class_init (EMHTMLStreamClass *klass);
static void em_html_stream_init (CamelObject *object);
static void em_html_stream_finalize (CamelObject *object);
static ssize_t emhs_sync_write(CamelStream *stream, const char *buffer, size_t n);
static int emhs_sync_close(CamelStream *stream);
static int emhs_sync_flush(CamelStream *stream);
static EMSyncStreamClass *parent_class = NULL;
CamelType
em_html_stream_get_type (void)
{
static CamelType type = CAMEL_INVALID_TYPE;
if (type == CAMEL_INVALID_TYPE) {
parent_class = (EMSyncStreamClass *)em_sync_stream_get_type();
type = camel_type_register (em_sync_stream_get_type(),
"EMHTMLStream",
sizeof (EMHTMLStream),
sizeof (EMHTMLStreamClass),
(CamelObjectClassInitFunc) em_html_stream_class_init,
NULL,
(CamelObjectInitFunc) em_html_stream_init,
(CamelObjectFinalizeFunc) em_html_stream_finalize);
}
return type;
}
static void
em_html_stream_class_init (EMHTMLStreamClass *klass)
{
((EMSyncStreamClass *)klass)->sync_write = emhs_sync_write;
((EMSyncStreamClass *)klass)->sync_flush = emhs_sync_flush;
((EMSyncStreamClass *)klass)->sync_close = emhs_sync_close;
}
static void
em_html_stream_init (CamelObject *object)
{
/*EMHTMLStream *emhs = (EMHTMLStream *)object;*/
}
static void
emhs_cleanup(EMHTMLStream *emhs)
emhs_cleanup (EMHTMLStream *emhs)
{
if (emhs->sync.cancel && emhs->html_stream)
gtk_html_stream_close (emhs->html_stream, GTK_HTML_STREAM_ERROR);
gtk_html_stream_close (
emhs->html_stream, GTK_HTML_STREAM_ERROR);
emhs->html_stream = NULL;
emhs->sync.cancel = TRUE;
g_signal_handler_disconnect(emhs->html, emhs->destroy_id);
g_object_unref(emhs->html);
g_signal_handler_disconnect (emhs->html, emhs->destroy_id);
g_object_unref (emhs->html);
emhs->html = NULL;
}
static void
em_html_stream_finalize (CamelObject *object)
emhs_gtkhtml_destroy (GtkHTML *html,
EMHTMLStream *emhs)
{
EMHTMLStream *emhs = (EMHTMLStream *)object;
d(printf("%p: finalising stream\n", object));
if (emhs->html_stream) {
d(printf("%p: html stream still open - error\n", object));
/* set 'in finalise' flag */
camel_stream_close((CamelStream *)emhs);
}
emhs->sync.cancel = TRUE;
emhs_cleanup (emhs);
}
static ssize_t
emhs_sync_write(CamelStream *stream, const char *buffer, size_t n)
emhs_sync_write (CamelStream *stream,
const char *buffer,
size_t n)
{
EMHTMLStream *emhs = EM_HTML_STREAM (stream);
@ -112,9 +67,10 @@ emhs_sync_write(CamelStream *stream, const char *buffer, size_t n)
return -1;
if (emhs->html_stream == NULL)
emhs->html_stream = gtk_html_begin_full (emhs->html, NULL, NULL, emhs->flags);
emhs->html_stream = gtk_html_begin_full (
emhs->html, NULL, NULL, emhs->flags);
gtk_html_stream_write(emhs->html_stream, buffer, n);
gtk_html_stream_write (emhs->html_stream, buffer, n);
return (ssize_t) n;
}
@ -133,48 +89,93 @@ emhs_sync_flush(CamelStream *stream)
}
static int
emhs_sync_close(CamelStream *stream)
emhs_sync_close (CamelStream *stream)
{
EMHTMLStream *emhs = (EMHTMLStream *)stream;
if (emhs->html_stream == NULL)
return -1;
gtk_html_stream_close(emhs->html_stream, GTK_HTML_STREAM_OK);
emhs_cleanup(emhs);
gtk_html_stream_close (emhs->html_stream, GTK_HTML_STREAM_OK);
emhs_cleanup (emhs);
return 0;
}
static void
emhs_gtkhtml_destroy(struct _GtkHTML *html, EMHTMLStream *emhs)
em_html_stream_class_init (EMHTMLStreamClass *class)
{
d(printf("%p: emhs gtkhtml destroy\n", emhs));
emhs->sync.cancel = TRUE;
emhs_cleanup(emhs);
EMSyncStreamClass *sync_stream_class;
parent_class = (EMSyncStreamClass *)em_sync_stream_get_type();
sync_stream_class = EM_SYNC_STREAM_CLASS (class);
sync_stream_class->sync_write = emhs_sync_write;
sync_stream_class->sync_flush = emhs_sync_flush;
sync_stream_class->sync_close = emhs_sync_close;
}
static void
em_html_stream_init (EMHTMLStream *emhs)
{
}
static void
em_html_stream_finalize (EMHTMLStream *emhs)
{
if (emhs->html_stream) {
/* set 'in finalise' flag */
camel_stream_close (CAMEL_STREAM (emhs));
}
}
CamelType
em_html_stream_get_type (void)
{
static CamelType type = CAMEL_INVALID_TYPE;
if (G_UNLIKELY (type == CAMEL_INVALID_TYPE)) {
type = camel_type_register (
em_sync_stream_get_type(),
"EMHTMLStream",
sizeof (EMHTMLStream),
sizeof (EMHTMLStreamClass),
(CamelObjectClassInitFunc) em_html_stream_class_init,
NULL,
(CamelObjectInitFunc) em_html_stream_init,
(CamelObjectFinalizeFunc) em_html_stream_finalize);
}
return type;
}
/* TODO: Could pass NULL for html_stream, and do a gtk_html_begin
on first data -> less flashing */
CamelStream *
em_html_stream_new(struct _GtkHTML *html, struct _GtkHTMLStream *html_stream)
em_html_stream_new (GtkHTML *html,
GtkHTMLStream *html_stream)
{
EMHTMLStream *new;
g_return_val_if_fail (GTK_IS_HTML (html), NULL);
new = EM_HTML_STREAM (camel_object_new (EM_HTML_STREAM_TYPE));
new->html_stream = html_stream;
new->html = html;
new->html = g_object_ref (html);
new->flags = 0;
g_object_ref(html);
new->destroy_id = g_signal_connect(html, "destroy", G_CALLBACK(emhs_gtkhtml_destroy), new);
new->destroy_id = g_signal_connect (
html, "destroy",
G_CALLBACK (emhs_gtkhtml_destroy), new);
em_sync_stream_set_buffer_size(&new->sync, 8192);
em_sync_stream_set_buffer_size (&new->sync, 8192);
return (CamelStream *)new;
return CAMEL_STREAM (new);
}
void
em_html_stream_set_flags (EMHTMLStream *emhs, GtkHTMLBeginFlags flags)
{
g_return_if_fail (EM_IS_HTML_STREAM (emhs));
emhs->flags = flags;
}

View File

@ -24,44 +24,47 @@
#ifndef EM_HTML_STREAM_H
#define EM_HTML_STREAM_H
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */
#include <gtkhtml/gtkhtml.h>
#include <gtkhtml/gtkhtml-stream.h>
#include <mail/em-sync-stream.h>
#define EM_HTML_STREAM_TYPE (em_html_stream_get_type ())
#define EM_HTML_STREAM(obj) (CAMEL_CHECK_CAST((obj), EM_HTML_STREAM_TYPE, EMHTMLStream))
#define EM_HTML_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_HTML_STREAM_TYPE, EMHTMLStreamClass))
#define EM_IS_HTML_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_HTML_STREAM_TYPE))
#define EM_HTML_STREAM_TYPE \
(em_html_stream_get_type ())
#define EM_HTML_STREAM(obj) \
(CAMEL_CHECK_CAST \
((obj), EM_HTML_STREAM_TYPE, EMHTMLStream))
#define EM_HTML_STREAM_CLASS(cls) \
(CAMEL_CHECK_CLASS_CAST \
((cls), EM_HTML_STREAM_TYPE, EMHTMLStreamClass))
#define EM_IS_HTML_STREAM(obj) \
(CAMEL_CHECK_TYPE \
((obj), EM_HTML_STREAM_TYPE))
struct _GtkHTML;
struct _GtkHTMLStream;
G_BEGIN_DECLS
#include "mail/em-sync-stream.h"
typedef struct _EMHTMLStream EMHTMLStream;
typedef struct _EMHTMLStreamClass EMHTMLStreamClass;
typedef struct _EMHTMLStream {
struct _EMHTMLStream {
EMSyncStream sync;
guint destroy_id;
struct _GtkHTML *html;
struct _GtkHTMLStream *html_stream;
GtkHTML *html;
GtkHTMLStream *html_stream;
GtkHTMLBeginFlags flags;
} EMHTMLStream;
};
typedef struct {
struct _EMHTMLStreamClass {
EMSyncStreamClass parent_class;
} EMHTMLStreamClass;
};
CamelType em_html_stream_get_type (void);
CamelStream * em_html_stream_new (GtkHTML *html,
GtkHTMLStream *html_stream);
void em_html_stream_set_flags (EMHTMLStream *emhs,
GtkHTMLBeginFlags flags);
CamelType em_html_stream_get_type (void);
/* the html_stream is closed when we are finalised (with an error), or closed (ok) */
CamelStream *em_html_stream_new(struct _GtkHTML *html, struct _GtkHTMLStream *html_stream);
void em_html_stream_set_flags (EMHTMLStream *emhs, GtkHTMLBeginFlags flags);
#ifdef __cplusplus
}
#endif /* __cplusplus */
G_END_DECLS
#endif /* EM_HTML_STREAM_H */