
* mail-format.c (mail_lookup_handler): Use gnome_vfs_mime_get_short_list_applications rather than gnome_vfs_mime_get_default_application. * mail-display.c (pixmap_press): Construct the EPopupMenu array on the fly, based on the number of applications available to open the MIME type. (launch_cb): Figure out which menu item was clicked, and invoke the appropriate application. Ugh, messy, because of the EPopupMenu interface. Probably should get rewritten some day. Also, make this handle apps with expects_uris set too. svn path=/trunk/; revision=10916
1899 lines
49 KiB
C
1899 lines
49 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* mail-display.c: Mail display widget
|
|
*
|
|
* Author:
|
|
* Miguel de Icaza
|
|
* Bertrand Guiheneuf (bg@aful.org)
|
|
*
|
|
* (C) 2000 Ximian, Inc.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <libgnorba/gnorba.h>
|
|
#include <libgnomevfs/gnome-vfs-mime-info.h>
|
|
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
|
|
#include <libgnomevfs/gnome-vfs.h>
|
|
#include <bonobo/bonobo-control-frame.h>
|
|
#include <bonobo/bonobo-stream-memory.h>
|
|
#include <bonobo/bonobo-ui-toolbar-icon.h>
|
|
#include <bonobo/bonobo-widget.h>
|
|
#include <bonobo/bonobo-socket.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf-loader.h>
|
|
#include <gal/util/e-util.h>
|
|
#include <gal/widgets/e-popup-menu.h>
|
|
#include <gtk/gtkinvisible.h>
|
|
#include <gtkhtml/gtkhtml-embedded.h>
|
|
#include <gtkhtml/htmlengine.h> /* XXX */
|
|
#include <gtkhtml/htmlobject.h> /* XXX */
|
|
#include <gtkhtml/htmltext.h> /* XXX */
|
|
#include <gtkhtml/htmlinterval.h> /* XXX */
|
|
|
|
#include "e-util/e-html-utils.h"
|
|
#include "addressbook/backend/ebook/e-book-util.h"
|
|
|
|
#include "e-searching-tokenizer.h"
|
|
#include "folder-browser-factory.h"
|
|
#include "mail-display.h"
|
|
#include "mail-config.h"
|
|
#include "mail-ops.h"
|
|
#include "mail-mt.h"
|
|
#include "mail.h"
|
|
|
|
#include "art/empty.xpm"
|
|
|
|
#define PARENT_TYPE (gtk_vbox_get_type ())
|
|
|
|
static GtkObjectClass *mail_display_parent_class;
|
|
|
|
struct _PixbufLoader {
|
|
CamelDataWrapper *wrapper; /* The data */
|
|
CamelStream *mstream;
|
|
GdkPixbufLoader *loader;
|
|
GtkHTMLEmbedded *eb;
|
|
char *type; /* Type of data, in case the conversion fails */
|
|
char *cid; /* Strdupped on creation, but not freed until
|
|
the hashtable is destroyed */
|
|
GtkWidget *pixmap;
|
|
guint32 destroy_id;
|
|
};
|
|
static GHashTable *thumbnail_cache = NULL;
|
|
|
|
static gchar *save_pathname = NULL; /* preserves last directory in save dialog */
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Callbacks
|
|
*----------------------------------------------------------------------*/
|
|
|
|
static void
|
|
write_data_written(CamelMimePart *part, char *name, int done, void *data)
|
|
{
|
|
int *ret = data;
|
|
|
|
/* should we popup a dialogue to say its done too? */
|
|
*ret = done;
|
|
}
|
|
|
|
static gboolean
|
|
write_data_to_file (CamelMimePart *part, const char *name, gboolean unique)
|
|
{
|
|
int fd;
|
|
int ret = FALSE;
|
|
|
|
g_return_val_if_fail (CAMEL_IS_MIME_PART (part), FALSE);
|
|
|
|
fd = open (name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
if (fd == -1 && errno == EEXIST && !unique) {
|
|
GtkWidget *dlg;
|
|
GtkWidget *text;
|
|
|
|
dlg = gnome_dialog_new (_("Overwrite file?"),
|
|
GNOME_STOCK_BUTTON_YES,
|
|
GNOME_STOCK_BUTTON_NO,
|
|
NULL);
|
|
text = gtk_label_new (_("A file by that name already exists.\nOverwrite it?"));
|
|
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dlg)->vbox), text, TRUE, TRUE, 4);
|
|
gtk_window_set_policy(GTK_WINDOW(dlg), FALSE, TRUE, FALSE);
|
|
gtk_widget_show (text);
|
|
|
|
if (gnome_dialog_run_and_close (GNOME_DIALOG (dlg)) != 0)
|
|
return FALSE;
|
|
}
|
|
if (fd != -1)
|
|
close(fd);
|
|
|
|
/* should this have progress of what its doing? */
|
|
mail_msg_wait(mail_save_part(part, name, write_data_written, &ret));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
make_safe_filename (const char *prefix,CamelMimePart *part)
|
|
{
|
|
const char *name = NULL;
|
|
char *safe, *p;
|
|
|
|
if (part) {
|
|
name = camel_mime_part_get_filename (part);
|
|
}
|
|
|
|
if (!name) {
|
|
/* This is a filename. Translators take note. */
|
|
name = _("attachment");
|
|
}
|
|
|
|
p = strrchr (name, '/');
|
|
if (p)
|
|
safe = g_strdup_printf ("%s%s", prefix, p);
|
|
else
|
|
safe = g_strdup_printf ("%s/%s", prefix, name);
|
|
|
|
p = strrchr (safe, '/') + 1;
|
|
if (p)
|
|
e_filename_make_safe (p);
|
|
|
|
return safe;
|
|
}
|
|
|
|
static void
|
|
save_data_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
GtkFileSelection *file_select = (GtkFileSelection *)
|
|
gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_SELECTION);
|
|
gchar *p;
|
|
|
|
/* uh, this doesn't really feel right, but i dont know what to do better */
|
|
gtk_widget_hide (GTK_WIDGET (file_select));
|
|
write_data_to_file (user_data,
|
|
gtk_file_selection_get_filename (file_select),
|
|
FALSE);
|
|
|
|
/* preserve the pathname */
|
|
g_free(save_pathname);
|
|
save_pathname = g_strdup(gtk_file_selection_get_filename(file_select));
|
|
if((p = strrchr(save_pathname, '/')) != NULL)
|
|
p[0] = 0;
|
|
else {
|
|
g_free(save_pathname);
|
|
save_pathname = NULL;
|
|
}
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (file_select));
|
|
}
|
|
|
|
static gboolean
|
|
idle_redisplay (gpointer data)
|
|
{
|
|
MailDisplay *md = data;
|
|
|
|
md->idle_id = 0;
|
|
mail_display_redisplay (md, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
mail_display_queue_redisplay (MailDisplay *md)
|
|
{
|
|
if (!md->idle_id) {
|
|
md->idle_id = g_idle_add_full (G_PRIORITY_LOW, idle_redisplay,
|
|
md, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_link_clicked (GtkHTML *html, const char *url, MailDisplay *md)
|
|
{
|
|
if (!g_strncasecmp (url, "news:", 5) ||
|
|
!g_strncasecmp (url, "nntp:", 5))
|
|
g_warning ("Can't handle news URLs yet.");
|
|
else if (!g_strncasecmp (url, "mailto:", 7))
|
|
send_to_url (url);
|
|
else if (!strcmp (url, "x-evolution-decode-pgp:")) {
|
|
g_datalist_set_data (md->data, "show_pgp",
|
|
GINT_TO_POINTER (1));
|
|
mail_display_queue_redisplay (md);
|
|
} else
|
|
gnome_url_show (url);
|
|
}
|
|
|
|
static void
|
|
save_part (CamelMimePart *part)
|
|
{
|
|
GtkFileSelection *file_select;
|
|
char *filename;
|
|
|
|
if(save_pathname == NULL)
|
|
save_pathname = g_get_home_dir();
|
|
|
|
filename = make_safe_filename (save_pathname, part);
|
|
|
|
file_select = GTK_FILE_SELECTION (
|
|
gtk_file_selection_new (_("Save Attachment")));
|
|
gtk_file_selection_set_filename (file_select, filename);
|
|
g_free (filename);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (file_select->ok_button), "clicked",
|
|
GTK_SIGNAL_FUNC (save_data_cb), part);
|
|
gtk_signal_connect_object (GTK_OBJECT (file_select->cancel_button),
|
|
"clicked",
|
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|
GTK_OBJECT (file_select));
|
|
|
|
gtk_widget_show (GTK_WIDGET (file_select));
|
|
}
|
|
|
|
static void
|
|
save_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
CamelMimePart *part = gtk_object_get_data (GTK_OBJECT (user_data), "CamelMimePart");
|
|
|
|
save_part (part);
|
|
}
|
|
|
|
static void
|
|
launch_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
|
|
MailMimeHandler *handler;
|
|
GList *apps, *children, *c;
|
|
GnomeVFSMimeApplication *app;
|
|
char *tmpl, *tmpdir, *filename, *url, *argv[2];
|
|
|
|
handler = mail_lookup_handler (gtk_object_get_data (user_data, "mime_type"));
|
|
g_return_if_fail (handler != NULL && handler->applications != NULL);
|
|
|
|
/* Yum. Too bad EPopupMenu doesn't allow per-item closures. */
|
|
children = gtk_container_children (GTK_CONTAINER (widget->parent));
|
|
g_return_if_fail (children != NULL && children->next != NULL && children->next->next != NULL);
|
|
|
|
for (c = children->next->next, apps = handler->applications; c && apps; c = c->next, apps = apps->next) {
|
|
if (c->data == widget)
|
|
break;
|
|
}
|
|
g_list_free (children);
|
|
g_return_if_fail (c != NULL && apps != NULL);
|
|
app = apps->data;
|
|
|
|
tmpl = g_strdup ("/tmp/evolution.XXXXXX");
|
|
#ifdef HAVE_MKDTEMP
|
|
tmpdir = mkdtemp (tmpl);
|
|
#else
|
|
tmpdir = mktemp (tmpl);
|
|
if (tmpdir) {
|
|
if (mkdir (tmpdir, S_IRWXU) == -1)
|
|
tmpdir = NULL;
|
|
}
|
|
#endif
|
|
if (!tmpdir) {
|
|
char *msg = g_strdup_printf (_("Could not create temporary "
|
|
"directory: %s"),
|
|
g_strerror (errno));
|
|
gnome_error_dialog (msg);
|
|
g_free (tmpl);
|
|
g_free (msg);
|
|
return;
|
|
}
|
|
|
|
filename = make_safe_filename (tmpdir, part);
|
|
|
|
if (!write_data_to_file (part, filename, TRUE)) {
|
|
g_free (tmpl);
|
|
g_free (filename);
|
|
return;
|
|
}
|
|
|
|
if (app->expects_uris == GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS) {
|
|
url = g_strdup_printf ("file:%s", filename);
|
|
g_free (filename);
|
|
filename = url;
|
|
}
|
|
|
|
argv[0] = app->command;
|
|
argv[1] = filename;
|
|
|
|
gnome_execute_async (tmpdir, 2, argv);
|
|
g_free (tmpdir);
|
|
g_free (filename);
|
|
}
|
|
|
|
static void
|
|
inline_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
MailDisplay *md = gtk_object_get_data (user_data, "MailDisplay");
|
|
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
|
|
|
|
if (mail_part_is_inline (part))
|
|
camel_mime_part_set_disposition (part, "attachment");
|
|
else
|
|
camel_mime_part_set_disposition (part, "inline");
|
|
|
|
mail_display_queue_redisplay (md);
|
|
}
|
|
|
|
static void
|
|
button_press (GtkWidget *widget, CamelMimePart *part)
|
|
{
|
|
MailDisplay *md;
|
|
|
|
md = gtk_object_get_data (GTK_OBJECT (widget), "MailDisplay");
|
|
if (md == NULL) {
|
|
g_warning ("No MailDisplay on button!");
|
|
return;
|
|
}
|
|
|
|
if (mail_part_is_inline (part))
|
|
camel_mime_part_set_disposition (part, "attachment");
|
|
else
|
|
camel_mime_part_set_disposition (part, "inline");
|
|
|
|
mail_display_queue_redisplay (md);
|
|
}
|
|
|
|
static gboolean
|
|
pixmap_press (GtkWidget *widget, GdkEventButton *event, EScrollFrame *user_data)
|
|
{
|
|
EPopupMenu *menu;
|
|
EPopupMenu save_item = { N_("Save to Disk..."), NULL,
|
|
GTK_SIGNAL_FUNC (save_cb), NULL, 0 };
|
|
EPopupMenu view_item = { N_("View Inline"), NULL,
|
|
GTK_SIGNAL_FUNC (inline_cb), NULL, 2 };
|
|
EPopupMenu open_item = { N_("Open in %s..."), NULL,
|
|
GTK_SIGNAL_FUNC (launch_cb), NULL, 1 };
|
|
CamelMimePart *part;
|
|
MailMimeHandler *handler;
|
|
int mask = 0, i, nitems;
|
|
|
|
#ifdef USE_OLD_DISPLAY_STYLE
|
|
if (event->button != 3) {
|
|
gtk_propagate_event (GTK_WIDGET (user_data),
|
|
(GdkEvent *)event);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
if (event->button != 1 && event->button != 3) {
|
|
gtk_propagate_event (GTK_WIDGET (user_data),
|
|
(GdkEvent *)event);
|
|
return TRUE;
|
|
}
|
|
|
|
part = gtk_object_get_data (GTK_OBJECT (widget), "CamelMimePart");
|
|
handler = mail_lookup_handler (gtk_object_get_data (GTK_OBJECT (widget),
|
|
"mime_type"));
|
|
|
|
if (handler && handler->applications)
|
|
nitems = g_list_length (handler->applications) + 2;
|
|
else
|
|
nitems = 3;
|
|
menu = g_new0 (EPopupMenu, nitems + 1);
|
|
|
|
/* Save item */
|
|
memcpy (&menu[0], &save_item, sizeof (menu[0]));
|
|
menu[0].name = _(menu[0].name);
|
|
|
|
/* Inline view item */
|
|
memcpy (&menu[1], &view_item, sizeof (menu[1]));
|
|
if (handler && handler->builtin) {
|
|
if (!mail_part_is_inline (part)) {
|
|
if (handler->component) {
|
|
OAF_Property *prop;
|
|
char *name;
|
|
|
|
prop = oaf_server_info_prop_find (
|
|
handler->component, "name");
|
|
if (!prop) {
|
|
prop = oaf_server_info_prop_find (
|
|
handler->component,
|
|
"description");
|
|
}
|
|
if (prop && prop->v._d == OAF_P_STRING)
|
|
name = prop->v._u.value_string;
|
|
else
|
|
name = "bonobo";
|
|
menu[1].name = g_strdup_printf (
|
|
_("View Inline (via %s)"), name);
|
|
} else
|
|
menu[1].name = g_strdup (_(menu[1].name));
|
|
} else
|
|
menu[1].name = g_strdup (_("Hide"));
|
|
} else {
|
|
menu[1].name = g_strdup (_(menu[1].name));
|
|
mask |= 2;
|
|
}
|
|
|
|
/* External views */
|
|
if (handler && handler->applications) {
|
|
GnomeVFSMimeApplication *app;
|
|
GList *apps;
|
|
int i;
|
|
|
|
apps = handler->applications;
|
|
for (i = 2; i < nitems; i++, apps = apps->next) {
|
|
app = apps->data;
|
|
memcpy (&menu[i], &open_item, sizeof (menu[i]));
|
|
menu[i].name = g_strdup_printf (_(menu[i].name), app->name);
|
|
}
|
|
} else {
|
|
memcpy (&menu[2], &open_item, sizeof (menu[2]));
|
|
menu[2].name = g_strdup_printf (_(menu[2].name),
|
|
_("External Viewer"));
|
|
mask |= 1;
|
|
}
|
|
|
|
e_popup_menu_run (menu, (GdkEvent *)event, mask, 0, widget);
|
|
|
|
for (i = 1; i < nitems; i++)
|
|
g_free (menu[i].name);
|
|
g_free (menu);
|
|
return TRUE;
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
pixbuf_for_mime_type (const char *mime_type)
|
|
{
|
|
const char *icon_name;
|
|
char *filename = NULL;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
|
|
icon_name = gnome_vfs_mime_get_value (mime_type, "icon-filename");
|
|
if (icon_name) {
|
|
if (*icon_name == '/') {
|
|
pixbuf = gdk_pixbuf_new_from_file (icon_name);
|
|
if (pixbuf)
|
|
return pixbuf;
|
|
}
|
|
|
|
filename = gnome_pixmap_file (icon_name);
|
|
if (!filename) {
|
|
char *fm_icon;
|
|
|
|
fm_icon = g_strdup_printf ("nautilus/%s", icon_name);
|
|
filename = gnome_pixmap_file (fm_icon);
|
|
if (!filename) {
|
|
fm_icon = g_strdup_printf ("mc/%s", icon_name);
|
|
filename = gnome_pixmap_file (fm_icon);
|
|
}
|
|
g_free (fm_icon);
|
|
}
|
|
}
|
|
|
|
if (filename) {
|
|
pixbuf = gdk_pixbuf_new_from_file (filename);
|
|
g_free (filename);
|
|
}
|
|
|
|
if (!pixbuf) {
|
|
filename = gnome_pixmap_file ("gnome-unknown.png");
|
|
if (filename) {
|
|
pixbuf = gdk_pixbuf_new_from_file (filename);
|
|
g_free (filename);
|
|
} else {
|
|
g_warning ("Could not get any icon for %s!",mime_type);
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data (
|
|
(const char **)empty_xpm);
|
|
}
|
|
}
|
|
|
|
return pixbuf;
|
|
}
|
|
|
|
static gboolean
|
|
pixbuf_uncache (gpointer key)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
|
|
pixbuf = g_hash_table_lookup (thumbnail_cache, key);
|
|
gdk_pixbuf_unref (pixbuf);
|
|
g_hash_table_remove (thumbnail_cache, key);
|
|
g_free (key);
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
pixbuf_gen_idle (struct _PixbufLoader *pbl)
|
|
{
|
|
GdkPixbuf *pixbuf, *mini;
|
|
gboolean error = FALSE;
|
|
char tmp[4096];
|
|
int len, width, height, ratio;
|
|
gpointer orig_key;
|
|
|
|
/* Get the pixbuf from the cache */
|
|
if (g_hash_table_lookup_extended (thumbnail_cache, pbl->cid,
|
|
&orig_key, (gpointer *)&mini)) {
|
|
width = gdk_pixbuf_get_width (mini);
|
|
height = gdk_pixbuf_get_height (mini);
|
|
|
|
bonobo_ui_toolbar_icon_set_pixbuf (
|
|
BONOBO_UI_TOOLBAR_ICON (pbl->pixmap), mini);
|
|
gtk_widget_set_usize (pbl->pixmap, width, height);
|
|
|
|
/* Restart the cache-cleaning timer */
|
|
g_source_remove_by_user_data (orig_key);
|
|
g_timeout_add (5 * 60 * 1000, pixbuf_uncache, orig_key);
|
|
|
|
if (pbl->loader) {
|
|
gdk_pixbuf_loader_close (pbl->loader);
|
|
gtk_object_destroy (GTK_OBJECT (pbl->loader));
|
|
camel_object_unref (CAMEL_OBJECT (pbl->mstream));
|
|
}
|
|
gtk_signal_disconnect (GTK_OBJECT (pbl->eb), pbl->destroy_id);
|
|
g_free (pbl->type);
|
|
g_free (pbl->cid);
|
|
g_free (pbl);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Not in cache, so get a pixbuf from the wrapper */
|
|
|
|
if (!GTK_IS_WIDGET (pbl->pixmap)) {
|
|
/* Widget has died */
|
|
if (pbl->mstream)
|
|
camel_object_unref (CAMEL_OBJECT (pbl->mstream));
|
|
|
|
if (pbl->loader) {
|
|
gdk_pixbuf_loader_close (pbl->loader);
|
|
gtk_object_destroy (GTK_OBJECT (pbl->loader));
|
|
}
|
|
|
|
g_free (pbl->type);
|
|
g_free (pbl->cid);
|
|
g_free (pbl);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pbl->mstream) {
|
|
if (pbl->loader == NULL)
|
|
pbl->loader = gdk_pixbuf_loader_new ();
|
|
|
|
len = camel_stream_read (pbl->mstream, tmp, 4096);
|
|
if (len > 0) {
|
|
error = !gdk_pixbuf_loader_write (pbl->loader, tmp, len);
|
|
if (!error)
|
|
return TRUE;
|
|
} else if (!camel_stream_eos (pbl->mstream))
|
|
error = TRUE;
|
|
}
|
|
|
|
if (error || !pbl->mstream)
|
|
pixbuf = pixbuf_for_mime_type (pbl->type);
|
|
else
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (pbl->loader);
|
|
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
if (width >= height) {
|
|
if (width > 24) {
|
|
ratio = width / 24;
|
|
width = 24;
|
|
height /= ratio;
|
|
}
|
|
} else {
|
|
if (height > 24) {
|
|
ratio = height / 24;
|
|
height = 24;
|
|
width /= ratio;
|
|
}
|
|
}
|
|
|
|
mini = gdk_pixbuf_scale_simple (pixbuf, width, height,
|
|
GDK_INTERP_BILINEAR);
|
|
if (error || !pbl->mstream)
|
|
gdk_pixbuf_unref (pixbuf);
|
|
bonobo_ui_toolbar_icon_set_pixbuf (
|
|
BONOBO_UI_TOOLBAR_ICON (pbl->pixmap), mini);
|
|
gtk_widget_set_usize (pbl->pixmap, 24, 24);
|
|
|
|
/* Add the pixbuf to the cache */
|
|
g_hash_table_insert (thumbnail_cache, pbl->cid, mini);
|
|
g_timeout_add (5 * 60 * 1000, pixbuf_uncache, pbl->cid);
|
|
|
|
gtk_signal_disconnect (GTK_OBJECT (pbl->eb), pbl->destroy_id);
|
|
if (pbl->loader) {
|
|
gdk_pixbuf_loader_close (pbl->loader);
|
|
gtk_object_destroy (GTK_OBJECT (pbl->loader));
|
|
camel_object_unref (CAMEL_OBJECT (pbl->mstream));
|
|
}
|
|
g_free (pbl->type);
|
|
g_free (pbl);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Stop the idle function and free the pbl structure
|
|
as the widget that the pixbuf was to be rendered to
|
|
has died on us. */
|
|
static void
|
|
embeddable_destroy_cb (GtkObject *embeddable,
|
|
struct _PixbufLoader *pbl)
|
|
{
|
|
g_idle_remove_by_data (pbl);
|
|
if (pbl->mstream)
|
|
camel_object_unref (CAMEL_OBJECT (pbl->mstream));
|
|
|
|
if (pbl->loader) {
|
|
gdk_pixbuf_loader_close (pbl->loader);
|
|
gtk_object_destroy (GTK_OBJECT (pbl->loader));
|
|
}
|
|
|
|
g_free (pbl->type);
|
|
g_free (pbl->cid);
|
|
g_free (pbl);
|
|
};
|
|
|
|
static GtkWidget *
|
|
get_embedded_for_component (const char *iid, MailDisplay *md)
|
|
{
|
|
GtkWidget *embedded;
|
|
BonoboControlFrame *control_frame;
|
|
Bonobo_PropertyBag prop_bag;
|
|
|
|
/*
|
|
* First try a control.
|
|
*/
|
|
embedded = bonobo_widget_new_control (iid, NULL);
|
|
if (embedded == NULL) {
|
|
/*
|
|
* No control, try an embeddable instead.
|
|
*/
|
|
embedded = bonobo_widget_new_subdoc (iid, NULL);
|
|
if (embedded != NULL) {
|
|
/* FIXME: as of bonobo 0.18, there's an extra
|
|
* client_site dereference in the BonoboWidget
|
|
* destruction path that we have to balance out to
|
|
* prevent problems.
|
|
*/
|
|
bonobo_object_ref (BONOBO_OBJECT(bonobo_widget_get_client_site (
|
|
BONOBO_WIDGET (embedded))));
|
|
|
|
return embedded;
|
|
}
|
|
}
|
|
|
|
if (embedded == NULL)
|
|
return NULL;
|
|
|
|
control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (embedded));
|
|
|
|
prop_bag = bonobo_control_frame_get_control_property_bag ( control_frame, NULL );
|
|
|
|
if (prop_bag != CORBA_OBJECT_NIL){
|
|
CORBA_Environment ev;
|
|
/*
|
|
* Now we can take care of business. Currently, the only control
|
|
* that needs something passed to it through a property bag is
|
|
* the iTip control, and it needs only the From email address,
|
|
* but perhaps in the future we can generalize this section of code
|
|
* to pass a bunch of useful things to all embedded controls.
|
|
*/
|
|
const CamelInternetAddress *from;
|
|
const MailConfigIdentity *id;
|
|
|
|
id = mail_config_get_default_identity ();
|
|
CORBA_exception_init (&ev);
|
|
if (id){
|
|
char *from_address;
|
|
|
|
|
|
from = camel_mime_message_get_from (md->current_message);
|
|
from_address = camel_address_encode((CamelAddress *)from);
|
|
bonobo_property_bag_client_set_value_string (
|
|
prop_bag, "from_address",
|
|
from_address, &ev);
|
|
bonobo_property_bag_client_set_value_string (
|
|
prop_bag, "my_address",
|
|
id ? id->address : "", &ev);
|
|
g_free(from_address);
|
|
}
|
|
Bonobo_Unknown_unref (prop_bag, &ev);
|
|
CORBA_exception_free (&ev);
|
|
}
|
|
|
|
return embedded;
|
|
}
|
|
|
|
static void *
|
|
save_url (MailDisplay *md, const char *url)
|
|
{
|
|
GHashTable *urls;
|
|
CamelMimePart *part;
|
|
|
|
urls = g_datalist_get_data (md->data, "part_urls");
|
|
g_return_val_if_fail (urls != NULL, NULL);
|
|
|
|
part = g_hash_table_lookup (urls, url);
|
|
if (part) {
|
|
CamelDataWrapper *data;
|
|
|
|
g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
|
|
|
|
data = camel_medium_get_content_object ((CamelMedium *)part);
|
|
if (!mail_content_loaded (data, md)) {
|
|
return NULL;
|
|
}
|
|
|
|
save_part (part);
|
|
return NULL;
|
|
}
|
|
|
|
g_warning ("part not found");
|
|
#if 0
|
|
urls = g_datalist_get_data (md->data, "data_urls");
|
|
g_return_val_if_fail (urls != NULL, NULL);
|
|
|
|
/* See if it's some piece of cached data */
|
|
ba = g_hash_table_lookup (urls, url);
|
|
if (ba) {
|
|
return ba;
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
|
|
{
|
|
MailDisplay *md = data;
|
|
GHashTable *urls;
|
|
CamelMedium *medium;
|
|
CamelDataWrapper *wrapper;
|
|
OAF_ServerInfo *component;
|
|
GtkWidget *embedded;
|
|
BonoboObjectClient *server;
|
|
Bonobo_PersistStream persist;
|
|
CORBA_Environment ev;
|
|
GByteArray *ba;
|
|
CamelStream *cstream;
|
|
BonoboStream *bstream;
|
|
char *cid;
|
|
|
|
cid = eb->classid;
|
|
|
|
if (!strncmp (cid, "popup:", 6))
|
|
cid += 6;
|
|
if (strncmp (cid, "cid:", 4) != 0)
|
|
return FALSE;
|
|
|
|
urls = g_datalist_get_data (md->data, "part_urls");
|
|
g_return_val_if_fail (urls != NULL, FALSE);
|
|
|
|
medium = g_hash_table_lookup (urls, cid);
|
|
g_return_val_if_fail (CAMEL_IS_MEDIUM (medium), FALSE);
|
|
|
|
if (cid != eb->classid) {
|
|
/* This is a part wrapper */
|
|
#ifdef USE_OLD_DISPLAY_STYLE
|
|
GtkWidget *ebox;
|
|
#else
|
|
GtkWidget *button, *mainbox, *hbox, *arrow, *popup;
|
|
MailMimeHandler *handler;
|
|
#endif
|
|
struct _PixbufLoader *pbl;
|
|
|
|
pbl = g_new0 (struct _PixbufLoader, 1);
|
|
if (g_strncasecmp (eb->type, "image/", 6) == 0) {
|
|
CamelDataWrapper *content;
|
|
|
|
content = camel_medium_get_content_object (medium);
|
|
if (!camel_data_wrapper_is_offline (content)) {
|
|
pbl->mstream = camel_stream_mem_new ();
|
|
camel_data_wrapper_write_to_stream (content, pbl->mstream);
|
|
camel_stream_reset (pbl->mstream);
|
|
}
|
|
}
|
|
pbl->type = g_strdup (eb->type);
|
|
pbl->cid = g_strdup (cid);
|
|
pbl->pixmap = bonobo_ui_toolbar_icon_new ();
|
|
pbl->eb = eb;
|
|
pbl->destroy_id = gtk_signal_connect (GTK_OBJECT (eb),
|
|
"destroy",
|
|
embeddable_destroy_cb,
|
|
pbl);
|
|
|
|
g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc)pixbuf_gen_idle,
|
|
pbl, NULL);
|
|
|
|
#ifdef USE_OLD_DISPLAY_STYLE
|
|
ebox = gtk_event_box_new ();
|
|
gtk_widget_set_sensitive (GTK_WIDGET (ebox), TRUE);
|
|
gtk_widget_add_events (GTK_WIDGET (ebox),
|
|
GDK_BUTTON_PRESS_MASK);
|
|
gtk_object_set_data (GTK_OBJECT (ebox), "MailDisplay", md);
|
|
gtk_object_set_data (GTK_OBJECT (ebox), "CamelMimePart",
|
|
medium);
|
|
gtk_object_set_data_full (GTK_OBJECT (ebox), "mime_type",
|
|
g_strdup (eb->type),
|
|
(GDestroyNotify)g_free);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ebox), "button_press_event",
|
|
GTK_SIGNAL_FUNC (pixmap_press), md->scroll);
|
|
|
|
gtk_container_add (GTK_CONTAINER (ebox), pbl->pixmap);
|
|
gtk_widget_show_all (ebox);
|
|
gtk_container_add (GTK_CONTAINER (eb), ebox);
|
|
#else
|
|
mainbox = gtk_hbox_new (FALSE, 0);
|
|
|
|
button = gtk_button_new ();
|
|
gtk_object_set_data (GTK_OBJECT (button), "MailDisplay", md);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
GTK_SIGNAL_FUNC (button_press), medium);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 2);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
|
|
|
|
if (mail_part_is_inline (CAMEL_MIME_PART (medium))) {
|
|
arrow = gnome_stock_new_with_icon (GNOME_STOCK_PIXMAP_DOWN);
|
|
} else {
|
|
arrow = gnome_stock_new_with_icon (GNOME_STOCK_PIXMAP_FORWARD);
|
|
}
|
|
gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), pbl->pixmap, TRUE, TRUE, 0);
|
|
gtk_container_add (GTK_CONTAINER (button), hbox);
|
|
|
|
popup = gtk_button_new ();
|
|
gtk_container_add (GTK_CONTAINER (popup),
|
|
gtk_arrow_new (GTK_ARROW_DOWN,
|
|
GTK_SHADOW_ETCHED_IN));
|
|
|
|
gtk_object_set_data (GTK_OBJECT (popup), "MailDisplay", md);
|
|
gtk_object_set_data (GTK_OBJECT (popup), "CamelMimePart",
|
|
medium);
|
|
gtk_object_set_data_full (GTK_OBJECT (popup), "mime_type",
|
|
g_strdup (eb->type),
|
|
(GDestroyNotify)g_free);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (popup), "button_press_event",
|
|
GTK_SIGNAL_FUNC (pixmap_press), md->scroll);
|
|
|
|
gtk_box_pack_start (GTK_BOX (mainbox), button, TRUE, TRUE, 0);
|
|
gtk_box_pack_start (GTK_BOX (mainbox), popup, TRUE, TRUE, 0);
|
|
gtk_widget_show_all (mainbox);
|
|
|
|
handler = mail_lookup_handler (eb->type);
|
|
if (handler && handler->builtin) {
|
|
gtk_widget_set_sensitive (button, TRUE);
|
|
} else {
|
|
gtk_widget_set_sensitive (button, FALSE);
|
|
}
|
|
|
|
gtk_container_add (GTK_CONTAINER (eb), mainbox);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
component = gnome_vfs_mime_get_default_component (eb->type);
|
|
if (!component)
|
|
return FALSE;
|
|
|
|
embedded = get_embedded_for_component (component->iid, md);
|
|
CORBA_free (component);
|
|
if (!embedded)
|
|
return FALSE;
|
|
|
|
server = bonobo_widget_get_server (BONOBO_WIDGET (embedded));
|
|
persist = (Bonobo_PersistStream) bonobo_object_client_query_interface (
|
|
server, "IDL:Bonobo/PersistStream:1.0", NULL);
|
|
if (persist == CORBA_OBJECT_NIL) {
|
|
gtk_object_sink (GTK_OBJECT (embedded));
|
|
return FALSE;
|
|
}
|
|
|
|
/* Write the data to a CamelStreamMem... */
|
|
ba = g_byte_array_new ();
|
|
cstream = camel_stream_mem_new_with_byte_array (ba);
|
|
wrapper = camel_medium_get_content_object (medium);
|
|
camel_data_wrapper_write_to_stream (wrapper, cstream);
|
|
|
|
/* ...convert the CamelStreamMem to a BonoboStreamMem... */
|
|
bstream = bonobo_stream_mem_create (ba->data, ba->len, TRUE, FALSE);
|
|
camel_object_unref (CAMEL_OBJECT (cstream));
|
|
|
|
/* ...and hydrate the PersistStream from the BonoboStream. */
|
|
CORBA_exception_init (&ev);
|
|
Bonobo_PersistStream_load (persist,
|
|
bonobo_object_corba_objref (
|
|
BONOBO_OBJECT (bstream)),
|
|
eb->type, &ev);
|
|
bonobo_object_unref (BONOBO_OBJECT (bstream));
|
|
Bonobo_Unknown_unref (persist, &ev);
|
|
CORBA_Object_release (persist, &ev);
|
|
|
|
if (ev._major != CORBA_NO_EXCEPTION) {
|
|
gtk_object_sink (GTK_OBJECT (embedded));
|
|
CORBA_exception_free (&ev);
|
|
return FALSE;
|
|
}
|
|
CORBA_exception_free (&ev);
|
|
|
|
gtk_widget_show (embedded);
|
|
gtk_container_add (GTK_CONTAINER (eb), embedded);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
load_http (MailDisplay *md, gpointer data)
|
|
{
|
|
char *url = data;
|
|
GHashTable *urls;
|
|
GnomeVFSHandle *handle;
|
|
GnomeVFSFileSize read;
|
|
GByteArray *ba;
|
|
char buf[8192];
|
|
|
|
urls = g_datalist_get_data (md->data, "data_urls");
|
|
ba = g_hash_table_lookup (urls, url);
|
|
g_return_if_fail (ba != NULL);
|
|
|
|
if (gnome_vfs_open (&handle, url, GNOME_VFS_OPEN_READ) != GNOME_VFS_OK) {
|
|
#if 0
|
|
printf ("failed to open %s\n", url);
|
|
#endif
|
|
g_free (url);
|
|
return;
|
|
}
|
|
|
|
while (gnome_vfs_read (handle, buf, sizeof (buf), &read) == GNOME_VFS_OK)
|
|
g_byte_array_append (ba, buf, read);
|
|
gnome_vfs_close (handle);
|
|
|
|
#if 0
|
|
if (!ba->len)
|
|
printf ("no data in %s\n", url);
|
|
#endif
|
|
|
|
g_free (url);
|
|
}
|
|
|
|
static void
|
|
ebook_callback (EBook *book, const gchar *addr, ECard *card, gpointer data)
|
|
{
|
|
MailDisplay *md = data;
|
|
|
|
if (card && md->current_message) {
|
|
const CamelInternetAddress *from = camel_mime_message_get_from (md->current_message);
|
|
const char *md_name, *md_addr;
|
|
|
|
if (camel_internet_address_get (from, 0, &md_name, &md_addr) &&
|
|
!strcmp (addr, md_addr))
|
|
mail_display_load_images (md);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle,
|
|
gpointer user_data)
|
|
{
|
|
MailDisplay *md = user_data;
|
|
GHashTable *urls;
|
|
CamelMedium *medium;
|
|
GByteArray *ba;
|
|
|
|
urls = g_datalist_get_data (md->data, "part_urls");
|
|
g_return_if_fail (urls != NULL);
|
|
|
|
/* See if it refers to a MIME part (cid: or http:) */
|
|
medium = g_hash_table_lookup (urls, url);
|
|
if (medium) {
|
|
CamelDataWrapper *data;
|
|
CamelStream *stream_mem;
|
|
|
|
g_return_if_fail (CAMEL_IS_MEDIUM (medium));
|
|
|
|
data = camel_medium_get_content_object (medium);
|
|
if (!mail_content_loaded (data, md)) {
|
|
gtk_html_end (html, handle, GTK_HTML_STREAM_ERROR);
|
|
return;
|
|
}
|
|
|
|
ba = g_byte_array_new ();
|
|
stream_mem = camel_stream_mem_new_with_byte_array (ba);
|
|
camel_data_wrapper_write_to_stream (data, stream_mem);
|
|
gtk_html_write (html, handle, ba->data, ba->len);
|
|
camel_object_unref (CAMEL_OBJECT (stream_mem));
|
|
|
|
gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
|
|
return;
|
|
}
|
|
|
|
urls = g_datalist_get_data (md->data, "data_urls");
|
|
g_return_if_fail (urls != NULL);
|
|
|
|
/* See if it's some piece of cached data */
|
|
ba = g_hash_table_lookup (urls, url);
|
|
if (ba) {
|
|
if (ba->len)
|
|
gtk_html_write (html, handle, ba->data, ba->len);
|
|
gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
|
|
return;
|
|
}
|
|
|
|
/* See if it's something we can load. */
|
|
if (strncmp (url, "http:", 5) == 0) {
|
|
if (mail_config_get_http_mode () == MAIL_CONFIG_HTTP_ALWAYS ||
|
|
g_datalist_get_data (md->data, "load_images")) {
|
|
ba = g_byte_array_new ();
|
|
g_hash_table_insert (urls, g_strdup (url), ba);
|
|
mail_display_redisplay_when_loaded (md, ba, load_http,
|
|
g_strdup (url));
|
|
} else if (mail_config_get_http_mode () == MAIL_CONFIG_HTTP_SOMETIMES &&
|
|
!g_datalist_get_data (md->data, "checking_from")) {
|
|
const CamelInternetAddress *from = camel_mime_message_get_from (md->current_message);
|
|
const char *name, *addr;
|
|
|
|
g_datalist_set_data (md->data, "checking_from",
|
|
GINT_TO_POINTER (1));
|
|
if (camel_internet_address_get (from, 0, &name, &addr))
|
|
e_book_query_address_locally (addr, ebook_callback, md);
|
|
}
|
|
}
|
|
|
|
gtk_html_end (html, handle, GTK_HTML_STREAM_ERROR);
|
|
}
|
|
|
|
struct _load_content_msg {
|
|
struct _mail_msg msg;
|
|
|
|
MailDisplay *display;
|
|
CamelMimeMessage *message;
|
|
void (*callback)(MailDisplay *, gpointer);
|
|
gpointer data;
|
|
};
|
|
|
|
static char *
|
|
load_content_desc (struct _mail_msg *mm, int done)
|
|
{
|
|
return g_strdup (_("Loading message content"));
|
|
}
|
|
|
|
static void
|
|
load_content_load (struct _mail_msg *mm)
|
|
{
|
|
struct _load_content_msg *m = (struct _load_content_msg *)mm;
|
|
|
|
m->callback (m->display, m->data);
|
|
}
|
|
|
|
static void
|
|
load_content_loaded (struct _mail_msg *mm)
|
|
{
|
|
struct _load_content_msg *m = (struct _load_content_msg *)mm;
|
|
|
|
if (m->display->current_message == m->message)
|
|
mail_display_queue_redisplay (m->display);
|
|
}
|
|
|
|
static void
|
|
load_content_free (struct _mail_msg *mm)
|
|
{
|
|
struct _load_content_msg *m = (struct _load_content_msg *)mm;
|
|
|
|
gtk_object_unref (GTK_OBJECT (m->display));
|
|
camel_object_unref (CAMEL_OBJECT (m->message));
|
|
}
|
|
|
|
static struct _mail_msg_op load_content_op = {
|
|
load_content_desc,
|
|
load_content_load,
|
|
load_content_loaded,
|
|
load_content_free,
|
|
};
|
|
|
|
void
|
|
mail_display_redisplay_when_loaded (MailDisplay *md,
|
|
gconstpointer key,
|
|
void (*callback)(MailDisplay *, gpointer),
|
|
gpointer data)
|
|
{
|
|
struct _load_content_msg *m;
|
|
GHashTable *loading;
|
|
|
|
loading = g_datalist_get_data (md->data, "loading");
|
|
if (loading) {
|
|
if (g_hash_table_lookup (loading, key))
|
|
return;
|
|
} else {
|
|
loading = g_hash_table_new (NULL, NULL);
|
|
g_datalist_set_data_full (md->data, "loading", loading,
|
|
(GDestroyNotify)g_hash_table_destroy);
|
|
}
|
|
g_hash_table_insert (loading, (gpointer)key, GINT_TO_POINTER (1));
|
|
|
|
m = mail_msg_new (&load_content_op, NULL, sizeof (*m));
|
|
m->display = md;
|
|
gtk_object_ref (GTK_OBJECT (m->display));
|
|
m->message = md->current_message;
|
|
camel_object_ref (CAMEL_OBJECT (m->message));
|
|
m->callback = callback;
|
|
m->data = data;
|
|
|
|
e_thread_put (mail_thread_queued, (EMsg *)m);
|
|
return;
|
|
}
|
|
|
|
void
|
|
mail_html_write (GtkHTML *html, GtkHTMLStream *stream,
|
|
const char *format, ...)
|
|
{
|
|
char *buf;
|
|
va_list ap;
|
|
|
|
va_start (ap, format);
|
|
buf = g_strdup_vprintf (format, ap);
|
|
va_end (ap);
|
|
gtk_html_write (html, stream, buf, strlen (buf));
|
|
g_free (buf);
|
|
}
|
|
|
|
void
|
|
mail_text_write (GtkHTML *html, GtkHTMLStream *stream,
|
|
const char *format, ...)
|
|
{
|
|
char *buf, *htmltext;
|
|
va_list ap;
|
|
|
|
va_start (ap, format);
|
|
buf = g_strdup_vprintf (format, ap);
|
|
va_end (ap);
|
|
|
|
htmltext = e_text_to_html_full (buf,
|
|
E_TEXT_TO_HTML_CONVERT_URLS |
|
|
E_TEXT_TO_HTML_CONVERT_ADDRESSES |
|
|
E_TEXT_TO_HTML_CONVERT_NL |
|
|
E_TEXT_TO_HTML_CONVERT_SPACES |
|
|
(mail_config_get_citation_highlight () ? E_TEXT_TO_HTML_MARK_CITATION : 0),
|
|
mail_config_get_citation_color ());
|
|
gtk_html_write (html, stream, "<tt>", 4);
|
|
gtk_html_write (html, stream, htmltext, strlen (htmltext));
|
|
gtk_html_write (html, stream, "</tt>", 5);
|
|
g_free (htmltext);
|
|
g_free (buf);
|
|
}
|
|
|
|
void
|
|
mail_error_write (GtkHTML *html, GtkHTMLStream *stream,
|
|
const char *format, ...)
|
|
{
|
|
char *buf, *htmltext;
|
|
va_list ap;
|
|
|
|
va_start (ap, format);
|
|
buf = g_strdup_vprintf (format, ap);
|
|
va_end (ap);
|
|
|
|
htmltext = e_text_to_html (buf, E_TEXT_TO_HTML_CONVERT_NL);
|
|
gtk_html_write (html, stream, "<em><font color=red>", 20);
|
|
gtk_html_write (html, stream, htmltext, strlen (htmltext));
|
|
gtk_html_write (html, stream, "</font></em><br>", 16);
|
|
g_free (htmltext);
|
|
g_free (buf);
|
|
}
|
|
|
|
static void
|
|
clear_data (CamelObject *object, gpointer event_data, gpointer user_data)
|
|
{
|
|
GData *data = user_data;
|
|
|
|
g_datalist_clear (&data);
|
|
}
|
|
|
|
/**
|
|
* mail_display_redisplay:
|
|
* @mail_display: the mail display object
|
|
* @unscroll: specifies whether or not to lose current scroll
|
|
*
|
|
* Force a redraw of the message display.
|
|
**/
|
|
void
|
|
mail_display_redisplay (MailDisplay *md, gboolean unscroll)
|
|
{
|
|
md->last_active = NULL;
|
|
md->stream = gtk_html_begin (GTK_HTML (md->html));
|
|
if (!unscroll) {
|
|
/* This is a hack until there's a clean way to do this. */
|
|
GTK_HTML (md->html)->engine->newPage = FALSE;
|
|
}
|
|
|
|
mail_html_write (md->html, md->stream, "<!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");
|
|
mail_html_write (md->html, md->stream, "<body marginwidth=0 marginheight=0>\n");
|
|
|
|
if (md->current_message) {
|
|
MailConfigDisplayStyle style = mail_config_get_message_display_style ();
|
|
if (style == MAIL_CONFIG_DISPLAY_SOURCE)
|
|
mail_format_raw_message (md->current_message, md);
|
|
else {
|
|
g_datalist_set_data (md->data, "full_headers", GINT_TO_POINTER (style == MAIL_CONFIG_DISPLAY_FULL_HEADERS));
|
|
mail_format_mime_message (md->current_message, md);
|
|
}
|
|
}
|
|
|
|
mail_html_write (md->html, md->stream, "</body></html>\n");
|
|
gtk_html_end (md->html, md->stream, GTK_HTML_STREAM_OK);
|
|
md->stream = NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* mail_display_set_message:
|
|
* @mail_display: the mail display object
|
|
* @medium: the input camel medium, or %NULL
|
|
*
|
|
* Makes the mail_display object show the contents of the medium
|
|
* param.
|
|
**/
|
|
void
|
|
mail_display_set_message (MailDisplay *md, CamelMedium *medium)
|
|
{
|
|
/* For the moment, we deal only with CamelMimeMessage, but in
|
|
* the future, we should be able to deal with any medium.
|
|
*/
|
|
if (medium && !CAMEL_IS_MIME_MESSAGE (medium))
|
|
return;
|
|
|
|
/* Clean up from previous message. */
|
|
if (md->current_message)
|
|
camel_object_unref (CAMEL_OBJECT (md->current_message));
|
|
|
|
md->current_message = (CamelMimeMessage*)medium;
|
|
|
|
g_datalist_init (md->data);
|
|
mail_display_redisplay (md, TRUE);
|
|
if (medium) {
|
|
camel_object_hook_event (CAMEL_OBJECT (medium), "finalize",
|
|
clear_data, *(md->data));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* mail_display_load_images:
|
|
* @md: the mail display object
|
|
*
|
|
* Load all HTTP images in the current message
|
|
**/
|
|
void
|
|
mail_display_load_images (MailDisplay *md)
|
|
{
|
|
g_datalist_set_data (md->data, "load_images", GINT_TO_POINTER (1));
|
|
mail_display_redisplay (md, FALSE);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Standard Gtk+ Class functions
|
|
*----------------------------------------------------------------------*/
|
|
|
|
static void
|
|
mail_display_init (GtkObject *object)
|
|
{
|
|
MailDisplay *mail_display = MAIL_DISPLAY (object);
|
|
|
|
mail_display->current_message = NULL;
|
|
mail_display->scroll = NULL;
|
|
mail_display->html = NULL;
|
|
mail_display->stream = NULL;
|
|
mail_display->last_active = NULL;
|
|
mail_display->idle_id = 0;
|
|
mail_display->selection = NULL;
|
|
mail_display->current_message = NULL;
|
|
mail_display->data = NULL;
|
|
|
|
mail_display->invisible = gtk_invisible_new ();
|
|
}
|
|
|
|
static void
|
|
mail_display_destroy (GtkObject *object)
|
|
{
|
|
MailDisplay *mail_display = MAIL_DISPLAY (object);
|
|
|
|
g_free (mail_display->selection);
|
|
|
|
g_datalist_clear (mail_display->data);
|
|
g_free (mail_display->data);
|
|
|
|
gtk_widget_destroy (mail_display->invisible);
|
|
|
|
mail_display_parent_class->destroy (object);
|
|
}
|
|
|
|
static void
|
|
invisible_selection_get_callback (GtkWidget *widget,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint time,
|
|
void *data)
|
|
{
|
|
MailDisplay *display;
|
|
|
|
display = MAIL_DISPLAY (data);
|
|
|
|
g_assert (info == 1);
|
|
|
|
gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8,
|
|
display->selection, strlen (display->selection));
|
|
}
|
|
|
|
static gint
|
|
invisible_selection_clear_event_callback (GtkWidget *widget,
|
|
GdkEventSelection *event,
|
|
void *data)
|
|
{
|
|
MailDisplay *display;
|
|
|
|
display = MAIL_DISPLAY (data);
|
|
|
|
g_free (display->selection);
|
|
display->selection = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
mail_display_class_init (GtkObjectClass *object_class)
|
|
{
|
|
object_class->destroy = mail_display_destroy;
|
|
mail_display_parent_class = gtk_type_class (PARENT_TYPE);
|
|
|
|
thumbnail_cache = g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
static void
|
|
link_open_in_browser (GtkWidget *w, MailDisplay *mail_display)
|
|
{
|
|
on_link_clicked (mail_display->html, mail_display->html->pointer_url,
|
|
mail_display);
|
|
}
|
|
|
|
static void
|
|
link_save_as (GtkWidget *w, MailDisplay *mail_display)
|
|
{
|
|
g_print ("FIXME save %s\n", mail_display->html->pointer_url);
|
|
}
|
|
|
|
static void
|
|
link_copy_location (GtkWidget *w, MailDisplay *mail_display)
|
|
{
|
|
g_free (mail_display->selection);
|
|
mail_display->selection = g_strdup (mail_display->html->pointer_url);
|
|
|
|
if (! gtk_selection_owner_set (GTK_WIDGET (mail_display->invisible), GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME))
|
|
g_warning ("Damn");
|
|
}
|
|
|
|
static void
|
|
image_save_as (GtkWidget *w, MailDisplay *mail_display)
|
|
{
|
|
const char *src;
|
|
|
|
src = gtk_object_get_data (GTK_OBJECT (mail_display), "current_src_uri");
|
|
|
|
g_warning ("loading uri=%s", src);
|
|
|
|
save_url (mail_display, src);
|
|
}
|
|
|
|
enum {
|
|
/*
|
|
* This is used to mask the link specific menu items.
|
|
*/
|
|
MASK_URL = 1,
|
|
|
|
/*
|
|
* This is used to mask src specific menu items.
|
|
*/
|
|
MASK_SRC = 2
|
|
};
|
|
|
|
#define SEPARATOR { "", NULL, (NULL), NULL, 0 }
|
|
#define TERMINATOR { NULL, NULL, (NULL), NULL, 0 }
|
|
|
|
static EPopupMenu link_menu [] = {
|
|
{ N_("Open Link in Browser"), NULL,
|
|
GTK_SIGNAL_FUNC (link_open_in_browser), NULL, MASK_URL },
|
|
{ N_("Copy Link Location"), NULL,
|
|
GTK_SIGNAL_FUNC (link_copy_location), NULL, MASK_URL },
|
|
{ N_("Save Link as (FIXME)"), NULL,
|
|
GTK_SIGNAL_FUNC (link_save_as), NULL, MASK_URL },
|
|
{ N_("Save Image as"), NULL,
|
|
GTK_SIGNAL_FUNC (image_save_as), NULL, MASK_SRC },
|
|
|
|
TERMINATOR
|
|
};
|
|
|
|
|
|
/*
|
|
* Create a window and popup our widget, with reasonable semantics for the popup
|
|
* disappearing, etc.
|
|
*/
|
|
|
|
typedef struct _PopupInfo PopupInfo;
|
|
struct _PopupInfo {
|
|
GtkWidget *w;
|
|
GtkWidget *win;
|
|
guint destroy_timeout;
|
|
guint widget_destroy_handle;
|
|
};
|
|
|
|
/* Aiieee! Global Data! */
|
|
static GtkWidget *the_popup = NULL;
|
|
|
|
static void
|
|
popup_info_free (PopupInfo *pop)
|
|
{
|
|
if (pop) {
|
|
if (pop->destroy_timeout)
|
|
gtk_timeout_remove (pop->destroy_timeout);
|
|
|
|
g_free (pop);
|
|
}
|
|
}
|
|
|
|
static void
|
|
popup_widget_destroy_cb (GtkWidget *w, gpointer user_data)
|
|
{
|
|
PopupInfo *pop = (PopupInfo *) user_data;
|
|
|
|
gtk_widget_destroy (pop->win);
|
|
}
|
|
|
|
static void
|
|
popup_window_destroy_cb (GtkWidget *w, gpointer user_data)
|
|
{
|
|
PopupInfo *pop = (PopupInfo *) user_data;
|
|
|
|
if (pop->widget_destroy_handle) {
|
|
gtk_signal_disconnect (GTK_OBJECT (pop->w), pop->widget_destroy_handle);
|
|
pop->widget_destroy_handle = 0;
|
|
}
|
|
|
|
the_popup = NULL;
|
|
|
|
popup_info_free (pop);
|
|
}
|
|
|
|
static gint
|
|
popup_timeout_cb (gpointer user_data)
|
|
{
|
|
PopupInfo *pop = (PopupInfo *) user_data;
|
|
|
|
pop->destroy_timeout = 0;
|
|
gtk_widget_destroy (pop->win);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gint
|
|
popup_enter_cb (GtkWidget *w, GdkEventCrossing *ev, gpointer user_data)
|
|
{
|
|
PopupInfo *pop = (PopupInfo *) user_data;
|
|
|
|
if (pop->destroy_timeout)
|
|
gtk_timeout_remove (pop->destroy_timeout);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gint
|
|
popup_leave_cb (GtkWidget *w, GdkEventCrossing *ev, gpointer user_data)
|
|
{
|
|
PopupInfo *pop = (PopupInfo *) user_data;
|
|
|
|
if (pop->destroy_timeout)
|
|
gtk_timeout_remove (pop->destroy_timeout);
|
|
pop->destroy_timeout = gtk_timeout_add (500, popup_timeout_cb, pop);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
popup_realize_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
PopupInfo *pop = (PopupInfo *) user_data;
|
|
|
|
gtk_widget_add_events (pop->win, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
|
|
gtk_widget_add_events (pop->w, GDK_BUTTON_PRESS_MASK);
|
|
|
|
if (pop->destroy_timeout == 0)
|
|
pop->destroy_timeout = gtk_timeout_add (5000, popup_timeout_cb, pop);
|
|
}
|
|
|
|
static void
|
|
popup_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
|
|
{
|
|
gint x, y, w, h, xmax, ymax;
|
|
|
|
xmax = gdk_screen_width ();
|
|
ymax = gdk_screen_height ();
|
|
|
|
gdk_window_get_pointer (NULL, &x, &y, NULL);
|
|
w = alloc->width;
|
|
h = alloc->height;
|
|
x = CLAMP (x - w/2, 0, xmax - w);
|
|
y = CLAMP (y - h/2, 0, ymax - h);
|
|
gtk_widget_set_uposition (widget, x, y);
|
|
|
|
}
|
|
|
|
static GtkWidget *
|
|
make_popup_window (GtkWidget *w)
|
|
{
|
|
PopupInfo *pop = g_new0 (PopupInfo, 1);
|
|
GtkWidget *fr;
|
|
|
|
/* Only allow for one popup at a time. Ugly. */
|
|
if (the_popup)
|
|
gtk_widget_destroy (the_popup);
|
|
|
|
pop->w = w;
|
|
the_popup = pop->win = gtk_window_new (GTK_WINDOW_POPUP);
|
|
fr = gtk_frame_new (NULL);
|
|
|
|
gtk_container_add (GTK_CONTAINER (pop->win), fr);
|
|
gtk_container_add (GTK_CONTAINER (fr), w);
|
|
|
|
gtk_window_set_policy (GTK_WINDOW (pop->win), FALSE, FALSE, FALSE);
|
|
|
|
|
|
pop->widget_destroy_handle = gtk_signal_connect (GTK_OBJECT (w),
|
|
"destroy",
|
|
GTK_SIGNAL_FUNC (popup_widget_destroy_cb),
|
|
pop);
|
|
gtk_signal_connect (GTK_OBJECT (pop->win),
|
|
"destroy",
|
|
GTK_SIGNAL_FUNC (popup_window_destroy_cb),
|
|
pop);
|
|
gtk_signal_connect (GTK_OBJECT (pop->win),
|
|
"enter_notify_event",
|
|
GTK_SIGNAL_FUNC (popup_enter_cb),
|
|
pop);
|
|
gtk_signal_connect (GTK_OBJECT (pop->win),
|
|
"leave_notify_event",
|
|
GTK_SIGNAL_FUNC (popup_leave_cb),
|
|
pop);
|
|
gtk_signal_connect_after (GTK_OBJECT (pop->win),
|
|
"realize",
|
|
GTK_SIGNAL_FUNC (popup_realize_cb),
|
|
pop);
|
|
gtk_signal_connect (GTK_OBJECT (pop->win),
|
|
"size_allocate",
|
|
GTK_SIGNAL_FUNC (popup_size_allocate_cb),
|
|
pop);
|
|
|
|
gtk_widget_show (w);
|
|
gtk_widget_show (fr);
|
|
gtk_widget_show (pop->win);
|
|
|
|
return pop->win;
|
|
}
|
|
|
|
/* Copied from e-shell-view.c */
|
|
static GtkWidget *
|
|
find_socket (GtkContainer *container)
|
|
{
|
|
GList *children, *tmp;
|
|
|
|
children = gtk_container_children (container);
|
|
while (children) {
|
|
if (BONOBO_IS_SOCKET (children->data))
|
|
return children->data;
|
|
else if (GTK_IS_CONTAINER (children->data)) {
|
|
GtkWidget *socket = find_socket (children->data);
|
|
if (socket)
|
|
return socket;
|
|
}
|
|
tmp = children->next;
|
|
g_list_free_1 (children);
|
|
children = tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
html_button_press_event (GtkWidget *widget, GdkEventButton *event, MailDisplay *mail_display)
|
|
{
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
if (event->type == GDK_BUTTON_PRESS) {
|
|
if (event->button == 3) {
|
|
HTMLEngine *e;
|
|
HTMLPoint *point;
|
|
GtkWidget *socket;
|
|
GtkWidget *popup_thing;
|
|
GtkWidget *win;
|
|
|
|
e = GTK_HTML (widget)->engine;
|
|
point = html_engine_get_point_at (e, event->x + e->x_offset, event->y + e->y_offset, FALSE);
|
|
|
|
if (point) {
|
|
const gchar *url;
|
|
const gchar *src;
|
|
|
|
url = html_object_get_url (point->object);
|
|
src = html_object_get_src (point->object);
|
|
|
|
if (url && !g_strncasecmp (url, "mailto:", 7)) {
|
|
|
|
popup_thing = bonobo_widget_new_control ("OAFIID:GNOME_Evolution_Addressbook_AddressPopup",
|
|
CORBA_OBJECT_NIL);
|
|
|
|
socket = find_socket (GTK_CONTAINER (popup_thing));
|
|
|
|
bonobo_widget_set_property (BONOBO_WIDGET (popup_thing),
|
|
"email", url+7,
|
|
NULL);
|
|
|
|
win = make_popup_window (popup_thing);
|
|
gtk_signal_connect_object (GTK_OBJECT (socket),
|
|
"destroy",
|
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|
GTK_OBJECT (win));
|
|
|
|
|
|
|
|
|
|
} else if (url || src) {
|
|
gint hide_mask = 0;
|
|
|
|
if (!url)
|
|
hide_mask |= MASK_URL;
|
|
|
|
if (!src)
|
|
hide_mask |= MASK_SRC;
|
|
|
|
g_free (gtk_object_get_data (GTK_OBJECT (mail_display), "current_src_uri"));
|
|
gtk_object_set_data (GTK_OBJECT (mail_display), "current_src_uri", g_strdup (src));
|
|
|
|
e_popup_menu_run (link_menu, (GdkEvent *) event, 0, hide_mask, mail_display);
|
|
|
|
}
|
|
|
|
html_point_destroy (point);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static inline void
|
|
set_underline (HTMLEngine *e, HTMLObject *o, gboolean underline)
|
|
{
|
|
HTMLText *text = HTML_TEXT (o);
|
|
|
|
html_text_set_font_style (text, e, underline
|
|
? html_text_get_font_style (text) | GTK_HTML_FONT_STYLE_UNDERLINE
|
|
: html_text_get_font_style (text) & ~GTK_HTML_FONT_STYLE_UNDERLINE);
|
|
html_engine_queue_draw (e, o);
|
|
}
|
|
|
|
static void
|
|
update_active (GtkWidget *widget, gint x, gint y, MailDisplay *mail_display)
|
|
{
|
|
HTMLEngine *e;
|
|
HTMLPoint *point;
|
|
const gchar *email;
|
|
|
|
e = GTK_HTML (widget)->engine;
|
|
|
|
point = html_engine_get_point_at (e, x + e->x_offset, y + e->y_offset, FALSE);
|
|
if (mail_display->last_active && (!point || mail_display->last_active != point->object)) {
|
|
set_underline (e, HTML_OBJECT (mail_display->last_active), FALSE);
|
|
mail_display->last_active = NULL;
|
|
}
|
|
if (point) {
|
|
email = (const gchar *) html_object_get_data (point->object, "email");
|
|
if (email && html_object_is_text (point->object)) {
|
|
set_underline (e, point->object, TRUE);
|
|
mail_display->last_active = point->object;
|
|
}
|
|
html_point_destroy (point);
|
|
}
|
|
}
|
|
|
|
static gint
|
|
html_enter_notify_event (GtkWidget *widget, GdkEventCrossing *event, MailDisplay *mail_display)
|
|
{
|
|
update_active (widget, event->x, event->y, mail_display);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
html_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, MailDisplay *mail_display)
|
|
{
|
|
gint x, y;
|
|
|
|
g_return_val_if_fail (widget != NULL, 0);
|
|
g_return_val_if_fail (GTK_IS_HTML (widget), 0);
|
|
g_return_val_if_fail (event != NULL, 0);
|
|
|
|
if (event->is_hint)
|
|
gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, &x, &y, NULL);
|
|
else {
|
|
x = event->x;
|
|
y = event->y;
|
|
}
|
|
|
|
update_active (widget, x, y, mail_display);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
html_iframe_created (GtkWidget *w, GtkHTML *iframe, MailDisplay *mail_display)
|
|
{
|
|
gtk_signal_connect (GTK_OBJECT (iframe), "button_press_event",
|
|
GTK_SIGNAL_FUNC (html_button_press_event), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (iframe), "motion_notify_event",
|
|
GTK_SIGNAL_FUNC (html_motion_notify_event), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (iframe), "enter_notify_event",
|
|
GTK_SIGNAL_FUNC (html_enter_notify_event), mail_display);
|
|
}
|
|
|
|
static GNOME_Evolution_ShellView
|
|
retrieve_shell_view_interface_from_control (BonoboControl *control)
|
|
{
|
|
Bonobo_ControlFrame control_frame;
|
|
GNOME_Evolution_ShellView shell_view_interface;
|
|
CORBA_Environment ev;
|
|
|
|
control_frame = bonobo_control_get_control_frame (control);
|
|
|
|
if (control_frame == NULL)
|
|
return CORBA_OBJECT_NIL;
|
|
|
|
CORBA_exception_init (&ev);
|
|
shell_view_interface = Bonobo_Unknown_queryInterface (control_frame,
|
|
"IDL:GNOME/Evolution/ShellView:1.0",
|
|
&ev);
|
|
CORBA_exception_free (&ev);
|
|
|
|
if (shell_view_interface != CORBA_OBJECT_NIL)
|
|
gtk_object_set_data (GTK_OBJECT (control),
|
|
"mail_threads_shell_view_interface",
|
|
shell_view_interface);
|
|
else
|
|
g_warning ("Control frame doesn't have Evolution/ShellView.");
|
|
|
|
return shell_view_interface;
|
|
}
|
|
|
|
static void
|
|
set_status_message (const char *message, int busy)
|
|
{
|
|
EList *controls;
|
|
EIterator *it;
|
|
|
|
controls = folder_browser_factory_get_control_list ();
|
|
for (it = e_list_get_iterator (controls); e_iterator_is_valid (it); e_iterator_next (it)) {
|
|
BonoboControl *control;
|
|
GNOME_Evolution_ShellView shell_view_interface;
|
|
CORBA_Environment ev;
|
|
|
|
control = BONOBO_CONTROL (e_iterator_get (it));
|
|
|
|
shell_view_interface = gtk_object_get_data (GTK_OBJECT (control), "mail_threads_shell_view_interface");
|
|
|
|
if (shell_view_interface == CORBA_OBJECT_NIL)
|
|
shell_view_interface = retrieve_shell_view_interface_from_control (control);
|
|
|
|
CORBA_exception_init (&ev);
|
|
|
|
if (shell_view_interface != CORBA_OBJECT_NIL) {
|
|
|
|
if (message != NULL)
|
|
GNOME_Evolution_ShellView_setMessage (shell_view_interface,
|
|
message[0] ? message: "",
|
|
busy,
|
|
&ev);
|
|
}
|
|
|
|
CORBA_exception_free (&ev);
|
|
|
|
/* yeah we only set the first one. Why? Because it seems to leave
|
|
random ones lying around otherwise. Shrug. */
|
|
break;
|
|
}
|
|
gtk_object_unref (GTK_OBJECT(it));
|
|
}
|
|
|
|
/* For now show every url but possibly limit it to showing only http:
|
|
or ftp: urls */
|
|
static void
|
|
html_on_url (GtkHTML *html,
|
|
const char *url,
|
|
MailDisplay *mail_display)
|
|
{
|
|
static char *previous_url = NULL;
|
|
|
|
/* This all looks silly but yes, this is the proper way to mix
|
|
GtkHTML's on_url with BonoboUIComponent statusbar */
|
|
if (!url || (previous_url && (strcmp (url, previous_url) != 0)))
|
|
set_status_message ("", FALSE);
|
|
if (url) {
|
|
set_status_message (url, FALSE);
|
|
g_free (previous_url);
|
|
previous_url = g_strdup (url);
|
|
}
|
|
}
|
|
|
|
GtkWidget *
|
|
mail_display_new (void)
|
|
{
|
|
MailDisplay *mail_display = gtk_type_new (mail_display_get_type ());
|
|
GtkWidget *scroll, *html;
|
|
|
|
gtk_box_set_homogeneous (GTK_BOX (mail_display), FALSE);
|
|
gtk_widget_show (GTK_WIDGET (mail_display));
|
|
|
|
scroll = e_scroll_frame_new (NULL, NULL);
|
|
e_scroll_frame_set_policy (E_SCROLL_FRAME (scroll),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
|
|
e_scroll_frame_set_shadow_type (E_SCROLL_FRAME (scroll), GTK_SHADOW_IN);
|
|
gtk_box_pack_start_defaults (GTK_BOX (mail_display), GTK_WIDGET (scroll));
|
|
gtk_widget_show (GTK_WIDGET (scroll));
|
|
|
|
html = gtk_html_new ();
|
|
html_engine_set_tokenizer (GTK_HTML (html)->engine, e_searching_tokenizer_new ());
|
|
|
|
gtk_html_set_default_content_type (GTK_HTML (html),
|
|
"text/html; charset=utf-8");
|
|
|
|
gtk_html_set_editable (GTK_HTML (html), FALSE);
|
|
gtk_signal_connect (GTK_OBJECT (html), "url_requested",
|
|
GTK_SIGNAL_FUNC (on_url_requested),
|
|
mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "object_requested",
|
|
GTK_SIGNAL_FUNC (on_object_requested),
|
|
mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "link_clicked",
|
|
GTK_SIGNAL_FUNC (on_link_clicked),
|
|
mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "button_press_event",
|
|
GTK_SIGNAL_FUNC (html_button_press_event), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "motion_notify_event",
|
|
GTK_SIGNAL_FUNC (html_motion_notify_event), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "enter_notify_event",
|
|
GTK_SIGNAL_FUNC (html_enter_notify_event), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "iframe_created",
|
|
GTK_SIGNAL_FUNC (html_iframe_created), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (html), "on_url",
|
|
GTK_SIGNAL_FUNC (html_on_url), mail_display);
|
|
|
|
gtk_container_add (GTK_CONTAINER (scroll), html);
|
|
gtk_widget_show (GTK_WIDGET (html));
|
|
|
|
gtk_signal_connect (GTK_OBJECT (mail_display->invisible), "selection_get",
|
|
GTK_SIGNAL_FUNC (invisible_selection_get_callback), mail_display);
|
|
gtk_signal_connect (GTK_OBJECT (mail_display->invisible), "selection_clear_event",
|
|
GTK_SIGNAL_FUNC (invisible_selection_clear_event_callback), mail_display);
|
|
|
|
gtk_selection_add_target (mail_display->invisible,
|
|
GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 1);
|
|
|
|
mail_display->scroll = E_SCROLL_FRAME (scroll);
|
|
mail_display->html = GTK_HTML (html);
|
|
mail_display->stream = NULL;
|
|
mail_display->last_active = NULL;
|
|
mail_display->data = g_new0 (GData *, 1);
|
|
g_datalist_init (mail_display->data);
|
|
|
|
return GTK_WIDGET (mail_display);
|
|
}
|
|
|
|
E_MAKE_TYPE (mail_display, "MailDisplay", MailDisplay, mail_display_class_init, mail_display_init, PARENT_TYPE);
|