setup private data. (mail_display_destroy): cancel any outstanding

2002-07-29  Not Zed  <NotZed@Ximian.com>

        * mail-display.c (mail_display_init): setup private data.
        (mail_display_destroy): cancel any outstanding fetches, and free
        private data.
        (mail_display_set_message): Cancel any outstanding fetches.
        (fetch_cancelled):
        (fetch_next):
        (fetch_remote):
        (fetch_data):
        (fetch_free):
        (fetch_cancel):
        (fetch_done): Implement, used to use soup to download remote
        images.
        (load_http): Removed.
        (on_url_requested): When requesting a http* url, use the
        fetch_remote call above.

        * mail-display.h (struct _MailDisplay): Added priv(ate) member.

svn path=/trunk/; revision=17640
This commit is contained in:
Not Zed
2002-07-30 14:20:06 +00:00
committed by Michael Zucci
parent 2f5cc27e5a
commit 432ddb8b68
5 changed files with 238 additions and 51 deletions

View File

@ -1,3 +1,23 @@
2002-07-29 Not Zed <NotZed@Ximian.com>
* mail-display.c (mail_display_init): setup private data.
(mail_display_destroy): cancel any outstanding fetches, and free
private data.
(mail_display_set_message): Cancel any outstanding fetches.
(fetch_cancelled):
(fetch_next):
(fetch_remote):
(fetch_data):
(fetch_free):
(fetch_cancel):
(fetch_done): Implement, used to use soup to download remote
images.
(load_http): Removed.
(on_url_requested): When requesting a http* url, use the
fetch_remote call above.
* mail-display.h (struct _MailDisplay): Added priv(ate) member.
2002-07-29 Ettore Perazzoli <ettore@ximian.com>
* mail-session.c (request_password): Left-align the check button's

View File

