/*
* 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
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#include "e-mail-display.h"
#include
#include
#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_set_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");
}