aboutsummaryrefslogblamecommitdiffstats
path: root/mail/e-mail-display.c
blob: 3719dc09f169355267df689bad31831338e4e1d3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                    
                                                                             







                                                        
                   



                          


                                   






                                                          

                                 





                        

                         










                                  





















































































































































































                                                                         



























                                                                            
                                         



                                               



                         


















                                                                                 
                                         






























                                                                           




























                                                                             
































































                                                             





                                                            




























                                                                       





                                                                        
















                                                                       




                                                  































                                                                            

                                                        
 

                                                                      








































































































































                                                                         












                                                                       



























                                                                           

                                                      





























                                            









                                            

























                                                                    




                                     
                                                             































                                                                           

























































                                                                      
                                                       










































                                                                      


































































































































































                                                                           
/*
 * 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 <config.h>
#include <string.h>
#include <glib/gi18n.h>

#include "e-util/e-util.h"
#include "e-util/e-plugin-ui.h"
#include "mail/em-composer-utils.h"
#include "mail/em-utils.h"

#define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate))

struct _EMailDisplayPrivate {
    EMFormatHTML *formatter;
    GtkUIManager *ui_manager;
    gchar *selected_uri;
};

enum {
    PROP_0,
    PROP_ANIMATE,
    PROP_CARET_MODE,
    PROP_FORMATTER,
    PROP_SELECTED_URI
};

enum {
    POPUP_EVENT,
    STATUS_MESSAGE,
    LAST_SIGNAL
};

static gpointer parent_class;
static guint signals[LAST_SIGNAL];

static const gchar *ui =
"<ui>"
"  <popup name='context'>"
"    <menuitem action='http-open'/>"
"    <menuitem action='send-message'/>"
"    <menuitem action='uri-copy'/>"
"    <menuitem action='add-to-address-book'/>"
"    <menuitem action='mailto-copy'/>"
"    <menu action='search-folder-menu'>"
"      <menuitem action='search-folder-sender'/>"
"      <menuitem action='search-folder-recipient'/>"
"    </menu>"
"  </popup>"
"</ui>";

static void
action_add_to_address_book_cb (GtkAction *action,
                               EMailDisplay *display)
{
    CamelURL *curl;
    const gchar *uri;
    gpointer parent;

    parent = gtk_widget_get_toplevel (GTK_WIDGET (display));
    parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;

    uri = e_mail_display_get_selected_uri (display);
    g_return_if_fail (uri != NULL);

    /* This should work because we checked it in update_actions(). */
    curl = camel_url_new (uri, NULL);
    g_return_if_fail (curl != NULL);

    if (curl->path != NULL && *curl->path != '\0')
        em_utils_add_address (parent, curl->path);

    camel_url_free (curl);
}

static void
action_http_open_cb (GtkAction *action,
                     EMailDisplay *display)
{
    const gchar *uri;
    gpointer parent;

    parent = gtk_widget_get_toplevel (GTK_WIDGET (display));
    parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;

    uri = e_mail_display_get_selected_uri (display);
    g_return_if_fail (uri != NULL);

    e_show_uri (parent, uri);
}

static void
action_mailto_copy_cb (GtkAction *action,
                       EMailDisplay *display)
{
    CamelURL *curl;
    CamelInternetAddress *inet_addr;
    GtkClipboard *clipboard;
    const gchar *uri;
    gchar *text;

    clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
    uri = e_mail_display_get_selected_uri (display);
    g_return_if_fail (uri != NULL);

    /* This should work because we checked it in update_actions(). */
    curl = camel_url_new (uri, NULL);
    g_return_if_fail (curl != NULL);

    inet_addr = camel_internet_address_new ();
    camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
    text = camel_address_encode (CAMEL_ADDRESS (inet_addr));
    if (text == NULL || *text == '\0')
        text = g_strdup (uri + strlen ("mailto:"));

    camel_object_unref (inet_addr);
    camel_url_free (curl);

    gtk_clipboard_set_text (clipboard, text, -1);
    gtk_clipboard_store (clipboard);

    g_free (text);
}

static void
action_send_message_cb (GtkAction *action,
                        EMailDisplay *display)
{
    const gchar *uri;

    uri = e_mail_display_get_selected_uri (display);
    g_return_if_fail (uri != NULL);

    em_utils_compose_new_message_with_mailto (uri, NULL);
}

static void
action_uri_copy_cb (GtkAction *action,
                    EMailDisplay *display)
{
    GtkClipboard *clipboard;
    const gchar *uri;

    clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
    uri = e_mail_display_get_selected_uri (display);
    g_return_if_fail (uri != NULL);

    gtk_clipboard_set_text (clipboard, uri, -1);
    gtk_clipboard_store (clipboard);
}