@ -40,6 +40,7 @@
#include <gtkhtml/htmltext.h>
#include <gtkhtml/htmlinterval.h>
#include <gtkhtml/gtkhtml-stream.h>
#include <libsoup/soup-message.h>
#include "e-util/e-html-utils.h"
#include "e-util/e-mktemp.h"
@ -59,6 +60,50 @@
#include "art/empty.xpm"
#define d(x)
struct _MailDisplayPrivate {
/* because we want to control resource usage, we need our own queues, etc */
EDList fetch_active;
EDList fetch_queue;
/* used to try and make some sense with progress reporting */
int fetch_total;
int fetch_total_done;
/* bit hackish, 'fake' an async message and processing,
so we can use that to get cancel and report progress */
struct _mail_msg *fetch_msg ;
GIOChannel *fetch_cancel_channel;
guint fetch_cancel_watch;
};
/* max number of connections to download images */
#define FETCH_MAX_CONNECTIONS (4)
/* for asynchronously downloading remote content */
struct _remote_data {
struct _remote_data *next;
struct _remote_data *prev;
MailDisplay *md; /* not ref'd */
SoupMessage *msg;
char *uri;
GtkHTML *html;
GtkHTMLStream *stream;
size_t length;
size_t total;
};
static void fetch_remote(MailDisplay *md, const char *uri, GtkHTML *html, GtkHTMLStream *stream);
static void fetch_cancel(MailDisplay *md);
static void fetch_next(MailDisplay *md);
static void fetch_data(SoupMessage *req, void *data);
static void fetch_free(struct _remote_data *rd);
static void fetch_done(SoupMessage *req, void *data);
#define PARENT_TYPE (gtk_vbox_get_type ())
static GtkObjectClass *mail_display_parent_class;
@ -1096,47 +1141,6 @@ on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
return FALSE;
}
static void
load_http (MailDisplay *md, gpointer data)
{
char *url = data;
GHashTable *urls;
GnomeVFSHandle *handle;
GnomeVFSFileSize read;
GnomeVFSResult result;
GByteArray *ba;
char buf[8192];
size_t total = 0;
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 ((result = gnome_vfs_read (handle, buf, sizeof (buf), &read)) == GNOME_VFS_OK) {
printf ("%s: read %d bytes\n", url, (int) read);
g_byte_array_append (ba, buf, read);
total += read;
}
gnome_vfs_close (handle);
printf ("gnome_vfs_read result is %d; read %d total bytes\n", result, total);
#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)
@ -1224,10 +1228,7 @@ on_url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle,
if (strncmp (url, "http:", 5) == 0 || strncmp (url, "https:", 6) == 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_stream_write_when_loaded (md, ba, url, load_http, html, handle,
g_strdup (url));
fetch_remote(md, url, html, handle);
} 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);
@ -1245,6 +1246,158 @@ on_url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle,
}
}
/* for processing asynchronous url fetch cancels */
static struct _mail_msg_op fetch_fake_op = {
NULL, NULL, NULL, NULL,
};
static gboolean fetch_cancelled(GIOChannel *source, GIOCondition cond, void *data)
{
fetch_cancel((MailDisplay *)data);
return FALSE;
}
static void fetch_next(MailDisplay *md)
{
struct _remote_data *rd;
struct _MailDisplayPrivate *p = md->priv;
SoupMessage *msg;
SoupContext *ctx;
/* if we're called and no more work to do, clean up, otherwise, setup */
if (e_dlist_empty(&p->fetch_active) && e_dlist_empty(&p->fetch_queue)) {
if (p->fetch_msg) {
p->fetch_total = 0;
mail_disable_stop();
camel_operation_end(p->fetch_msg->cancel);
camel_operation_unregister(p->fetch_msg->cancel);
mail_msg_free(p->fetch_msg);
p->fetch_msg = NULL;
g_source_remove(p->fetch_cancel_watch);
g_io_channel_unref(p->fetch_cancel_channel);
}
} else {
if (p->fetch_msg == NULL) {
p->fetch_total_done = 0;
p->fetch_msg = mail_msg_new(&fetch_fake_op, NULL, sizeof(*p->fetch_msg));
camel_operation_register(p->fetch_msg->cancel);
camel_operation_start(p->fetch_msg->cancel, _("Downloading images"));
p->fetch_cancel_channel = g_io_channel_unix_new(camel_operation_cancel_fd(p->fetch_msg->cancel));
p->fetch_cancel_watch = g_io_add_watch(p->fetch_cancel_channel, G_IO_IN, fetch_cancelled, md);
mail_enable_stop();
}
}
while (e_dlist_length(&p->fetch_active) < FETCH_MAX_CONNECTIONS
&& (rd = (struct _remote_data *)e_dlist_remhead(&p->fetch_queue))) {
ctx = soup_context_get(rd->uri);
rd->msg = msg = soup_message_new(ctx, SOUP_METHOD_GET);
soup_context_unref(ctx);
soup_message_set_flags(msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
soup_message_add_handler(msg, SOUP_HANDLER_BODY_CHUNK, fetch_data, rd);
soup_message_queue(msg, fetch_done, rd);
e_dlist_addtail(&p->fetch_active, (EDListNode *)rd);
}
}
static void fetch_remote(MailDisplay *md, const char *uri, GtkHTML *html, GtkHTMLStream *stream)
{
struct _remote_data *rd;
rd = g_malloc0(sizeof(*rd));
rd->md = md; /* dont ref */
rd->uri = g_strdup(uri);
rd->html = html;
gtk_object_ref((GtkObject *)html);
rd->stream = stream;
md->priv->fetch_total++;
e_dlist_addtail(&md->priv->fetch_queue, (EDListNode *)rd);
fetch_next(md);
}
static void fetch_data(SoupMessage *req, void *data)
{
struct _remote_data *rd = data, *wd;
struct _MailDisplayPrivate *p = rd->md->priv;
int count;
double complete;
/* we could just hook into the header function for this, but i'm lazy today */
if (rd->total == 0) {
const char *cl = soup_message_get_header(req->response_headers, "content-length");
if (cl)
rd->total = strtoul(cl, 0, 10);
else
rd->total = 0;
}
rd->length += req->response.length;
gtk_html_write(rd->html, rd->stream, req->response.body, req->response.length);
/* update based on total active + finished totals */
complete = 0.0;
wd = (struct _remote_data *)p->fetch_active.head;
count = e_dlist_length(&p->fetch_active);
while (wd->next) {
if (wd->total)
complete += (double)wd->length / wd->total / count;
wd = wd->next;
}
d(printf("%s: %f total %f (%d,%d)\n", rd->uri, complete, (p->fetch_total_done + complete ) * 100.0 / p->fetch_total, p->fetch_total, p->fetch_total_done));
camel_operation_progress(p->fetch_msg->cancel, (p->fetch_total_done + complete ) * 100.0 / p->fetch_total);
}
static void fetch_free(struct _remote_data *rd)
{
gtk_object_unref((GtkObject *)rd->html);
g_free(rd->uri);
g_free(rd);
}
static void fetch_done(SoupMessage *req, void *data)
{
struct _remote_data *rd = data;
MailDisplay *md = rd->md;
if (SOUP_MESSAGE_IS_ERROR(req)) {
d(printf("Loading '%s' failed!\n", rd->uri));
gtk_html_end(rd->html, rd->stream, GTK_HTML_STREAM_ERROR);
} else {
d(printf("Loading '%s' complete!\n", rd->uri));
gtk_html_end(rd->html, rd->stream, GTK_HTML_STREAM_OK);
}
e_dlist_remove((EDListNode *)rd);
fetch_free(rd);
md->priv->fetch_total_done++;
fetch_next(md);
}
static void fetch_cancel(MailDisplay *md)
{
struct _remote_data *rd;
/* first, clean up all the ones we haven't finished yet */
while ((rd = (struct _remote_data *)e_dlist_remhead(&md->priv->fetch_queue))) {
gtk_html_end(rd->html, rd->stream, GTK_HTML_STREAM_ERROR);
fetch_free(rd);
}
/* cancel the rest, cancellation will free it/etc */
while (!e_dlist_empty(&md->priv->fetch_active)) {
rd = (struct _remote_data *)md->priv->fetch_active.head;
soup_message_cancel(rd->msg);
}
}
struct _load_content_msg {
struct _mail_msg msg;
@ -1585,6 +1738,8 @@ mail_display_redisplay (MailDisplay *md, gboolean reset_scroll)
{
if (GTK_OBJECT_DESTROYED (md))
return;
fetch_cancel(md);
md->last_active = NULL;
md->redisplay_counter++;
@ -1614,6 +1769,7 @@ mail_display_set_message (MailDisplay *md, CamelMedium *medium, const char *foll
/* Clean up from previous message. */
if (md->current_message) {
fetch_cancel(md);
camel_object_unref (CAMEL_OBJECT (md->current_message));
g_datalist_clear (md->data);
}
@ -1687,18 +1843,23 @@ mail_display_init (GtkObject *object)
mail_display->display_style = mail_config_get_message_display_style ();
mail_display->printing = FALSE;
mail_display->priv = g_malloc0(sizeof(*mail_display->priv));
e_dlist_init(&mail_display->priv->fetch_active);
e_dlist_init(&mail_display->priv->fetch_queue);
}
static void
mail_display_destroy (GtkObject *object)
{
MailDisplay *mail_display = MAIL_DISPLAY (object);
gtk_object_unref (GTK_OBJECT (mail_display->html));
if (mail_display->current_message) {
camel_object_unref (mail_display->current_message);
g_datalist_clear (mail_display->data);
fetch_cancel(mail_display);
}
g_free (mail_display->charset);
@ -1712,6 +1873,8 @@ mail_display_destroy (GtkObject *object)
gtk_timeout_remove (mail_display->idle_id);
gtk_widget_unref (mail_display->invisible);
g_free(mail_display->priv);
mail_display_parent_class->destroy (object);
}

View File

@ -25,6 +25,8 @@
struct _MailDisplay {
GtkVBox parent;
struct _MailDisplayPrivate *priv;
EScrollFrame *scroll;
GtkHTML *html;
/* GtkHTMLStream *stream; */
@ -44,7 +46,7 @@ struct _MailDisplay {
relative URL Content-Location on a leaf part in order to
construct the full URL */
struct _location_url_stack *urls;
GHashTable *related; /* related parts not displayed yet */
/* Sigh. This shouldn't be needed. I haven't figured out why it is

View File

@ -40,8 +40,6 @@
#define d(x)
static void set_stop(int sensitive);
static void mail_enable_stop(void);
static void mail_disable_stop(void);
static void mail_operation_status(struct _CamelOperation *op, const char *what, int pc, void *data);
#ifdef LOG_LOCKS
@ -799,7 +797,7 @@ struct _mail_msg_op set_busy_op = {
NULL,
};
static void mail_enable_stop(void)
void mail_enable_stop(void)
{
struct _mail_msg *m;
@ -812,7 +810,7 @@ static void mail_enable_stop(void)
MAIL_MT_UNLOCK(status_lock);
}
static void mail_disable_stop(void)
void mail_disable_stop(void)
{
struct _mail_msg *m;

View File

@ -102,6 +102,10 @@ typedef void *(*MailMainFunc)();
void *mail_call_main(mail_call_t type, MailMainFunc func, ...);
/* use with caution. only works with active message's anyway */
void mail_enable_stop(void);
void mail_disable_stop(void);
/* a message port that receives messages in the gui thread, used for sending port */
extern EMsgPort *mail_gui_port;
/* a message port that receives messages in the gui thread, used for the reply port */