From 11ca78748823a81e9a063ea5354ff7f0051199eb Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 25 Apr 2000 23:00:52 +0000 Subject: Now that we're not limited to a single GtkHTML for the display, there's no * mail-format.c, mail-display.c: Now that we're not limited to a single GtkHTML for the display, there's no reason to embed Bonobo objects for unrecognized content-types in GtkHTML rather than embedded them into the vbox directly. So do that. Meanwhile, fix up the handler-selection code so that we can declare which built-in handlers are more desirable than external handlers and which are less. (Of course, eventually we'll want this to be customizable.) Add some cleverness to handle_multipart_alternative as well so it doesn't accept an alternative which we can display generically over one we can display specifically. svn path=/trunk/; revision=2616 --- mail/ChangeLog | 13 +++ mail/mail-display.c | 191 -------------------------------------- mail/mail-format.c | 257 ++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 191 insertions(+), 270 deletions(-) diff --git a/mail/ChangeLog b/mail/ChangeLog index 72ab07c756..0edb047b14 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,5 +1,18 @@ 2000-04-25 Dan Winship + * mail-format.c, mail-display.c: Now that we're not limited to + a single GtkHTML for the display, there's no reason to embed + Bonobo objects for unrecognized content-types in GtkHTML rather + than embedded them into the vbox directly. So do that. + + Meanwhile, fix up the handler-selection code so that we can + declare which built-in handlers are more desirable than external + handlers and which are less. (Of course, eventually we'll want + this to be customizable.) Add some cleverness to + handle_multipart_alternative as well so it doesn't accept an + alternative which we can display generically over one we can + display specifically. + * mail-format.c (reply_body): Fix some bugs that crept into reply generation. This needs a lot more work to deal correctly with complicated bodies. diff --git a/mail/mail-display.c b/mail/mail-display.c index 215a74c2ab..f5595dd671 100644 --- a/mail/mail-display.c +++ b/mail/mail-display.c @@ -14,116 +14,15 @@ #include "mail-display.h" #include "mail-format.h" -/* corba/bonobo stuff */ -#include -#include -#include - #define PARENT_TYPE (gtk_vbox_get_type ()) static GtkObjectClass *mail_display_parent_class; -/*----------------------------------------------------------------------* - * Helper utility functions - *----------------------------------------------------------------------*/ - - -/* stuff to display Bonobo Components inside the html message - * body view */ -static gboolean -hydrate_persist_stream_from_gstring (Bonobo_PersistStream persist_stream, - GString* gstr) -{ - CORBA_Environment ev; - BonoboStream* mem_stream = - bonobo_stream_mem_create (gstr->str, gstr->len, TRUE, FALSE); - CORBA_Object mem_stream_corba = - bonobo_object_corba_objref (BONOBO_OBJECT (mem_stream)); - - g_assert (persist_stream != CORBA_OBJECT_NIL); - - CORBA_exception_init (&ev); - - /* - * Load the file into the component using PersistStream. - */ - Bonobo_PersistStream_load (persist_stream, mem_stream_corba, &ev); - - bonobo_object_unref (BONOBO_OBJECT (mem_stream)); - - if (ev._major != CORBA_NO_EXCEPTION) { - gnome_warning_dialog (_("An exception occured while trying " - "to load data into the component with " - "PersistStream")); - CORBA_exception_free (&ev); - return FALSE; - } - - CORBA_exception_free (&ev); - return TRUE; -} - - -static GString* -camel_stream_to_gstring (CamelStream* stream) -{ - gchar tmp_buffer[4097]; - GString *tmp_gstring = g_string_new (""); - - do { /* read next chunk of text */ - - gint nb_bytes_read; - - nb_bytes_read = camel_stream_read (stream, - tmp_buffer, - 4096); - tmp_buffer [nb_bytes_read] = '\0'; - - /* If there's any text, append it to the gstring */ - if (nb_bytes_read > 0) { - tmp_gstring = g_string_append (tmp_gstring, tmp_buffer); - } - - } while (!camel_stream_eos (stream)); - - return tmp_gstring; -} - /*----------------------------------------------------------------------* * Callbacks *----------------------------------------------------------------------*/ -static void -embeddable_destroy_cb (GtkObject *obj, gpointer user_data) -{ - BonoboWidget *be; /* bonobo embeddable */ - BonoboViewFrame *vf; /* the embeddable view frame */ - BonoboObjectClient* server; - CORBA_Environment ev; - - printf ("in the bonobo embeddable destroy callback\n"); - be = BONOBO_WIDGET (obj); - server = bonobo_widget_get_server (be); - - vf = bonobo_widget_get_view_frame (be); - bonobo_control_frame_control_deactivate ( - BONOBO_CONTROL_FRAME (vf)); - /* w = bonobo_control_frame_get_widget (BONOBO_CONTROL_FRAME (vf)); */ - - /* gtk_widget_destroy (w); */ - - CORBA_exception_init (&ev); - Bonobo_Unknown_unref ( - bonobo_object_corba_objref (BONOBO_OBJECT(server)), &ev); - CORBA_Object_release ( - bonobo_object_corba_objref (BONOBO_OBJECT(server)), &ev); - - CORBA_exception_free (&ev); - bonobo_object_destroy (BONOBO_OBJECT (vf)); - /* gtk_object_unref (obj); */ -} - static CamelStream * cid_stream (const char *cid, CamelMimeMessage *message) { @@ -135,94 +34,6 @@ cid_stream (const char *cid, CamelMimeMessage *message) return camel_data_wrapper_get_output_stream (data); } -/* - * As a page is loaded, when gtkhtml comes across tags, this - * callback is invoked. The GtkHTMLEmbedded param is a GtkContainer; - * our job in this function is to simply add a child widget to it. - */ -static void -on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data) -{ - CamelStream *stream; - GString *camel_stream_gstr; - CamelMimeMessage *message = data; - GtkWidget *bonobo_embeddable; - BonoboObjectClient* server; - Bonobo_PersistStream persist; - CORBA_Environment ev; - gchar *uid = gtk_html_embedded_get_parameter (eb, "uid"); - - /* Both the classid (which specifies which bonobo object to - * fire up) and the uid (which tells us where to find data to - * persist from) must be available; if one of them isn't, - * print an error and bail. - */ - if (!uid || !eb->classid) { - printf ("on_object_requested: couldn't find %s%s%s\n", - uid ? "a uid" : "", - (!uid && !eb->classid) ? " or " : "", - eb->classid ? "a classid" : ""); - return; - } - printf ("object requested : %s\n", eb->classid); - printf ("UID = %s\n", uid); - - /* Try to get a server with goadid specified by eb->classid */ - bonobo_embeddable = bonobo_widget_new_subdoc (eb->classid, NULL); - gtk_signal_connect (GTK_OBJECT (bonobo_embeddable), - "destroy", embeddable_destroy_cb, NULL); - - server = bonobo_widget_get_server (BONOBO_WIDGET (bonobo_embeddable)); - if (!server) { - printf ("Couldn't get the server for the bonobo embeddable\n"); - return; - } - - if (!strncmp (uid, "cid:", 4)) { - stream = cid_stream (uid + 4, message); - g_return_if_fail (CAMEL_IS_STREAM (stream)); - } else - return; - - /* Try to get a PersistStream interface from the server; - if it doesn't support that interface, bail. */ - persist = (Bonobo_PersistStream) bonobo_object_client_query_interface ( - server, - "IDL:Bonobo/PersistStream:1.0", - NULL); - - if (persist == CORBA_OBJECT_NIL) { - gchar* msg = g_strdup_printf ( - _("The %s component doesn't support PersistStream!\n"), - uid); - - gnome_warning_dialog (msg); - gtk_object_unref (GTK_OBJECT (bonobo_embeddable)); - - g_free (msg); - return; - } - - /* Hydrate the PersistStream from the CamelStream */ - camel_stream_gstr = camel_stream_to_gstring (stream); - printf ("on_object_requested: The CamelStream has %d elements\n", - camel_stream_gstr->len); - hydrate_persist_stream_from_gstring (persist, camel_stream_gstr); - - /* Give our new window to the container */ - - gtk_widget_show (bonobo_embeddable); - gtk_container_add (GTK_CONTAINER(eb), bonobo_embeddable); - - /* Destroy the PersistStream object.*/ - CORBA_exception_init (&ev); - Bonobo_Unknown_unref (persist, &ev); - CORBA_Object_release (persist, &ev); - CORBA_exception_free (&ev); - - g_string_free (camel_stream_gstr, TRUE); -} - static void on_url_requested (GtkHTML *html, const char *url, GtkHTMLStreamHandle handle, gpointer user_data) @@ -265,8 +76,6 @@ mail_html_new (GtkHTML **html, GtkHTMLStreamHandle **stream, gtk_html_set_editable (*html, FALSE); gtk_signal_connect (GTK_OBJECT (*html), "size_request", GTK_SIGNAL_FUNC (html_size_req), NULL); - gtk_signal_connect (GTK_OBJECT (*html), "object_requested", - GTK_SIGNAL_FUNC (on_object_requested), root); gtk_signal_connect (GTK_OBJECT (*html), "url_requested", GTK_SIGNAL_FUNC (on_url_requested), root); diff --git a/mail/mail-format.c b/mail/mail-format.c index d1b3372fb7..eaef1320a2 100644 --- a/mail/mail-format.c +++ b/mail/mail-format.c @@ -28,6 +28,9 @@ #include "camel/hash-table-utils.h" #include +#include +#include +#include #include /* for isprint */ #include /* for strstr */ @@ -125,49 +128,20 @@ get_cid (CamelMimePart *part, CamelMimeMessage *root) return cid; } -/* This tries to create a tag, given a mimetype and the child of a - * mime message. It can return NULL if it can't match the mimetype to - * a bonobo object. - */ -static char * -get_bonobo_tag_for_object (CamelMimePart *part, CamelMimeMessage *root) -{ - GMimeContentField *type; - char *cid = get_cid (part, root), *mimetype; - const char *goad_id; - - type = camel_data_wrapper_get_mime_type_field ( - camel_medium_get_content_object (CAMEL_MEDIUM (part))); - mimetype = g_strdup_printf ("%s/%s", type->type, type->subtype); - goad_id = gnome_mime_get_value (mimetype, "bonobo-goad-id"); - g_free (mimetype); - - if (!goad_id) - goad_id = gnome_mime_get_value (type->type, "bonobo-goad-id"); - - if (goad_id) { - return g_strdup_printf (" " - "", - goad_id, cid); - } else - return NULL; -} - /* We're maintaining a hashtable of mimetypes -> functions; * Those functions have the following signature... */ typedef void (*mime_handler_fn) (CamelMimePart *part, CamelMimeMessage *root, GtkBox *box); -static GHashTable *mime_function_table; +static GHashTable *mime_function_table, *mime_fallback_table; static GHashTable *mime_icon_table; static void setup_function_table (void) { - mime_function_table = g_hash_table_new (g_strcase_hash, - g_strcase_equal); + mime_function_table = g_hash_table_new (g_str_hash, g_str_equal); + mime_fallback_table = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (mime_function_table, "text/plain", handle_text_plain); @@ -177,16 +151,11 @@ setup_function_table (void) handle_text_enriched); g_hash_table_insert (mime_function_table, "text/html", handle_text_html); - /* RFC 2046 says unrecognized text subtypes can be treated - * as text/plain (as long as you recognize the character set). - */ - g_hash_table_insert (mime_function_table, "text", - handle_text_plain); - g_hash_table_insert (mime_function_table, "image", + g_hash_table_insert (mime_function_table, "image/*", handle_image); - g_hash_table_insert (mime_function_table, "audio", + g_hash_table_insert (mime_function_table, "audio/*", handle_audio); g_hash_table_insert (mime_function_table, "message/rfc822", @@ -200,10 +169,14 @@ setup_function_table (void) handle_multipart_mixed); g_hash_table_insert (mime_function_table, "multipart/appledouble", handle_multipart_appledouble); - /* RFC 2046 says unrecognized multipart subtypes should be - * treated like multipart/mixed. + + /* RFC 2046 says unrecognized text subtypes can be treated + * as text/plain (as long as you recognize the character set), + * and unrecognized multipart subtypes as multipart/mixed. */ - g_hash_table_insert (mime_function_table, "multipart", + g_hash_table_insert (mime_fallback_table, "text/*", + handle_text_plain); + g_hash_table_insert (mime_function_table, "multipart/*", handle_multipart_mixed); } @@ -264,11 +237,11 @@ setup_icon_table (void) } static mime_handler_fn -lookup_handler (CamelMimePart *part) +lookup_handler (CamelMimePart *part, gboolean *generic) { CamelDataWrapper *wrapper; mime_handler_fn handler_function; - const char *goad_id; + const char *whole_goad_id, *generic_goad_id; char *mimetype_whole = NULL; char *mimetype_main = NULL; @@ -281,40 +254,73 @@ lookup_handler (CamelMimePart *part) wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - /* Try to find a handler function in our own lookup table. */ + /* OK. There are 6 possibilities, which we try in this order: + * 1) full match in the main table + * 2) partial match in the main table + * 3) full match in bonobo + * 4) full match in the fallback table + * 5) partial match in the fallback table + * 6) partial match in bonobo + * + * Of these, 1-4 are considered exact matches, and 5 and 6 are + * considered generic. + */ + + /* Check for full match in mime_function_table. */ mimetype_whole = camel_data_wrapper_get_mime_type (wrapper); g_strdown (mimetype_whole); - - handler_function = g_hash_table_lookup (mime_function_table, - mimetype_whole); - if (handler_function) - goto out; - - mimetype_main = g_strdup (wrapper->mime_type->type); + mimetype_main = g_strdup_printf ("%s/*", wrapper->mime_type->type); g_strdown (mimetype_main); handler_function = g_hash_table_lookup (mime_function_table, - mimetype_main); - if (handler_function) - goto out; - - /* See if there's a bonobo control that can show the object. */ - goad_id = gnome_mime_get_value (mimetype_whole, "bonobo-goad-id"); - if (goad_id) { - handler_function = handle_via_bonobo; - goto out; + mimetype_whole); + if (!handler_function) { + handler_function = g_hash_table_lookup (mime_function_table, + mimetype_main); + if (handler_function) { + /* Optimize this for the next time through. */ + g_hash_table_insert (mime_function_table, + g_strdup (mimetype_whole), + handler_function); + } } - goad_id = gnome_mime_get_value (mimetype_main, "bonobo-goad-id"); - if (goad_id) - handler_function = handle_via_bonobo; - - out: - if (mimetype_whole) + if (handler_function) { g_free (mimetype_whole); - if (mimetype_main) g_free (mimetype_main); + *generic = FALSE; + return handler_function; + } + whole_goad_id = gnome_mime_get_value (mimetype_whole, + "bonobo-goad-id"); + generic_goad_id = gnome_mime_get_value (mimetype_main, + "bonobo-goad-id"); + + if (whole_goad_id && (!generic_goad_id || + strcmp (whole_goad_id, generic_goad_id) != 0)) { + /* Optimize this for the next time through. */ + g_hash_table_insert (mime_function_table, + mimetype_whole, + handle_via_bonobo); + g_free (mimetype_main); + *generic = FALSE; + return handle_via_bonobo; + } + + handler_function = g_hash_table_lookup (mime_fallback_table, + mimetype_whole); + g_free (mimetype_whole); + if (handler_function) + *generic = FALSE; + else { + handler_function = g_hash_table_lookup (mime_fallback_table, + mimetype_main); + if (!handler_function && generic_goad_id) + handler_function = handle_via_bonobo; + *generic = TRUE; + } + g_free (mimetype_main); return handler_function; } @@ -324,10 +330,11 @@ call_handler_function (CamelMimePart *part, CamelMimeMessage *root, { CamelDataWrapper *wrapper; mime_handler_fn handler_function = NULL; + gboolean generic; wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - handler_function = lookup_handler (part); + handler_function = lookup_handler (part, &generic); if (handler_function) (*handler_function) (part, root, box); @@ -837,7 +844,7 @@ handle_multipart_related (CamelMimePart *part, CamelMimeMessage *root, GtkBox *b part = CAMEL_MIME_PART (body_part); cid = get_cid (part, root); g_free (cid); - } + } /* Now, display the displayed part. */ call_handler_function (CAMEL_MIME_PART (display_part), root, box); @@ -849,13 +856,15 @@ find_preferred_alternative (CamelMultipart *multipart) { int i, nparts; CamelMimePart *preferred_part = NULL; + gboolean generic; nparts = camel_multipart_get_number (multipart); for (i = 0; i < nparts; i++) { CamelMimePart *part = CAMEL_MIME_PART ( camel_multipart_get_part (multipart, i)); - if (lookup_handler (part)) + if (lookup_handler (part, &generic) && + (!preferred_part || !generic)) preferred_part = part; } @@ -966,20 +975,110 @@ handle_unknown_type (CamelMimePart *part, CamelMimeMessage *root, GtkBox *box) g_free (id); } +static void +embeddable_destroy_cb (GtkObject *obj, gpointer user_data) +{ + BonoboWidget *be; /* bonobo embeddable */ + BonoboViewFrame *vf; /* the embeddable view frame */ + BonoboObjectClient* server; + CORBA_Environment ev; + + be = BONOBO_WIDGET (obj); + server = bonobo_widget_get_server (be); + + vf = bonobo_widget_get_view_frame (be); + bonobo_control_frame_control_deactivate ( + BONOBO_CONTROL_FRAME (vf)); + /* w = bonobo_control_frame_get_widget (BONOBO_CONTROL_FRAME (vf)); */ + + /* gtk_widget_destroy (w); */ + + CORBA_exception_init (&ev); + Bonobo_Unknown_unref ( + bonobo_object_corba_objref (BONOBO_OBJECT(server)), &ev); + CORBA_Object_release ( + bonobo_object_corba_objref (BONOBO_OBJECT(server)), &ev); + + CORBA_exception_free (&ev); + bonobo_object_destroy (BONOBO_OBJECT (vf)); + /* gtk_object_unref (obj); */ +} + static void handle_via_bonobo (CamelMimePart *part, CamelMimeMessage *root, GtkBox *box) { - GtkHTML *html; - GtkHTMLStreamHandle *stream; - char *bonobo_tag = get_bonobo_tag_for_object (part, root); + CamelDataWrapper *wrapper = + camel_medium_get_content_object (CAMEL_MEDIUM (part)); + GMimeContentField *type; + char *mimetype; + const char *goad_id; + GtkWidget *embedded; + BonoboObjectClient *server; + Bonobo_PersistStream persist; + CORBA_Environment ev; + GByteArray *ba; + CamelStream *cstream; + BonoboStream *bstream; - g_return_if_fail (bonobo_tag); + type = camel_data_wrapper_get_mime_type_field ( + camel_medium_get_content_object (CAMEL_MEDIUM (part))); + mimetype = g_strdup_printf ("%s/%s", type->type, type->subtype); + goad_id = gnome_mime_get_value (mimetype, "bonobo-goad-id"); + g_free (mimetype); - mail_html_new (&html, &stream, root, TRUE); - mail_html_write (html, stream, bonobo_tag); - mail_html_end (html, stream, TRUE, box); + if (!goad_id) + goad_id = gnome_mime_get_value (type->type, "bonobo-goad-id"); + if (!goad_id) + return; + + embedded = bonobo_widget_new_subdoc (goad_id, NULL); + if (!embedded) + return; + server = bonobo_widget_get_server (BONOBO_WIDGET (embedded)); + if (!server) { + bonobo_object_destroy (BONOBO_OBJECT (embedded)); + return; + } + + persist = (Bonobo_PersistStream) bonobo_object_client_query_interface ( + server, "IDL:Bonobo/PersistStream:1.0", NULL); + if (persist == CORBA_OBJECT_NIL) { + bonobo_object_destroy (BONOBO_OBJECT (embedded)); + return; + } + + /* Write the data to a CamelStreamMem... */ + ba = g_byte_array_new (); + cstream = camel_stream_mem_new_with_byte_array ( + ba, CAMEL_STREAM_MEM_WRITE); + 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_stream_close (cstream); + + /* ...and hydrate the PersistStream from the BonoboStream. */ + CORBA_exception_init (&ev); + Bonobo_PersistStream_load (persist, + bonobo_object_corba_objref ( + BONOBO_OBJECT (bstream)), + &ev); + bonobo_object_unref (BONOBO_OBJECT (bstream)); + Bonobo_Unknown_unref (persist, &ev); + CORBA_Object_release (persist, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + bonobo_object_unref (BONOBO_OBJECT (embedded)); + CORBA_exception_free (&ev); + return; + } + CORBA_exception_free (&ev); - g_free (bonobo_tag); + /* Embed the widget. */ + gtk_widget_show (embedded); + gtk_box_pack_start (box, embedded, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT (embedded), "destroy", + embeddable_destroy_cb, NULL); } -- cgit v1.2.3