static GtkActionEntry uri_entries[] = {

    { "uri-copy",
      GTK_STOCK_COPY,
      N_("_Copy Link Location"),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      G_CALLBACK (action_uri_copy_cb) },
};

static GtkActionEntry http_entries[] = {

    { "http-open",
      "emblem-web",
      N_("_Open Link in Browser"),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      G_CALLBACK (action_http_open_cb) },
};

static GtkActionEntry mailto_entries[] = {

    { "add-to-address-book",
      "contact-new",
      N_("_Add to Address Book"),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      G_CALLBACK (action_add_to_address_book_cb) },

    { "mailto-copy",
      GTK_STOCK_COPY,
      N_("_Copy Email Address"),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      G_CALLBACK (action_mailto_copy_cb) },

    { "search-folder-recipient",
      NULL,
      N_("_To This Address"),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      NULL   /* Handled by EMailReader */ },

    { "search-folder-sender",
      NULL,
      N_("_From This Address"),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      NULL   /* Handled by EMailReader */ },

    { "send-message",
      "mail-message-new",
      N_("_Send New Message To..."),
      NULL,
      NULL,  /* XXX Add a tooltip! */
      G_CALLBACK (action_send_message_cb) },

    /*** Menus ***/

    { "search-folder-menu",
      "folder-saved-search",
      N_("Create Search _Folder"),
      NULL,
      NULL,
      NULL }
};

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,
                           GtkHTML *html,
                           gchar **uri,
                           EMFormatPURI **puri)
{
    EMFormat *formatter;
    gchar *text_uri;
    gchar *image_uri;
    gboolean is_cid;

    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);
            image_uri = temp;
        }
    }

    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 gboolean
mail_display_button_press_event_cb (EMailDisplay *display,
                                    GdkEventButton *event,
                                    GtkHTML *html)
{
    EMFormatPURI *puri = NULL;
    gboolean finished = TRUE;
    gchar *uri = NULL;

    /* The GtkHTML object may be the EMailDisplay itself
     * or an inner iframe. */

    if (event->button != 3)
        return FALSE;

    mail_display_get_uri_puri (display, event, html, &uri, &puri);

    if (uri == NULL || g_str_has_prefix (uri, "##")) {
        g_free (uri);
        return FALSE;
    }

    finished = mail_display_emit_popup_event (display, event, uri, puri);

    g_free (uri);

    return finished;
}

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;

        case PROP_SELECTED_URI:
            e_mail_display_set_selected_uri (
                E_MAIL_DISPLAY (object),
                g_value_get_string (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;

        case PROP_SELECTED_URI:
            g_value_set_string (
                value, e_mail_display_get_selected_uri (
                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;
    }

    if (priv->ui_manager) {
        g_object_unref (priv->ui_manager);
        priv->ui_manager = 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)
{
    EMailDisplay *display = E_MAIL_DISPLAY (widget);
    GtkHTML *html = GTK_HTML (widget);

    if (mail_display_button_press_event_cb (display, event, html))
        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_cb), html);
}

static gboolean
mail_display_popup_event (EMailDisplay *display,
                          GdkEventButton *event,
                          const gchar *uri,
                          EMFormatPURI *puri)
{
    e_mail_display_set_selected_uri (display, uri);
    e_mail_display_show_popup_menu (display, event, NULL, NULL);

    return TRUE;
}

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;

    class->popup_event = mail_display_popup_event;

    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));

    g_object_class_install_property (
        object_class,
        PROP_SELECTED_URI,
        g_param_spec_string (
            "selected-uri",
            "Selected URI",
            NULL,
            NULL,
            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)
{
    GtkUIManager *ui_manager;
    GtkActionGroup *action_group;
    const gchar *id;
    GError *error = NULL;

    display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);

    ui_manager = gtk_ui_manager_new ();
    display->priv->ui_manager = ui_manager;

    action_group = e_mail_display_add_action_group (display, "uri");

    gtk_action_group_add_actions (
        action_group, uri_entries,
        G_N_ELEMENTS (uri_entries), display);

    action_group = e_mail_display_add_action_group (display, "http");

    gtk_action_group_add_actions (
        action_group, http_entries,
        G_N_ELEMENTS (http_entries), display);

    action_group = e_mail_display_add_action_group (display, "mailto");

    gtk_action_group_add_actions (
        action_group, mailto_entries,
        G_N_ELEMENTS (mailto_entries), display);

    /* Because we are loading from a hard-coded string, there is
     * no chance of I/O errors.  Failure here implies a malformed
     * UI definition.  Full stop. */
    gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
    if (error != NULL)
        g_error ("%s", error->message);

    id = "org.gnome.evolution.mail.display";
    e_plugin_ui_register_manager (ui_manager, id, display);
    e_plugin_ui_enable_manager (ui_manager, id);
}

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");
}

