
2001-04-23 Jon Trowbridge <trow@ximian.com> * e-html-utils.c (e_text_to_html_full): Removed attempts to use Radek's evil <DATA> hacks, which were just causing me (and GtkHTML) grief. 2001-04-23 Jon Trowbridge <trow@ximian.com> * gui/component/e-address-popup.c: Lots of code has been simplified here. (e_address_popup_factory_new_control): Rather than directly pop our control up in a window (via the e_address_popup_popup function, which is now gone), just return the widget and let the caller do the popping. This works better, since it means we don't have to work around the vagaries of bonobo focus & event handling. (e_address_popup_set_name): Refresh when both name & email have been set, rather than checking a stupid counter. (e_address_popup_set_email): Ditto. 2001-04-23 Jon Trowbridge <trow@ximian.com> * mail-display.c (html_button_press_event): Check for mailto: links, and pop up our mail address menu when we find one. (make_popup_window): The main piece of code (ignoring a zillion little callbacks) to pop up our windows with reasonable semantics for having them close automatically. (mail_text_write): Enable converting addresses to mailto links in message bodies. * mail-format.c (write_address): Simplify code, removing Radek's <DATA> hacks. Write out addresses as mailto: links. svn path=/trunk/; revision=9534
1466 lines
38 KiB
C
1466 lines
38 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 Helix Code, 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 <bonobo/bonobo-control-frame.h>
|
|
#include <bonobo/bonobo-stream-memory.h>
|
|
#include <bonobo/bonobo-ui-toolbar-icon.h>
|
|
#include <bonobo/bonobo-widget.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 <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 "mail-display.h"
|
|
#include "mail-config.h"
|
|
#include "mail.h"
|
|
#include "art/empty.xpm"
|
|
|
|
#include "mail-ops.h"
|
|
#include "mail-mt.h"
|
|
|
|
#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;
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* 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;
|
|
|
|
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);
|
|
|
|
/* 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);
|
|
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_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
|
|
GtkFileSelection *file_select;
|
|
char *filename;
|
|
|
|
filename = make_safe_filename (g_get_home_dir (), 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
|
|
launch_cb (GtkWidget *widget, gpointer user_data)
|
|
{
|
|
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
|
|
GnomeVFSMimeApplication *app;
|
|
CamelContentType *content_type;
|
|
char *mime_type, *tmpl, *tmpdir, *filename, *argv[2];
|
|
|
|
content_type = camel_mime_part_get_content_type (part);
|
|
mime_type = header_content_type_simple (content_type);
|
|
app = gnome_vfs_mime_get_default_application (mime_type);
|
|
g_free (mime_type);
|
|
|
|
g_return_if_fail (app != NULL);
|
|
|
|
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 (msg);
|
|
return;
|
|
}
|
|
|
|
filename = make_safe_filename (tmpdir, part);
|
|
|
|
if (!write_data_to_file (part, filename, TRUE)) {
|
|
g_free (tmpl);
|
|
g_free (filename);
|
|
return;
|
|
}
|
|
|
|
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[] = {
|
|
{ N_("Save to Disk..."), NULL,
|
|
GTK_SIGNAL_FUNC (save_cb), NULL, 0 },
|
|
{ N_("Open in %s..."), NULL,
|
|
GTK_SIGNAL_FUNC (launch_cb), NULL, 1 },
|
|
{ N_("View Inline"), NULL,
|
|
GTK_SIGNAL_FUNC (inline_cb), NULL, 2 },
|
|
{ NULL, NULL, NULL, 0 }
|
|
};
|
|
CamelMimePart *part;
|
|
MailMimeHandler *handler;
|
|
int mask = 0;
|
|
|
|
#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"));
|
|
|
|
/* Save item */
|
|
menu[0].name = _(menu[0].name);
|
|
|
|
/* External view item */
|
|
if (handler && handler->application) {
|
|
menu[1].name = g_strdup_printf (_(menu[1].name),
|
|
handler->application->name);
|
|
} else {
|
|
menu[1].name = g_strdup_printf (_(menu[1].name),
|
|
_("External Viewer"));
|
|
mask |= 1;
|
|
}
|
|
|
|
/* Inline view item */
|
|
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[2].name = g_strdup_printf (
|
|
_("View Inline (via %s)"), name);
|
|
} else
|
|
menu[2].name = g_strdup (_(menu[2].name));
|
|
} else
|
|
menu[2].name = g_strdup (_("Hide"));
|
|
} else {
|
|
menu[2].name = g_strdup (_(menu[2].name));
|
|
mask |= 2;
|
|
}
|
|
|
|
e_popup_menu_run (menu, (GdkEvent *)event, mask, 0, widget);
|
|
g_free (menu[1].name);
|
|
g_free (menu[2].name);
|
|
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
|
|
handle_embedded_address_object (GtkHTMLEmbedded *eb)
|
|
{
|
|
const gchar *name, *email;
|
|
GtkWidget *w;
|
|
|
|
w = bonobo_widget_new_control ("OAFIID:GNOME_Evolution_Addressbook_AddressWidget",
|
|
CORBA_OBJECT_NIL);
|
|
|
|
name = gtk_html_embedded_get_parameter (eb, "name");
|
|
email = gtk_html_embedded_get_parameter (eb, "email");
|
|
|
|
bonobo_widget_set_property (BONOBO_WIDGET (w),
|
|
"name", name,
|
|
"email", email,
|
|
/* Hackish: this is the bg color defined for the HTML table
|
|
in mail-format.c. If you change it there, you'd better
|
|
change it here as well. */
|
|
"background_rgb", 0xeeeeee,
|
|
NULL);
|
|
|
|
gtk_widget_show (w);
|
|
gtk_container_add (GTK_CONTAINER (eb), w);
|
|
|
|
gtk_html_embedded_set_descent (eb, 0);
|
|
}
|
|
|
|
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 (!strcmp (cid, "address")) {
|
|
handle_embedded_address_object (eb);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!strncmp (cid, "popup:", 6))
|
|
cid += 6;
|
|
if (strncmp (cid, "cid:", 4) != 0)
|
|
return FALSE;
|
|
|
|
urls = g_datalist_get_data (md->data, "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
|
|
on_url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle,
|
|
gpointer user_data)
|
|
{
|
|
MailDisplay *md = user_data;
|
|
GHashTable *urls;
|
|
|
|
urls = g_datalist_get_data (md->data, "urls");
|
|
g_return_if_fail (urls != NULL);
|
|
|
|
user_data = g_hash_table_lookup (urls, url);
|
|
if (user_data == NULL) {
|
|
gtk_html_end (html, handle, GTK_HTML_STREAM_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (strncmp (url, "cid:", 4) == 0) {
|
|
CamelMedium *medium = user_data;
|
|
CamelDataWrapper *data;
|
|
CamelStream *stream_mem;
|
|
GByteArray *ba;
|
|
|
|
g_return_if_fail (CAMEL_IS_MEDIUM (medium));
|
|
data = camel_medium_get_content_object (medium);
|
|
if (!mail_content_loaded (data, md))
|
|
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));
|
|
} else if (strncmp (url, "x-evolution-data:", 17) == 0) {
|
|
GByteArray *ba = user_data;
|
|
|
|
g_return_if_fail (ba != NULL);
|
|
gtk_html_write (html, handle, ba->data, ba->len);
|
|
}
|
|
|
|
gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
|
|
}
|
|
|
|
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)
|
|
{
|
|
printf("redisplaying\n");
|
|
|
|
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, "%s%s", HTML_HEADER, "<BODY>\n");
|
|
|
|
if (md->current_message) {
|
|
if (mail_config_get_view_source ())
|
|
mail_format_raw_message (md->current_message, md);
|
|
else
|
|
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));
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Standard Gtk+ Class functions
|
|
*----------------------------------------------------------------------*/
|
|
|
|
static void
|
|
mail_display_init (GtkObject *object)
|
|
{
|
|
MailDisplay *mail_display = MAIL_DISPLAY (object);
|
|
|
|
/* various other initializations */
|
|
mail_display->current_message = NULL;
|
|
}
|
|
|
|
static void
|
|
mail_display_destroy (GtkObject *object)
|
|
{
|
|
MailDisplay *mail_display = MAIL_DISPLAY (object);
|
|
|
|
g_datalist_clear (mail_display->data);
|
|
g_free (mail_display->data);
|
|
|
|
mail_display_parent_class->destroy (object);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
on_selection_get (GtkWidget *widget, GtkSelectionData *selection_data,
|
|
guint info, guint time_stamp, gpointer data)
|
|
{
|
|
gchar *text;
|
|
|
|
text = gtk_object_get_data (GTK_OBJECT(widget), "selection");
|
|
if (text != NULL)
|
|
gtk_selection_data_set (selection_data,
|
|
GDK_SELECTION_TYPE_STRING,
|
|
8, text, strlen (text));
|
|
}
|
|
#endif
|
|
|
|
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\n");
|
|
}
|
|
|
|
static void
|
|
link_copy_location (GtkWidget *w, MailDisplay *mail_display)
|
|
{
|
|
g_print ("FIXME\n");
|
|
#if 0
|
|
gtk_object_set_data (GTK_OBJECT (mail_display->html),
|
|
"selection", g_strdup (mail_display->html->pointer_url));
|
|
|
|
gtk_selection_owner_set (GTK_WIDGET (mail_display->html),
|
|
GDK_SELECTION_PRIMARY,
|
|
GDK_CURRENT_TIME);
|
|
#endif
|
|
}
|
|
|
|
#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, 0 },
|
|
{ N_("Save as (FIXME)"), NULL,
|
|
GTK_SIGNAL_FUNC (link_save_as), NULL, 0 },
|
|
{ N_("Copy location (FIXME)"), NULL,
|
|
GTK_SIGNAL_FUNC (link_copy_location), NULL, 0 },
|
|
|
|
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 void
|
|
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);
|
|
}
|
|
|
|
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 *popup_thing;
|
|
const gchar *email;
|
|
const gchar *name;
|
|
const gchar *link;
|
|
|
|
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;
|
|
|
|
url = html_object_get_url (point->object);
|
|
|
|
if (url && !g_strncasecmp (url, "mailto:", 7)) {
|
|
|
|
popup_thing = bonobo_widget_new_control ("OAFIID:GNOME_Evolution_Addressbook_AddressPopup",
|
|
CORBA_OBJECT_NIL);
|
|
|
|
bonobo_widget_set_property (BONOBO_WIDGET (popup_thing),
|
|
"name", "",
|
|
"email", url+7,
|
|
NULL);
|
|
make_popup_window (popup_thing);
|
|
|
|
} else if ((link = html_object_get_url (point->object))) {
|
|
|
|
e_popup_menu_run (link_menu, (GdkEvent *) event, 0, 0, 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);
|
|
}
|
|
|
|
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 ();
|
|
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);
|
|
#if 0
|
|
gtk_selection_add_target (GTK_WIDGET(html),
|
|
GDK_SELECTION_PRIMARY,
|
|
GDK_SELECTION_TYPE_STRING, 1);
|
|
gtk_signal_connect (GTK_OBJECT (html), "selection_get",
|
|
GTK_SIGNAL_FUNC (on_selection_get), NULL);
|
|
#endif
|
|
|
|
gtk_container_add (GTK_CONTAINER (scroll), html);
|
|
gtk_widget_show (GTK_WIDGET (html));
|
|
|
|
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);
|