const gchar *
e_mail_display_get_selected_uri (EMailDisplay *display)
{
    g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);

    return display->priv->selected_uri;
}

void
e_mail_display_set_selected_uri (EMailDisplay *display,
                                 const gchar *selected_uri)
{
    g_return_if_fail (E_IS_MAIL_DISPLAY (display));

    g_free (display->priv->selected_uri);
    display->priv->selected_uri = g_strdup (selected_uri);

    g_object_notify (G_OBJECT (display), "selected-uri");
}

GtkAction *
e_mail_display_get_action (EMailDisplay *display,
                           const gchar *action_name)
{
    GtkUIManager *ui_manager;

    g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
    g_return_val_if_fail (action_name != NULL, NULL);

    ui_manager = e_mail_display_get_ui_manager (display);

    return e_lookup_action (ui_manager, action_name);
}

GtkActionGroup *
e_mail_display_add_action_group (EMailDisplay *display,
                                 const gchar *group_name)
{
    GtkActionGroup *action_group;
    GtkUIManager *ui_manager;
    const gchar *domain;

    g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
    g_return_val_if_fail (group_name != NULL, NULL);

    ui_manager = e_mail_display_get_ui_manager (display);
    domain = GETTEXT_PACKAGE;

    action_group = gtk_action_group_new (group_name);
    gtk_action_group_set_translation_domain (action_group, domain);
    gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
    g_object_unref (action_group);

    return action_group;
}

GtkActionGroup *
e_mail_display_get_action_group (EMailDisplay *display,
                                 const gchar *group_name)
{
    GtkUIManager *ui_manager;

    g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
    g_return_val_if_fail (group_name != NULL, NULL);

    ui_manager = e_mail_display_get_ui_manager (display);

    return e_lookup_action_group (ui_manager, group_name);
}

GtkWidget *
e_mail_display_get_popup_menu (EMailDisplay *display)
{
    GtkUIManager *ui_manager;
    GtkWidget *menu;

    g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);

    ui_manager = e_mail_display_get_ui_manager (display);
    menu = gtk_ui_manager_get_widget (ui_manager, "/context");
    g_return_val_if_fail (GTK_IS_MENU (menu), NULL);

    return menu;
}

GtkUIManager *
e_mail_display_get_ui_manager (EMailDisplay *display)
{
    EMailDisplayPrivate *priv;

    g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);

    priv = E_MAIL_DISPLAY_GET_PRIVATE (display);

    return priv->ui_manager;
}

void
e_mail_display_show_popup_menu (EMailDisplay *display,
                                GdkEventButton *event,
                                GtkMenuPositionFunc func,
                                gpointer user_data)
{
    GtkWidget *menu;

    g_return_if_fail (E_IS_MAIL_DISPLAY (display));

    e_mail_display_update_actions (display);

    menu = e_mail_display_get_popup_menu (display);

    if (event != NULL)
        gtk_menu_popup (
            GTK_MENU (menu), NULL, NULL, func,
            user_data, event->button, event->time);
    else
        gtk_menu_popup (
            GTK_MENU (menu), NULL, NULL, func,
            user_data, 0, gtk_get_current_event_time ());
}

void
e_mail_display_update_actions (EMailDisplay *display)
{
    CamelURL *curl;
    GtkActionGroup *action_group;
    gboolean scheme_is_http;
    gboolean scheme_is_mailto;
    gboolean uri_is_valid;
    gboolean visible;
    const gchar *uri;

    g_return_if_fail (E_IS_MAIL_DISPLAY (display));

    uri = e_mail_display_get_selected_uri (display);
    g_return_if_fail (uri != NULL);

    /* Parse the URI early so we know if the actions will work. */
    curl = camel_url_new (uri, NULL);
    uri_is_valid = (curl != NULL);
    camel_url_free (curl);

    scheme_is_http =
        (g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
        (g_ascii_strncasecmp (uri, "https:", 6) == 0);

    scheme_is_mailto =
        (g_ascii_strncasecmp (uri, "mailto:", 7) == 0);

    /* Allow copying the URI even if it's malformed. */
    visible = !scheme_is_mailto;
    action_group = e_mail_display_get_action_group (display, "uri");
    gtk_action_group_set_visible (action_group, visible);

    visible = uri_is_valid && scheme_is_http;
    action_group = e_mail_display_get_action_group (display, "http");
    gtk_action_group_set_visible (action_group, visible);

    visible = uri_is_valid && scheme_is_mailto;
    action_group = e_mail_display_get_action_group (display, "mailto");
    gtk_action_group_set_visible (action_group, visible);
}