From 16068d9b4191ea142a9e75a50eb8d260ed2bb406 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 2 Apr 2008 18:37:22 +0000 Subject: ** Merge the mbarnes-composer branch 2008-04-02 Matthew Barnes ** Merge the mbarnes-composer branch * configure.in: Bump libgtkhtml requirement to 3.19.1. Add gtkhtml-editor dependency for addressbook, calendar and mail. Remove print-message plugin; new composer implements this natively. * tools/Makefile.am: Remove CORBA rules for the old composer. * addressbook/gui/widgets/Makefile.am: Remove CORBA rules for the old composer. * addressbook/gui/widgets/eab-gui-util.c (eab_send_to_contact_and_email_num_list), (eab_send_contact_list_as_attachment): Adapt to new Bonobo-less composer widget. * calendar/gui/Makefile.am: Remove CORBA rules for the old composer. * calendar/gui/itip-utils.c (comp_from), (comp_to_list), (comp_subject), (comp_content_type), (comp_filename), (comp_description), (append_cal_attachments), (itip_send_comp), (reply_to_calendar_comp): Adapt to new Bonobo-less composer widget. * composer/Makefile.am: Remove CORBA rules for the old composer. * composer/e-msg-composer.c: * composer/e-msg-composer.h: EMsgComposer is now a subclass of GtkhtmlEditor. Extensive refactoring and cleanup, too much to list in detail. * composer/e-composer-header.c: * composer/e-composer-header.h: Add "sensitive" property along with get/set functions. * composer/e-composer-from-header.c: * composer/e-composer-from-header.h: Propagate "refreshed" signal from EAccountComboBox. Add function e_composer_from_header_get_account_list(). * composer/e-composer-private.c: * composer/e-composer-private.h: New files manage composer's private data. Allows other composer files to manipulate private data. * composer/e-msg-composer-hdrs.c: * composer/e-msg-composer-hdrs.h: Remove these files; replaced by EComposerHeaderTable widget. * composer/evolution-composer.c: * composer/evolution-composer.h: Remove these files; composer is now a subclass of GtkhtmlEditor. * composer/e-msg-composer-select-file.c: * composer/e-msg-composer-select-file.h: Remove these files; logic moved to e-msg-composer.c. * composer/listener.c: * composer/listener.h: Remove these files; event handlers moved to e-msg-composer.c. * composer/Composer.idl: * composer/Evolution-Composer.idl: Remove these files; composer is no longer a Bonobo object. * mail/em-composer-prefs (sig_edit_cb), (em_composer_prefs_new_signature): Adapt to new Bonobo-less signature editor. * mail/mail-signature-editor.c: * mail/mail-signature-editor.h: Rewrite the signature editor as a subclass of GtkhtmlEditor. Eliminates Bonobo from the equation. * mail/em-composer-utils.c (composer_get_message), (em_utils_composer_send_cb), (save_draft_done), (em_utils_composer_save_draft_cb), (create_new_composer), (em_utils_compose_new_message), (em_utils_compose_new_message_with_mailto), (em_utils_post_to_folder), (em_utils_post_to_url), (edit_message), (forward_attached), (forward_non_attached), (reply_get_composer), (composer_set_body), (em_utils_reply_to_message), (post_reply_to_message): Adapt to new Bonobo-less composer. * mail/mail-component-factory.c: Composer is no longer needs a Bonobo factory. * mail/mail-config.c: Fix style pattern for EMsgComposer widgets. * plugins/groupwise/mail-send-options.c (org_gnome_composer_send_options): Adapt to streamlined EMsgComposer API. * plugins/exchange-operations/Makefile.am: Add EVOLUTION_MAIL_CFLAGS and EVOLUTION_MAIL_LIBS. * plugins/exchange-operations/exchange-mail-send-options.c (append_to_header), (org_gnome_exchange_send_options): Adapt to streamlined EMsgComposer API. * plugins/mailing-list-actions/mailing-list-actions.c (emla_list_action_do): Adapt to streamlined EMsgComposer API. * po/POTFILES.in: Update file list for new composer. * ui/evolution-composer-entries.xml: Remove this file; obsoleted by new composer. * widgets/misc/Makefile.am: Add EVOLUTION_MAIL_LIBS. * widgets/misc/e-account-combo-box.c: * widgets/misc/e-account-combo-box.h: New function e_account_combo_box_get_account_list(). Emit a "refreshed" signal when the EAccountList changes. Add an internal reverse-lookup index. * widgets/misc/e-charset-picker.c (e_charser_add_radio_actions): New function adds radio actions to an action group. Will eventually replace e_charset_picker_bonobo_ui_populate(). * widgets/misc/e-signature-combo-box.c: * widgets/misc/e-signature-combo-box.h: New function e_signature_combo_box_get_signature_list(). ... separate issue ... * configure.in: Bump eds_minimum_version to 2.23.1 for CAMEL_FOLDER_JUNKED_NOT_DELETED symbol. svn path=/trunk/; revision=35313 --- composer/e-msg-composer.c | 7926 +++++++++++++++++---------------------------- 1 file changed, 3008 insertions(+), 4918 deletions(-) (limited to 'composer/e-msg-composer.c') diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index d346bc2821..fe4af52e8a 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -30,7 +30,7 @@ TODO - - Somehow users should be able to see if any file(s) are attached even when + - Somehow users should be able to see if any file (s) are attached even when the attachment bar is not shown. Should use EventSources to keep track of global changes made to configuration @@ -43,45 +43,30 @@ #define SMIME_SUPPORTED 1 -#include +#include +#include +#include #include +#include +#include #include #include -#include -#include #include -#include -#include #include -#include #include +#include #include #include -#include -#include #include -#include -#include -#include - -#include -#include -#include -#include -#include - #include #include -#include - #include "e-util/e-dialog-utils.h" -#include "e-util/e-signature-list.h" #include "misc/e-charset-picker.h" #include "misc/e-expander.h" #include "e-util/e-error.h" @@ -119,113 +104,53 @@ #include "e-msg-composer.h" #include "e-attachment.h" #include "e-attachment-bar.h" -#include "e-msg-composer-hdrs.h" -#include "e-msg-composer-select-file.h" +#include "e-composer-autosave.h" +#include "e-composer-private.h" +#include "e-composer-header-table.h" #include "evolution-shell-component-utils.h" #include -#include "Editor.h" -#include "listener.h" - #ifdef HAVE_XFREE #include #endif -#define GNOME_GTKHTML_EDITOR_CONTROL_ID "OAFIID:GNOME_GtkHTML_Editor:" GTKHTML_API_VERSION - -#define COMPOSER_CURRENT_FOLDER_KEY "/apps/evolution/mail/composer/current_folder" - #define d(x) -typedef struct _EMsgComposerPrivate EMsgComposerPrivate; - -struct _EMsgComposer { - BonoboWindow parent; - - EMsgComposerPrivate *priv; -}; - -struct _EMsgComposerClass { - BonoboWindowClass parent_class; - - void (* send) (EMsgComposer *composer); - void (* save_draft) (EMsgComposer *composer, int quit); -}; - - -struct _EMsgComposerPrivate { - - -/* Main UIComponent */ - BonoboUIComponent *uic; - - /* UIComponent for the non-control GtkEntries */ - BonoboUIComponent *entry_uic; - - GtkWidget *hdrs; - GPtrArray *extra_hdr_names, *extra_hdr_values; - - GtkWidget *focused_entry; - - GtkWidget *eeditor; - - GtkWidget *attachment_bar; - GtkWidget *attachment_scrolled_window; - GtkWidget *attachment_expander; - GtkWidget *attachment_expander_label; - GtkWidget *attachment_expander_icon; - GtkWidget *attachment_expander_num; - - GtkWidget *address_dialog; - - Bonobo_PersistFile persist_file_interface; - Bonobo_PersistStream persist_stream_interface; - GNOME_GtkHTML_Editor_Engine eeditor_engine; - BonoboObject *eeditor_listener; - GHashTable *inline_images, *inline_images_by_url; - GList *current_images; - - char *mime_type, *mime_body, *charset; - - char *autosave_file; - int autosave_fd; - guint32 enable_autosave : 1; - - guint32 attachment_bar_visible : 1; - guint32 send_html : 1; - guint32 is_alternative : 1; - guint32 pgp_sign : 1; - guint32 pgp_encrypt : 1; - guint32 smime_sign : 1; - guint32 smime_encrypt : 1; - guint32 view_from : 1; - guint32 view_replyto : 1; - guint32 view_to : 1; - guint32 view_postto : 1; - guint32 view_bcc : 1; - guint32 view_cc : 1; - guint32 view_subject : 1; - guint32 request_receipt : 1; - guint32 set_priority : 1; - guint32 has_changed : 1; - guint32 autosaved : 1; - - guint32 mode_post : 1; - - guint32 in_signature_insert : 1; - - CamelMimeMessage *redirect; - - guint notify_id; - - gboolean send_invoked; - EMMenu *menu; - - GtkWidget *saveas; /* saveas async file requester */ - GtkWidget *load; /* same for load - not used */ - -}; +#define E_MSG_COMPOSER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MSG_COMPOSER, EMsgComposerPrivate)) + +#define E_MSG_COMPOSER_VISIBLE_MASK_SENDER \ + (E_MSG_COMPOSER_VISIBLE_FROM | \ + E_MSG_COMPOSER_VISIBLE_REPLYTO) + +#define E_MSG_COMPOSER_VISIBLE_MASK_BASIC \ + (E_MSG_COMPOSER_VISIBLE_MASK_SENDER | \ + E_MSG_COMPOSER_VISIBLE_SUBJECT) + +#define E_MSG_COMPOSER_VISIBLE_MASK_RECIPIENTS \ + (E_MSG_COMPOSER_VISIBLE_TO | \ + E_MSG_COMPOSER_VISIBLE_CC | \ + E_MSG_COMPOSER_VISIBLE_BCC) + +#define E_MSG_COMPOSER_VISIBLE_MASK_MAIL \ + (E_MSG_COMPOSER_VISIBLE_MASK_BASIC | \ + E_MSG_COMPOSER_VISIBLE_MASK_RECIPIENTS) + +#define E_MSG_COMPOSER_VISIBLE_MASK_POST \ + (E_MSG_COMPOSER_VISIBLE_MASK_BASIC | \ + E_MSG_COMPOSER_VISIBLE_POSTTO) + +typedef enum { + E_MSG_COMPOSER_VISIBLE_FROM = (1 << 0), + E_MSG_COMPOSER_VISIBLE_REPLYTO = (1 << 1), + E_MSG_COMPOSER_VISIBLE_TO = (1 << 2), + E_MSG_COMPOSER_VISIBLE_CC = (1 << 3), + E_MSG_COMPOSER_VISIBLE_BCC = (1 << 4), + E_MSG_COMPOSER_VISIBLE_POSTTO = (1 << 5), + E_MSG_COMPOSER_VISIBLE_SUBJECT = (1 << 7) +} EMsgComposerHeaderVisibleFlags; enum { SEND, @@ -233,77 +158,66 @@ enum { LAST_SIGNAL }; -static guint signals[LAST_SIGNAL] = { 0 }; - enum { DND_TYPE_MESSAGE_RFC822, DND_TYPE_X_UID_LIST, DND_TYPE_TEXT_URI_LIST, DND_TYPE_NETSCAPE_URL, DND_TYPE_TEXT_VCARD, - DND_TYPE_TEXT_CALENDAR, + DND_TYPE_TEXT_CALENDAR }; static GtkTargetEntry drop_types[] = { { "message/rfc822", 0, DND_TYPE_MESSAGE_RFC822 }, - { "x-uid-list", 0, DND_TYPE_X_UID_LIST }, - { "text/uri-list", 0, DND_TYPE_TEXT_URI_LIST }, - { "_NETSCAPE_URL", 0, DND_TYPE_NETSCAPE_URL }, - { "text/x-vcard", 0, DND_TYPE_TEXT_VCARD }, - { "text/calendar", 0, DND_TYPE_TEXT_CALENDAR }, + { "x-uid-list", 0, DND_TYPE_X_UID_LIST }, + { "text/uri-list", 0, DND_TYPE_TEXT_URI_LIST }, + { "_NETSCAPE_URL", 0, DND_TYPE_NETSCAPE_URL }, + { "text/x-vcard", 0, DND_TYPE_TEXT_VCARD }, + { "text/calendar", 0, DND_TYPE_TEXT_CALENDAR } }; -#define num_drop_types (sizeof (drop_types) / sizeof (drop_types[0])) - static struct { - char *target; + gchar *target; GdkAtom atom; guint32 actions; } drag_info[] = { { "message/rfc822", NULL, GDK_ACTION_COPY }, - { "x-uid-list", NULL, GDK_ACTION_ASK|GDK_ACTION_MOVE|GDK_ACTION_COPY }, - { "text/uri-list", NULL, GDK_ACTION_COPY }, - { "_NETSCAPE_URL", NULL, GDK_ACTION_COPY }, - { "text/x-vcard", NULL, GDK_ACTION_COPY }, - { "text/calendar", NULL, GDK_ACTION_COPY }, + { "x-uid-list", NULL, GDK_ACTION_ASK | + GDK_ACTION_MOVE | + GDK_ACTION_COPY }, + { "text/uri-list", NULL, GDK_ACTION_COPY }, + { "_NETSCAPE_URL", NULL, GDK_ACTION_COPY }, + { "text/x-vcard", NULL, GDK_ACTION_COPY }, + { "text/calendar", NULL, GDK_ACTION_COPY } }; -static const char *emc_draft_format_names[] = { "pgp-sign", "pgp-encrypt", "smime-sign", "smime-encrypt" }; - - -/* The parent class. */ -static BonoboWindowClass *parent_class = NULL; +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; /* All the composer windows open, for bookkeeping purposes. */ static GSList *all_composers = NULL; - /* local prototypes */ -static GList *add_recipients (GList *list, const char *recips); +static GList *add_recipients (GList *list, const gchar *recips); -static void handle_mailto (EMsgComposer *composer, const char *mailto); -static void handle_uri (EMsgComposer *composer, const char *uri, gboolean html_dnd); +static void handle_mailto (EMsgComposer *composer, const gchar *mailto); +static void handle_uri (EMsgComposer *composer, const gchar *uri, gboolean html_dnd); -/* used by e_msg_composer_add_message_attachments() */ +/* used by e_msg_composer_add_message_attachments () */ static void add_attachments_from_multipart (EMsgComposer *composer, CamelMultipart *multipart, - gboolean just_inlines, int depth); - -/* used by e_msg_composer_new_with_message() */ -static void handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, int depth); -static void handle_multipart_alternative (EMsgComposer *composer, CamelMultipart *multipart, int depth); -static void handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, int depth); -static void handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, int depth); + gboolean just_inlines, gint depth); -static void set_editor_signature (EMsgComposer *composer); +/* used by e_msg_composer_new_with_message () */ +static void handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, gint depth); +static void handle_multipart_alternative (EMsgComposer *composer, CamelMultipart *multipart, gint depth); +static void handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, gint depth); +static void handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, gint depth); -/* used by e_msg_composer for showing the help menu item */ -static void e_msg_composer_show_help (EMsgComposer *composer); - static EDestination** -destination_list_to_vector_sized (GList *list, int n) +destination_list_to_vector_sized (GList *list, gint n) { EDestination **destv; - int i = 0; + gint i = 0; if (n == -1) n = g_list_length (list); @@ -329,43 +243,14 @@ destination_list_to_vector (GList *list) return destination_list_to_vector_sized (list, -1); } -static GByteArray * -get_text (Bonobo_PersistStream persist, char *format) -{ - BonoboStream *stream; - BonoboStreamMem *stream_mem; - CORBA_Environment ev; - GByteArray *text; - - CORBA_exception_init (&ev); - - stream = bonobo_stream_mem_create (NULL, 0, FALSE, TRUE); - Bonobo_PersistStream_save (persist, (Bonobo_Stream)bonobo_object_corba_objref (BONOBO_OBJECT (stream)), - format, &ev); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("Exception getting mail '%s'", - bonobo_exception_get_text (&ev)); - return NULL; - } - - CORBA_exception_free (&ev); - - stream_mem = BONOBO_STREAM_MEM (stream); - text = g_byte_array_new (); - g_byte_array_append (text, (const guint8 *)stream_mem->buffer, stream_mem->pos); - bonobo_object_unref (BONOBO_OBJECT (stream)); - - return text; -} - #define LINE_LEN 72 static CamelTransferEncoding -best_encoding (GByteArray *buf, const char *charset) +best_encoding (GByteArray *buf, const gchar *charset) { - char *in, *out, outbuf[256], *ch; - size_t inlen, outlen; - int status, count = 0; + gchar *in, *out, outbuf[256], *ch; + gsize inlen, outlen; + gint status, count = 0; iconv_t cd; if (!charset) @@ -375,20 +260,20 @@ best_encoding (GByteArray *buf, const char *charset) if (cd == (iconv_t) -1) return -1; - in = (char*)buf->data; + in = (gchar *) buf->data; inlen = buf->len; do { out = outbuf; outlen = sizeof (outbuf); - status = e_iconv (cd, (const char **) &in, &inlen, &out, &outlen); + status = e_iconv (cd, (const gchar **) &in, &inlen, &out, &outlen); for (ch = out - 1; ch >= outbuf; ch--) { - if ((unsigned char)*ch > 127) + if ((guchar) *ch > 127) count++; } - } while (status == (size_t) -1 && errno == E2BIG); + } while (status == (gsize) -1 && errno == E2BIG); e_iconv_close (cd); - if (status == (size_t) -1 || status > 0) + if (status == (gsize) -1 || status > 0) return -1; if (count == 0) @@ -399,37 +284,10 @@ best_encoding (GByteArray *buf, const char *charset) return CAMEL_TRANSFER_ENCODING_BASE64; } -static char * -composer_get_default_charset_setting (void) -{ - GConfClient *gconf; - const char *locale; - char *charset; - - gconf = gconf_client_get_default (); - charset = gconf_client_get_string (gconf, "/apps/evolution/mail/composer/charset", NULL); - - if (!charset || charset[0] == '\0') { - g_free (charset); - charset = gconf_client_get_string (gconf, "/apps/evolution/mail/format/charset", NULL); - if (charset && charset[0] == '\0') { - g_free (charset); - charset = NULL; - } - } - - g_object_unref (gconf); - - if (!charset && (locale = e_iconv_locale_charset ())) - charset = g_strdup (locale); - - return charset ? charset : g_strdup ("us-ascii"); -} - -static char * -best_charset (GByteArray *buf, const char *default_charset, CamelTransferEncoding *encoding) +static gchar * +best_charset (GByteArray *buf, const gchar *default_charset, CamelTransferEncoding *encoding) { - char *charset; + gchar *charset; /* First try US-ASCII */ *encoding = best_encoding (buf, "US-ASCII"); @@ -442,7 +300,7 @@ best_charset (GByteArray *buf, const char *default_charset, CamelTransferEncodin return g_strdup (default_charset); /* Now try the user's default charset from the mail config */ - charset = composer_get_default_charset_setting (); + charset = e_composer_get_default_charset (); *encoding = best_encoding (buf, charset); if (*encoding != -1) return charset; @@ -496,20 +354,191 @@ add_inlined_images (EMsgComposer *composer, CamelMultipart *multipart) g_hash_table_destroy (added); } -/* This functions builds a CamelMimeMessage for the message that the user has +/* These functions builds a CamelMimeMessage for the message that the user has * composed in `composer'. */ + +static void +set_recipients_from_destv (CamelMimeMessage *msg, + EDestination **to_destv, + EDestination **cc_destv, + EDestination **bcc_destv, + gboolean redirect) +{ + CamelInternetAddress *to_addr; + CamelInternetAddress *cc_addr; + CamelInternetAddress *bcc_addr; + CamelInternetAddress *target; + const gchar *text_addr, *header; + gboolean seen_hidden_list = FALSE; + gint i; + + to_addr = camel_internet_address_new (); + cc_addr = camel_internet_address_new (); + bcc_addr = camel_internet_address_new (); + + for (i = 0; to_destv != NULL && to_destv[i] != NULL; ++i) { + text_addr = e_destination_get_address (to_destv[i]); + + if (text_addr && *text_addr) { + target = to_addr; + if (e_destination_is_evolution_list (to_destv[i]) + && !e_destination_list_show_addresses (to_destv[i])) { + target = bcc_addr; + seen_hidden_list = TRUE; + } + + camel_address_decode (CAMEL_ADDRESS (target), text_addr); + } + } + + for (i = 0; cc_destv != NULL && cc_destv[i] != NULL; ++i) { + text_addr = e_destination_get_address (cc_destv[i]); + if (text_addr && *text_addr) { + target = cc_addr; + if (e_destination_is_evolution_list (cc_destv[i]) + && !e_destination_list_show_addresses (cc_destv[i])) { + target = bcc_addr; + seen_hidden_list = TRUE; + } + + camel_address_decode (CAMEL_ADDRESS (target), text_addr); + } + } + + for (i = 0; bcc_destv != NULL && bcc_destv[i] != NULL; ++i) { + text_addr = e_destination_get_address (bcc_destv[i]); + if (text_addr && *text_addr) { + camel_address_decode (CAMEL_ADDRESS (bcc_addr), text_addr); + } + } + + header = redirect ? CAMEL_RECIPIENT_TYPE_RESENT_TO : CAMEL_RECIPIENT_TYPE_TO; + if (camel_address_length (CAMEL_ADDRESS (to_addr)) > 0) { + camel_mime_message_set_recipients (msg, header, to_addr); + } else if (seen_hidden_list) { + camel_medium_set_header (CAMEL_MEDIUM (msg), header, "Undisclosed-Recipient:;"); + } + + header = redirect ? CAMEL_RECIPIENT_TYPE_RESENT_CC : CAMEL_RECIPIENT_TYPE_CC; + if (camel_address_length (CAMEL_ADDRESS (cc_addr)) > 0) { + camel_mime_message_set_recipients (msg, header, cc_addr); + } + + header = redirect ? CAMEL_RECIPIENT_TYPE_RESENT_BCC : CAMEL_RECIPIENT_TYPE_BCC; + if (camel_address_length (CAMEL_ADDRESS (bcc_addr)) > 0) { + camel_mime_message_set_recipients (msg, header, bcc_addr); + } + + camel_object_unref (to_addr); + camel_object_unref (cc_addr); + camel_object_unref (bcc_addr); +} + +static void +build_message_headers (EMsgComposer *composer, + CamelMimeMessage *msg, + gboolean redirect) +{ + EComposerHeaderTable *table; + EAccount *account; + const gchar *subject; + const gchar *reply_to; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg)); + + table = e_msg_composer_get_header_table (composer); + + /* Subject: */ + subject = e_composer_header_table_get_subject (table); + camel_mime_message_set_subject (msg, subject); + + /* From: / Resent-From: */ + account = e_composer_header_table_get_account (table); + if (account != NULL) { + CamelInternetAddress *addr; + const gchar *name = account->id->name; + const gchar *address = account->id->address; + + addr = camel_internet_address_new (); + camel_internet_address_add (addr, name, address); + + if (redirect) { + gchar *value; + + value = camel_address_encode (CAMEL_ADDRESS (addr)); + camel_medium_set_header ( + CAMEL_MEDIUM (msg), "Resent-From", value); + g_free (value); + } else + camel_mime_message_set_from (msg, addr); + + camel_object_unref (addr); + } + + /* Reply-To: */ + reply_to = e_composer_header_table_get_reply_to (table); + if (reply_to != NULL && *reply_to != '\0') { + CamelInternetAddress *addr; + + addr = camel_internet_address_new (); + + if (camel_address_unformat (CAMEL_ADDRESS (addr), reply_to) > 0) + camel_mime_message_set_reply_to (msg, addr); + + camel_object_unref (addr); + } + + /* To:, Cc:, Bcc: */ + if (e_composer_header_table_get_header_visible (table, E_COMPOSER_HEADER_TO) || + e_composer_header_table_get_header_visible (table, E_COMPOSER_HEADER_CC) || + e_composer_header_table_get_header_visible (table, E_COMPOSER_HEADER_BCC)) { + EDestination **to, **cc, **bcc; + + to = e_composer_header_table_get_destinations_to (table); + cc = e_composer_header_table_get_destinations_cc (table); + bcc = e_composer_header_table_get_destinations_bcc (table); + + set_recipients_from_destv (msg, to, cc, bcc, redirect); + + e_destination_freev (to); + e_destination_freev (cc); + e_destination_freev (bcc); + } + + /* X-Evolution-PostTo: */ + if (e_composer_header_table_get_header_visible (table, E_COMPOSER_HEADER_POST_TO)) { + CamelMedium *medium = CAMEL_MEDIUM (msg); + const gchar *name = "X-Evolution-PostTo"; + GList *list, *iter; + + camel_medium_remove_header (medium, name); + + list = e_composer_header_table_get_post_to (table); + for (iter = list; iter != NULL; iter = iter->next) { + gchar *folder = iter->data; + camel_medium_add_header (medium, name, folder); + g_free (folder); + } + g_list_free (list); + } +} + static CamelMimeMessage * -build_message (EMsgComposer *composer, gboolean save_html_object_data) +build_message (EMsgComposer *composer, + gboolean html_content, + gboolean save_html_object_data) { + GtkhtmlEditor *editor; EMsgComposerPrivate *p = composer->priv; - EAttachmentBar *attachment_bar = - E_ATTACHMENT_BAR (p->attachment_bar); - EMsgComposerHdrs *hdrs = E_MSG_COMPOSER_HDRS (p->hdrs); + EAttachmentBar *attachment_bar; + EComposerHeaderTable *table; + GtkToggleAction *action; CamelDataWrapper *plain, *html, *current; CamelTransferEncoding plain_encoding; - const char *iconv_charset = NULL; + const gchar *iconv_charset = NULL; GPtrArray *recipients = NULL; CamelMultipart *body = NULL; CamelContentType *type; @@ -519,23 +548,29 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) CamelException ex; GByteArray *data; EAccount *account; - char *charset; - int i; + gchar *charset; + gboolean pgp_sign; + gboolean pgp_encrypt; + gboolean smime_sign; + gboolean smime_encrypt; + gint i; - account = e_msg_composer_hdrs_get_from_account (hdrs); + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - if (p->persist_stream_interface == CORBA_OBJECT_NIL) - return NULL; + editor = GTKHTML_EDITOR (composer); + table = e_msg_composer_get_header_table (composer); + account = e_composer_header_table_get_account (table); + attachment_bar = E_ATTACHMENT_BAR (p->attachment_bar); /* evil kludgy hack for Redirect */ if (p->redirect) { - e_msg_composer_hdrs_to_redirect (hdrs, p->redirect); + build_message_headers (composer, p->redirect, TRUE); camel_object_ref (p->redirect); return p->redirect; } new = camel_mime_message_new (); - e_msg_composer_hdrs_to_message (hdrs, new); + build_message_headers (composer, new, FALSE); for (i = 0; i < p->extra_hdr_names->len; i++) { camel_medium_add_header (CAMEL_MEDIUM (new), p->extra_hdr_names->pdata[i], @@ -543,22 +578,27 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) } /* Message Disposition Notification */ - if (p->request_receipt) { - char *mdn_address = account->id->reply_to; + action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT)); + if (gtk_toggle_action_get_active (action)) { + gchar *mdn_address = account->id->reply_to; if (!mdn_address || !*mdn_address) mdn_address = account->id->address; - camel_medium_add_header (CAMEL_MEDIUM (new), "Disposition-Notification-To", mdn_address); + camel_medium_add_header ( + CAMEL_MEDIUM (new), + "Disposition-Notification-To", mdn_address); } /* Message Priority */ - if (p->set_priority) - camel_medium_add_header (CAMEL_MEDIUM (new), "X-Priority", "1"); + action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE)); + if (gtk_toggle_action_get_active (action)) + camel_medium_add_header ( + CAMEL_MEDIUM (new), "X-Priority", "1"); if (p->mime_body) { plain_encoding = CAMEL_TRANSFER_ENCODING_7BIT; for (i = 0; p->mime_body[i]; i++) { - if ((unsigned char) p->mime_body[i] > 127) { + if ((guchar) p->mime_body[i] > 127) { plain_encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE; break; } @@ -567,12 +607,13 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) g_byte_array_append (data, (const guint8 *)p->mime_body, strlen (p->mime_body)); type = camel_content_type_decode (p->mime_type); } else { - data = get_text (p->persist_stream_interface, "text/plain"); - if (!data) { - /* The component has probably died */ - camel_object_unref (CAMEL_OBJECT (new)); - return NULL; - } + gchar *text; + gsize length; + + data = g_byte_array_new (); + text = gtkhtml_editor_get_text_plain (editor, &length); + g_byte_array_append (data, (guint8 *) text, (guint) length); + g_free (text); /* FIXME: we may want to do better than this... */ @@ -608,26 +649,22 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) camel_data_wrapper_set_mime_type_field (plain, type); camel_content_type_unref (type); - if (p->send_html) { - CORBA_Environment ev; + if (html_content) { + gchar *text; + gsize length; + clear_current_images (composer); - if (save_html_object_data) { - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "save-data-on", &ev); - } - data = get_text (p->persist_stream_interface, "text/html"); - if (save_html_object_data) { - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "save-data-off", &ev); - CORBA_exception_free (&ev); - } + if (save_html_object_data) + gtkhtml_editor_run_command (editor, "save-data-on"); - if (!data) { - /* The component has probably died */ - camel_object_unref (new); - camel_object_unref (plain); - return NULL; - } + data = g_byte_array_new (); + text = gtkhtml_editor_get_text_html (editor, &length); + g_byte_array_append (data, (guint8 *) text, (guint) length); + g_free (text); + + if (save_html_object_data) + gtkhtml_editor_run_command (editor, "save-data-off"); html = camel_data_wrapper_new (); @@ -716,31 +753,45 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) camel_exception_init (&ex); - /* Setup working recipient list if we're encrypting */ - if (p->pgp_encrypt + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + pgp_sign = gtk_toggle_action_get_active (action); + + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + pgp_encrypt = gtk_toggle_action_get_active (action); + #if defined (HAVE_NSS) && defined (SMIME_SUPPORTED) - || p->smime_encrypt + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + smime_sign = gtk_toggle_action_get_active (action); + + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + smime_encrypt = gtk_toggle_action_get_active (action); +#else + smime_sign = FALSE; + smime_encrypt = FALSE; #endif - ) { - int j; - const char *types[] = { CAMEL_RECIPIENT_TYPE_TO, CAMEL_RECIPIENT_TYPE_CC, CAMEL_RECIPIENT_TYPE_BCC }; - recipients = g_ptr_array_new(); - for (i=0; i < sizeof(types)/sizeof(types[0]); i++) { + /* Setup working recipient list if we're encrypting */ + if (pgp_encrypt || smime_encrypt) { + gint j; + const gchar *types[] = { CAMEL_RECIPIENT_TYPE_TO, CAMEL_RECIPIENT_TYPE_CC, CAMEL_RECIPIENT_TYPE_BCC }; + + recipients = g_ptr_array_new (); + for (i = 0; i < G_N_ELEMENTS (types); i++) { const CamelInternetAddress *addr; - const char *address; + const gchar *address; - addr = camel_mime_message_get_recipients(new, types[i]); - for (j=0;camel_internet_address_get(addr, j, NULL, &address); j++) - g_ptr_array_add(recipients, g_strdup (address)); + addr = camel_mime_message_get_recipients (new, types[i]); + for (j=0;camel_internet_address_get (addr, j, NULL, &address); j++) + g_ptr_array_add (recipients, g_strdup (address)); } } - if (p->pgp_sign || p->pgp_encrypt) { - const char *pgp_userid; + if (pgp_sign || pgp_encrypt) { + const gchar *pgp_userid; CamelInternetAddress *from = NULL; CamelCipherContext *cipher; + EAccount *account; part = camel_mime_part_new (); camel_medium_set_content_object (CAMEL_MEDIUM (part), current); @@ -748,45 +799,47 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) camel_mime_part_set_encoding (part, plain_encoding); camel_object_unref (current); + account = e_composer_header_table_get_account (table); + if (account && account->pgp_key && *account->pgp_key) { pgp_userid = account->pgp_key; } else { - from = e_msg_composer_hdrs_get_from(hdrs); - camel_internet_address_get(from, 0, NULL, &pgp_userid); + from = e_msg_composer_get_from (composer); + camel_internet_address_get (from, 0, NULL, &pgp_userid); } - if (p->pgp_sign) { - CamelMimePart *npart = camel_mime_part_new(); + if (pgp_sign) { + CamelMimePart *npart = camel_mime_part_new (); - cipher = mail_crypto_get_pgp_cipher_context(account); - camel_cipher_sign(cipher, pgp_userid, CAMEL_CIPHER_HASH_SHA1, part, npart, &ex); - camel_object_unref(cipher); + cipher = mail_crypto_get_pgp_cipher_context (account); + camel_cipher_sign (cipher, pgp_userid, CAMEL_CIPHER_HASH_SHA1, part, npart, &ex); + camel_object_unref (cipher); - if (camel_exception_is_set(&ex)) { - camel_object_unref(npart); + if (camel_exception_is_set (&ex)) { + camel_object_unref (npart); goto exception; } - camel_object_unref(part); + camel_object_unref (part); part = npart; } - if (p->pgp_encrypt) { - CamelMimePart *npart = camel_mime_part_new(); + if (pgp_encrypt) { + CamelMimePart *npart = camel_mime_part_new (); /* check to see if we should encrypt to self, NB gets removed immediately after use */ if (account && account->pgp_encrypt_to_self && pgp_userid) g_ptr_array_add (recipients, g_strdup (pgp_userid)); cipher = mail_crypto_get_pgp_cipher_context (account); - camel_cipher_encrypt(cipher, pgp_userid, recipients, part, npart, &ex); + camel_cipher_encrypt (cipher, pgp_userid, recipients, part, npart, &ex); camel_object_unref (cipher); if (account && account->pgp_encrypt_to_self && pgp_userid) - g_ptr_array_set_size(recipients, recipients->len - 1); + g_ptr_array_set_size (recipients, recipients->len - 1); if (camel_exception_is_set (&ex)) { - camel_object_unref(npart); + camel_object_unref (npart); goto exception; } @@ -803,84 +856,84 @@ build_message (EMsgComposer *composer, gboolean save_html_object_data) } #if defined (HAVE_NSS) && defined (SMIME_SUPPORTED) - if (p->smime_sign || p->smime_encrypt) { + if (smime_sign || smime_encrypt) { CamelInternetAddress *from = NULL; CamelCipherContext *cipher; - part = camel_mime_part_new(); - camel_medium_set_content_object((CamelMedium *)part, current); + part = camel_mime_part_new (); + camel_medium_set_content_object ((CamelMedium *)part, current); if (current == plain) - camel_mime_part_set_encoding(part, plain_encoding); - camel_object_unref(current); + camel_mime_part_set_encoding (part, plain_encoding); + camel_object_unref (current); - if (p->smime_sign + if (smime_sign && (account == NULL || account->smime_sign_key == NULL || account->smime_sign_key[0] == 0)) { camel_exception_set (&ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot sign outgoing message: No signing certificate set for this account")); goto exception; } - if (p->smime_encrypt + if (smime_encrypt && (account == NULL || account->smime_sign_key == NULL || account->smime_sign_key[0] == 0)) { camel_exception_set (&ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot encrypt outgoing message: No encryption certificate set for this account")); goto exception; } - if (p->smime_sign) { - CamelMimePart *npart = camel_mime_part_new(); + if (smime_sign) { + CamelMimePart *npart = camel_mime_part_new (); - cipher = camel_smime_context_new(session); + cipher = camel_smime_context_new (session); /* if we're also encrypting, envelope-sign rather than clear-sign */ - if (p->smime_encrypt) { - camel_smime_context_set_sign_mode((CamelSMIMEContext *)cipher, CAMEL_SMIME_SIGN_ENVELOPED); - camel_smime_context_set_encrypt_key((CamelSMIMEContext *)cipher, TRUE, account->smime_encrypt_key); + if (smime_encrypt) { + camel_smime_context_set_sign_mode ((CamelSMIMEContext *)cipher, CAMEL_SMIME_SIGN_ENVELOPED); + camel_smime_context_set_encrypt_key ((CamelSMIMEContext *)cipher, TRUE, account->smime_encrypt_key); } else if (account && account->smime_encrypt_key && *account->smime_encrypt_key) { - camel_smime_context_set_encrypt_key((CamelSMIMEContext *)cipher, TRUE, account->smime_encrypt_key); + camel_smime_context_set_encrypt_key ((CamelSMIMEContext *)cipher, TRUE, account->smime_encrypt_key); } - camel_cipher_sign(cipher, account->smime_sign_key, CAMEL_CIPHER_HASH_SHA1, part, npart, &ex); - camel_object_unref(cipher); + camel_cipher_sign (cipher, account->smime_sign_key, CAMEL_CIPHER_HASH_SHA1, part, npart, &ex); + camel_object_unref (cipher); - if (camel_exception_is_set(&ex)) { - camel_object_unref(npart); + if (camel_exception_is_set (&ex)) { + camel_object_unref (npart); goto exception; } - camel_object_unref(part); + camel_object_unref (part); part = npart; } - if (p->smime_encrypt) { + if (smime_encrypt) { /* check to see if we should encrypt to self, NB removed after use */ if (account->smime_encrypt_to_self) - g_ptr_array_add(recipients, g_strdup (account->smime_encrypt_key)); + g_ptr_array_add (recipients, g_strdup (account->smime_encrypt_key)); - cipher = camel_smime_context_new(session); - camel_smime_context_set_encrypt_key((CamelSMIMEContext *)cipher, TRUE, account->smime_encrypt_key); + cipher = camel_smime_context_new (session); + camel_smime_context_set_encrypt_key ((CamelSMIMEContext *)cipher, TRUE, account->smime_encrypt_key); - camel_cipher_encrypt(cipher, NULL, recipients, part, (CamelMimePart *)new, &ex); - camel_object_unref(cipher); + camel_cipher_encrypt (cipher, NULL, recipients, part, (CamelMimePart *)new, &ex); + camel_object_unref (cipher); - if (camel_exception_is_set(&ex)) + if (camel_exception_is_set (&ex)) goto exception; if (account->smime_encrypt_to_self) - g_ptr_array_set_size(recipients, recipients->len - 1); + g_ptr_array_set_size (recipients, recipients->len - 1); } if (from) - camel_object_unref(from); + camel_object_unref (from); /* we replaced the message directly, we don't want to do reparenting foo */ - if (p->smime_encrypt) { - camel_object_unref(part); + if (smime_encrypt) { + camel_object_unref (part); goto skip_content; } else { - current = camel_medium_get_content_object((CamelMedium *)part); - camel_object_ref(current); - camel_object_unref(part); + current = camel_medium_get_content_object ((CamelMedium *)part); + camel_object_ref (current); + camel_object_unref (part); } } #endif /* HAVE_NSS */ @@ -895,13 +948,14 @@ skip_content: #endif if (recipients) { for (i=0; ilen; i++) - g_free(recipients->pdata[i]); - g_ptr_array_free(recipients, TRUE); + g_free (recipients->pdata[i]); + g_ptr_array_free (recipients, TRUE); } /* Attach whether this message was written in HTML */ - camel_medium_set_header (CAMEL_MEDIUM (new), "X-Evolution-Format", - p->send_html ? "text/html" : "text/plain"); + camel_medium_set_header ( + CAMEL_MEDIUM (new), "X-Evolution-Format", + html_content ? "text/html" : "text/plain"); return new; @@ -913,39 +967,145 @@ skip_content: camel_object_unref (new); if (ex.id != CAMEL_EXCEPTION_USER_CANCEL) { - e_error_run((GtkWindow *)composer, "mail-composer:no-build-message", - camel_exception_get_description(&ex), NULL); + e_error_run ((GtkWindow *)composer, "mail-composer:no-build-message", + camel_exception_get_description (&ex), NULL); } camel_exception_clear (&ex); if (recipients) { for (i=0; ilen; i++) - g_free(recipients->pdata[i]); - g_ptr_array_free(recipients, TRUE); + g_free (recipients->pdata[i]); + g_ptr_array_free (recipients, TRUE); } return NULL; } +/* Attachment Bar */ + +static void +emcab_add (EPopup *ep, EPopupItem *item, gpointer data) +{ + GtkWidget *widget = data; + GtkWidget *composer; + + composer = gtk_widget_get_toplevel (widget); + gtk_action_activate (ACTION (ATTACH)); +} + +static void +emcab_properties (EPopup *ep, EPopupItem *item, gpointer data) +{ + EAttachmentBar *attachment_bar = data; + + e_attachment_bar_edit_selected (attachment_bar); +} + +static void +emcab_remove (EPopup *ep, EPopupItem *item, gpointer data) +{ + EAttachmentBar *attachment_bar = data; + + e_attachment_bar_remove_selected (attachment_bar); +} + +static void +emcab_popup_position (GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data) +{ + GtkWidget *widget = user_data; + GnomeIconList *icon_list = user_data; + GList *selection; + GnomeCanvasPixbuf *image; + + gdk_window_get_origin (widget->window, x, y); + + selection = gnome_icon_list_get_selection (icon_list); + if (selection == NULL) + return; + + image = gnome_icon_list_get_icon_pixbuf_item ( + icon_list, GPOINTER_TO_INT(selection->data)); + if (image == NULL) + return; + + /* Put menu to the center of icon. */ + *x += (int)(image->item.x1 + image->item.x2) / 2; + *y += (int)(image->item.y1 + image->item.y2) / 2; +} + +static void +emcab_popups_free (EPopup *ep, GSList *list, gpointer data) +{ + g_slist_free (list); +} + +/* Popup menu handling. */ +static EPopupItem emcab_popups[] = { + { E_POPUP_ITEM, "10.attach", N_("_Remove"), emcab_remove, NULL, GTK_STOCK_REMOVE, EM_POPUP_ATTACHMENTS_MANY }, + { E_POPUP_ITEM, "20.attach", N_("_Properties"), emcab_properties, NULL, GTK_STOCK_PROPERTIES, EM_POPUP_ATTACHMENTS_ONE }, + { E_POPUP_BAR, "30.attach.00", NULL, NULL, NULL, NULL, EM_POPUP_ATTACHMENTS_MANY|EM_POPUP_ATTACHMENTS_ONE }, + { E_POPUP_ITEM, "30.attach.01", N_("_Add attachment..."), emcab_add, NULL, GTK_STOCK_ADD, 0 }, +}; + +/* if id != -1, then use it as an index for target of the popup */ + +static void +emcab_popup (EAttachmentBar *bar, GdkEventButton *event, int id) +{ + GSList *attachments = NULL, *menus = NULL; + int i; + EMPopup *emp; + EMPopupTargetAttachments *t; + GtkMenu *menu; + + attachments = e_attachment_bar_get_attachment (bar, id); + + for (i=0;itarget.widget = (GtkWidget *)bar; + menu = e_popup_create_menu_once ((EPopup *)emp, (EPopupTarget *)t, 0); + + if (event == NULL) + gtk_menu_popup (menu, NULL, NULL, emcab_popup_position, bar, 0, gtk_get_current_event_time ()); + else + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button, event->time); +} + +/* Signatures */ -static char * -get_file_content (EMsgComposer *composer, const char *file_name, gboolean want_html, guint flags, gboolean warn) +static gchar * +get_file_content (EMsgComposer *composer, + const gchar *filename, + gboolean want_html, + guint flags, + gboolean warn) { CamelStreamFilter *filtered_stream; CamelStreamMem *memstream; CamelMimeFilter *html, *charenc; CamelStream *stream; GByteArray *buffer; - char *charset; - char *content; - int fd; + gchar *charset; + gchar *content; + gint fd; - fd = g_open (file_name, O_RDONLY, 0); + fd = g_open (filename, O_RDONLY, 0); if (fd == -1) { if (warn) - e_error_run((GtkWindow *)composer, "mail-composer:no-sig-file", - file_name, g_strerror(errno), NULL); + e_error_run ((GtkWindow *)composer, "mail-composer:no-sig-file", + filename, g_strerror (errno), NULL); return g_strdup (""); } @@ -983,7 +1143,7 @@ get_file_content (EMsgComposer *composer, const char *file_name, gboolean want_h camel_object_unref (stream); charset = composer && composer->priv->charset ? composer->priv->charset : NULL; - charset = charset ? g_strdup (charset) : composer_get_default_charset_setting (); + charset = charset ? g_strdup (charset) : e_composer_get_default_charset (); if ((charenc = (CamelMimeFilter *) camel_mime_filter_charset_new_convert (charset, "UTF-8"))) { camel_stream_filter_add (filtered_stream, charenc); camel_object_unref (charenc); @@ -1007,8 +1167,8 @@ get_file_content (EMsgComposer *composer, const char *file_name, gboolean want_h return content; } -char * -e_msg_composer_get_sig_file_content (const char *sigfile, gboolean in_html) +gchar * +e_msg_composer_get_sig_file_content (const gchar *sigfile, gboolean in_html) { if (!sigfile || !*sigfile) { return NULL; @@ -1022,147 +1182,22 @@ e_msg_composer_get_sig_file_content (const char *sigfile, gboolean in_html) FALSE); } -static void -prepare_engine (EMsgComposer *composer) +static gchar * +encode_signature_name (const gchar *name) { - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - /* printf ("prepare_engine\n"); */ - - CORBA_exception_init (&ev); - p->eeditor_engine = (GNOME_GtkHTML_Editor_Engine) Bonobo_Unknown_queryInterface - (bonobo_widget_get_objref (BONOBO_WIDGET (p->eeditor)), "IDL:GNOME/GtkHTML/Editor/Engine:1.0", &ev); - if ((p->eeditor_engine != CORBA_OBJECT_NIL) && (ev._major == CORBA_NO_EXCEPTION)) { - - /* printf ("trying set listener\n"); */ - p->eeditor_listener = BONOBO_OBJECT (listener_new (composer)); - if (p->eeditor_listener != NULL) - GNOME_GtkHTML_Editor_Engine__set_listener (p->eeditor_engine, - (GNOME_GtkHTML_Editor_Listener) - bonobo_object_dup_ref - (bonobo_object_corba_objref (p->eeditor_listener), - &ev), - &ev); + const gchar *s; + gchar *ename, *e; + gint len = 0; - if ((ev._major != CORBA_NO_EXCEPTION) || (p->eeditor_listener == NULL)) { - CORBA_Environment err_ev; + s = name; + while (*s) { + len ++; + if (*s == '"' || *s == '.' || *s == '=') + len ++; + s ++; + } - CORBA_exception_init (&err_ev); - - Bonobo_Unknown_unref (p->eeditor_engine, &err_ev); - CORBA_Object_release (p->eeditor_engine, &err_ev); - - CORBA_exception_free (&err_ev); - - p->eeditor_engine = CORBA_OBJECT_NIL; - g_warning ("Can't establish Editor Listener\n"); - } else { - gchar *path; - GConfClient *gconf = gconf_client_get_default (); - - path = gconf_client_get_string (gconf, COMPOSER_CURRENT_FOLDER_KEY, NULL); - g_object_unref (gconf); - - /* change it only if we have set path before */ - if (path && *path) - e_msg_composer_set_attach_path (composer, path); - g_free (path); - } - } else { - p->eeditor_engine = CORBA_OBJECT_NIL; - g_warning ("Can't get Editor Engine\n"); - } - - CORBA_exception_free (&ev); -} - -/** - * e_msg_composer_set_attach_path - * Attach path is used to be preset when choosing files. This function ensures same path - * in editor and in composer. - * @param composer Composer. - * @param path Path to be used. Should not be NULL. - **/ -void -e_msg_composer_set_attach_path (EMsgComposer *composer, const gchar *path) -{ - GConfClient *gconf; - GError *error = NULL; - - g_return_if_fail (composer != NULL); - g_return_if_fail (path != NULL); - - gconf = gconf_client_get_default (); - gconf_client_set_string (gconf, COMPOSER_CURRENT_FOLDER_KEY, path, &error); - g_object_unref (gconf); - - if (error) { - g_warning ("Could not write current_folder setting: %s", error->message); - g_error_free (error); - } - - if (composer->priv->eeditor_engine) { - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - GNOME_GtkHTML_Editor_Engine_setFilePath (composer->priv->eeditor_engine, path, &ev); - - CORBA_exception_free (&ev); - } - - /* do this as last thing here, so we can do e_msg_composer_set_attach_path (composer, e_msg_composer_get_attach_path (composer)) */ - g_object_set_data_full ((GObject *) composer, "attach_path", g_strdup (path), g_free); -} - -/** - * e_msg_composer_get_attach_path - * Last path, if any, used to select file. - * @param composer Composer. - * @return Last used path, or NULL when not set yet. - **/ -const gchar * -e_msg_composer_get_attach_path (EMsgComposer *composer) -{ - g_return_val_if_fail (composer != NULL, g_object_get_data ((GObject *) composer, "attach_path")); - - if (composer->priv->eeditor_engine) { - char *str; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - str = GNOME_GtkHTML_Editor_Engine_getFilePath (composer->priv->eeditor_engine, &ev); - if (ev._major == CORBA_NO_EXCEPTION && str) - e_msg_composer_set_attach_path (composer, str); - if (str) - CORBA_free (str); - - CORBA_exception_free (&ev); - } - - return g_object_get_data ((GObject *) composer, "attach_path"); -} - -static char * -encode_signature_name (const char *name) -{ - const char *s; - char *ename, *e; - int len = 0; - - s = name; - while (*s) { - len ++; - if (*s == '"' || *s == '.' || *s == '=') - len ++; - s ++; - } - - ename = g_new (gchar, len + 1); + ename = g_new (gchar, len + 1); s = name; e = ename; @@ -1192,12 +1227,12 @@ encode_signature_name (const char *name) return ename; } -static char * -decode_signature_name (const char *name) +static gchar * +decode_signature_name (const gchar *name) { - const char *s; - char *dname, *d; - int len = 0; + const gchar *s; + gchar *dname, *d; + gint len = 0; s = name; while (*s) { @@ -1239,16 +1274,16 @@ decode_signature_name (const char *name) #define CONVERT_SPACES CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES -static char * +static gchar * get_signature_html (EMsgComposer *composer) { - EMsgComposerHdrs *hdrs; - char *text = NULL, *html = NULL; + EComposerHeaderTable *table; + gchar *text = NULL, *html = NULL; ESignature *signature; gboolean format_html; - hdrs = E_MSG_COMPOSER_HDRS (composer->priv->hdrs); - signature = e_msg_composer_hdrs_get_signature (hdrs); + table = e_msg_composer_get_header_table (composer); + signature = e_composer_header_table_get_signature (table); if (!signature) return NULL; @@ -1266,11 +1301,11 @@ get_signature_html (EMsgComposer *composer) } } else { EAccountIdentity *id; - char *organization; - char *address; - char *name; + gchar *organization; + gchar *address; + gchar *name; - id = e_msg_composer_hdrs_get_from_account (hdrs)->id; + id = e_composer_header_table_get_account (table)->id; address = id->address ? camel_text_to_html (id->address, CONVERT_SPACES, 0) : NULL; name = id->name ? camel_text_to_html (id->name, CONVERT_SPACES, 0) : NULL; organization = id->organization ? camel_text_to_html (id->organization, CONVERT_SPACES, 0) : NULL; @@ -1292,7 +1327,7 @@ get_signature_html (EMsgComposer *composer) /* printf ("text: %s\n", text); */ if (text) { - char *encoded_uid = NULL; + gchar *encoded_uid = NULL; if (signature) encoded_uid = encode_signature_name (signature->uid); @@ -1308,7 +1343,7 @@ get_signature_html (EMsgComposer *composer) "", encoded_uid ? encoded_uid : "", format_html ? "" : "
\n",
-					format_html || (!strncmp ("-- \n", text, 4) || strstr(text, "\n-- \n")) ? "" : "-- \n",
+					format_html || (!strncmp ("-- \n", text, 4) || strstr (text, "\n-- \n")) ? "" : "-- \n",
 					text,
 					format_html ? "" : "
\n"); g_free (text); @@ -1320,22 +1355,16 @@ get_signature_html (EMsgComposer *composer) } static void -set_editor_text(EMsgComposer *composer, const char *text, ssize_t len, int set_signature, int pad_signature) +set_editor_text (EMsgComposer *composer, + const gchar *text, + gboolean set_signature) { - EMsgComposerPrivate *p = composer->priv; - Bonobo_PersistStream persist; - BonoboStream *stream; - CORBA_Environment ev; - Bonobo_Unknown object; gboolean reply_signature_on_top; - char *body = NULL, *html = NULL; + gchar *body = NULL, *html = NULL; GConfClient *gconf; - g_return_if_fail (p->persist_stream_interface != CORBA_OBJECT_NIL); - - persist = p->persist_stream_interface; - - CORBA_exception_init (&ev); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (text != NULL); gconf = gconf_client_get_default (); @@ -1353,12 +1382,12 @@ set_editor_text(EMsgComposer *composer, const char *text, ssize_t len, int set_s */ - reply_signature_on_top = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/top_signature", NULL); + reply_signature_on_top = gconf_client_get_bool (gconf, COMPOSER_GCONF_TOP_SIGNATURE_KEY, NULL); g_object_unref (gconf); if (set_signature && reply_signature_on_top) { - char *tmp = NULL; + gchar *tmp = NULL; tmp = get_signature_html (composer); if (tmp) { /* Minimizing the damage. Make it just a part of the body instead of a signature */ @@ -1379,26 +1408,10 @@ set_editor_text(EMsgComposer *composer, const char *text, ssize_t len, int set_s "
%s", text); } } else { - body = g_strdup(text); - } - - if (body) { - len = strlen (body); - } - - stream = bonobo_stream_mem_create (body, len, TRUE, FALSE); - object = bonobo_object_corba_objref (BONOBO_OBJECT (stream)); - Bonobo_PersistStream_load (persist, (Bonobo_Stream) object, "text/html", &ev); - if (ev._major != CORBA_NO_EXCEPTION) { - /* FIXME. Some error message. */ - bonobo_object_unref (BONOBO_OBJECT (stream)); - CORBA_exception_free (&ev); - return; + body = g_strdup (text); } - CORBA_exception_free (&ev); - - bonobo_object_unref (BONOBO_OBJECT (stream)); + gtkhtml_editor_set_text_html (GTKHTML_EDITOR (composer), body, -1); if (set_signature && !reply_signature_on_top) e_msg_composer_show_sig_file (composer); @@ -1406,168 +1419,8 @@ set_editor_text(EMsgComposer *composer, const char *text, ssize_t len, int set_s /* Commands. */ -static void -show_attachments (EMsgComposer *composer, - gboolean show) -{ - EMsgComposerPrivate *p = composer->priv; - - e_expander_set_expanded (E_EXPANDER (p->attachment_expander), show); - if (show) - gtk_label_set_text_with_mnemonic (GTK_LABEL (composer->priv->attachment_expander_label), - _("Hide _Attachment Bar")); - else - gtk_label_set_text_with_mnemonic (GTK_LABEL (composer->priv->attachment_expander_label), - _("Show _Attachment Bar")); -} - -static void -save (EMsgComposer *composer, const char *filename) -{ - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - int fd; - - /* check to see if we already have the file and that we can create it */ - if ((fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, 0777)) == -1) { - int resp, errnosav = errno; - struct stat st; - - if (g_stat (filename, &st) == 0 && S_ISREG (st.st_mode)) { - resp = e_error_run((GtkWindow *)composer, E_ERROR_ASK_FILE_EXISTS_OVERWRITE, filename, NULL); - if (resp != GTK_RESPONSE_OK) - return; - } else { - e_error_run((GtkWindow *)composer, E_ERROR_NO_SAVE_FILE, filename, g_strerror(errnosav)); - return; - } - } else - close (fd); - - CORBA_exception_init (&ev); - - Bonobo_PersistFile_save (p->persist_file_interface, filename, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - e_error_run((GtkWindow *)composer, E_ERROR_NO_SAVE_FILE, - filename, _("Unknown reason")); - } else { - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "saved", &ev); - e_msg_composer_unset_autosaved (composer); - } - CORBA_exception_free (&ev); -} - -static void -saveas_response(EMsgComposer *composer, const char *name) -{ - save(composer, name); -} - -static void -saveas(EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - - e_msg_composer_select_file (composer, &p->saveas, saveas_response, _("Save as..."), TRUE); -} - -static void -load (EMsgComposer *composer, const char *file_name) -{ - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - Bonobo_PersistFile_load (p->persist_file_interface, file_name, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) - e_error_run((GtkWindow *)composer, E_ERROR_NO_LOAD_FILE, - file_name, _("Unknown reason"), NULL); - - CORBA_exception_free (&ev); -} - -#define AUTOSAVE_SEED ".evolution-composer.autosave-XXXXXX" -#define AUTOSAVE_INTERVAL 60000 - -typedef struct _AutosaveManager AutosaveManager; -struct _AutosaveManager { - GHashTable *table; - guint id; - gboolean ask; -}; - -static AutosaveManager *am = NULL; -static void autosave_manager_start (AutosaveManager *am); -static void autosave_manager_stop (AutosaveManager *am); - -static gboolean -autosave_save_draft (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - CamelMimeMessage *message; - CamelStream *stream; - char *file; - int fd, camelfd; - gboolean success = TRUE; - - if (!e_msg_composer_is_dirty (composer)) - return TRUE; - - fd = p->autosave_fd; - file = p->autosave_file; - - if (fd == -1) { - /* This code is odd, the fd is opened elsewhere but a failure is ignored */ - e_error_run((GtkWindow *)composer, "mail-composer:no-autosave", - file, _("Could not open file"), NULL); - return FALSE; - } - - message = e_msg_composer_get_message_draft (composer); - - if (message == NULL) { - e_error_run((GtkWindow *)composer, "mail-composer:no-autosave", - file, _("Unable to retrieve message from editor"), NULL); - return FALSE; - } - - if (lseek (fd, (off_t)0, SEEK_SET) == -1 - || ftruncate (fd, (off_t)0) == -1 - || (camelfd = dup(fd)) == -1) { - camel_object_unref (message); - e_error_run((GtkWindow *)composer, "mail-composer:no-autosave", - file, g_strerror(errno), NULL); - return FALSE; - } - - /* this does an lseek so we don't have to */ - stream = camel_stream_fs_new_with_fd (camelfd); - if (camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream) == -1 - || camel_stream_close (CAMEL_STREAM (stream)) == -1) { - e_error_run((GtkWindow *)composer, "mail-composer:no-autosave", - file, g_strerror(errno), NULL); - success = FALSE; - } else { - CORBA_Environment ev; - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "saved", &ev); - CORBA_exception_free (&ev); - e_msg_composer_unset_changed (composer); - e_msg_composer_set_autosaved (composer); - } - - camel_object_unref (stream); - - camel_object_unref (message); - - return success; -} - static EMsgComposer * -autosave_load_draft (const char *filename) +autosave_load_draft (const gchar *filename) { CamelStream *stream; CamelMimeMessage *msg; @@ -1586,14 +1439,16 @@ autosave_load_draft (const char *filename) composer = e_msg_composer_new_with_message (msg); if (composer) { - if (autosave_save_draft (composer)) + if (e_composer_autosave_snapshot (composer)) g_unlink (filename); - g_signal_connect (GTK_OBJECT (composer), "send", - G_CALLBACK (em_utils_composer_send_cb), NULL); + g_signal_connect ( + composer, "send", + G_CALLBACK (em_utils_composer_send_cb), NULL); - g_signal_connect (GTK_OBJECT (composer), "save-draft", - G_CALLBACK (em_utils_composer_save_draft_cb), NULL); + g_signal_connect ( + composer, "save-draft", + G_CALLBACK (em_utils_composer_save_draft_cb), NULL); gtk_widget_show (GTK_WIDGET (composer)); } @@ -1601,4600 +1456,3076 @@ autosave_load_draft (const char *filename) return composer; } -static gboolean -autosave_is_owned (AutosaveManager *am, const char *file) -{ - return g_hash_table_lookup (am->table, file) != NULL; -} +/* Miscellaneous callbacks. */ -static void -autosave_manager_query_load_orphans (AutosaveManager *am, GtkWindow *parent) +static gint +attachment_bar_button_press_event_cb (EAttachmentBar *attachment_bar, + GdkEventButton *event) { - GDir *dir; - const char *dname; - GSList *match = NULL; - gint len = strlen (AUTOSAVE_SEED); - gint load = FALSE; - const gchar *dirname; - - dirname = e_get_user_data_dir (); - dir = g_dir_open (dirname, 0, NULL); - if (!dir) { - return; - } + GnomeIconList *icon_list; + gint icon_number; - while ((dname = g_dir_read_name (dir))) { - if ((!strncmp (dname, AUTOSAVE_SEED, len - 6)) - && (strlen (dname) == len) - && (!autosave_is_owned (am, dname))) { - gchar *filename; - struct stat st; - - filename = g_build_filename (dirname, dname, NULL); - - /* - * check if the file has any length, It is a valid case if it doesn't - * so we simply don't ask then. - */ - if (g_stat (filename, &st) == -1 || st.st_size == 0) { - g_unlink (filename); - g_free (filename); - continue; - } - match = g_slist_prepend (match, filename); - } - } - - g_dir_close (dir); - - if (match != NULL) - load = e_error_run(parent, "mail-composer:recover-autosave", NULL) == GTK_RESPONSE_YES; + if (event->button != 3) + return FALSE; - while (match != NULL) { - GSList *next = match->next; - char *filename = match->data; - EMsgComposer *composer; + icon_list = GNOME_ICON_LIST (attachment_bar); + icon_number = gnome_icon_list_get_icon_at ( + icon_list, event->x, event->y); + if (icon_number >= 0) { + gnome_icon_list_unselect_all (icon_list); + gnome_icon_list_select_icon (icon_list, icon_number); + } - if (load) { - /* FIXME: composer is never used */ - composer = autosave_load_draft (filename); - } else { - g_unlink (filename); - } + emcab_popup (attachment_bar, event, icon_number); - g_free (filename); - g_slist_free_1 (match); - match = next; - } + return TRUE; } static void -autosave_run_foreach_cb (gpointer key, gpointer value, gpointer data) +attachment_bar_changed_cb (EAttachmentBar *attachment_bar, + EMsgComposer *composer) { - EMsgComposer *composer = E_MSG_COMPOSER (value); - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + GtkWidget *widget; + guint attachment_num; - if (p->enable_autosave) - autosave_save_draft (composer); -} + editor = GTKHTML_EDITOR (composer); + attachment_num = e_attachment_bar_get_num_attachments (attachment_bar); -static gint -autosave_run (gpointer data) -{ - AutosaveManager *am = data; + if (attachment_num > 0) { + gchar *markup; - g_hash_table_foreach (am->table, (GHFunc)autosave_run_foreach_cb, am); + markup = g_strdup_printf ( + "%d %s", attachment_num, ngettext ( + "Attachment", "Attachments", attachment_num)); + widget = composer->priv->attachment_expander_num; + gtk_label_set_markup (GTK_LABEL (widget), markup); + g_free (markup); - autosave_manager_stop (am); - autosave_manager_start (am); + gtk_widget_show (composer->priv->attachment_expander_icon); - return FALSE; + widget = composer->priv->attachment_expander; + gtk_expander_set_expanded (GTK_EXPANDER (widget), TRUE); + } else { + widget = composer->priv->attachment_expander_num; + gtk_label_set_text (GTK_LABEL (widget), ""); + + gtk_widget_hide (composer->priv->attachment_expander_icon); + + widget = composer->priv->attachment_expander; + gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE); + } + + /* Mark the editor as changed so it prompts about unsaved + changes on close. */ + gtkhtml_editor_set_changed (editor, TRUE); } -static gboolean -autosave_init_file (EMsgComposer *composer) +static gint +attachment_bar_key_press_event_cb (EAttachmentBar *attachment_bar, + GdkEventKey *event) { - EMsgComposerPrivate *p = composer->priv; - if (p->autosave_file == NULL) { - p->autosave_file = g_build_filename ( - e_get_user_data_dir (), AUTOSAVE_SEED, NULL); - p->autosave_fd = g_mkstemp (p->autosave_file); + if (event->keyval == GDK_Delete) { + e_attachment_bar_remove_selected (attachment_bar); return TRUE; } + return FALSE; } -static void -autosave_manager_start (AutosaveManager *am) +static gboolean +attachment_bar_popup_menu_cb (EAttachmentBar *attachment_bar) { - if (am->id == 0) - am->id = g_timeout_add (AUTOSAVE_INTERVAL, autosave_run, am); -} + emcab_popup (attachment_bar, NULL, -1); -static void -autosave_manager_stop (AutosaveManager *am) -{ - if (am->id) { - g_source_remove (am->id); - am->id = 0; - } + return TRUE; } -static AutosaveManager * -autosave_manager_new (void) +static void +attachment_expander_notify_cb (GtkExpander *expander, + GParamSpec *pspec, + EMsgComposer *composer) { - AutosaveManager *am; - GHashTable *table; + GtkLabel *label; + const gchar *text; - table = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); + label = GTK_LABEL (composer->priv->attachment_expander_label); - am = g_new (AutosaveManager, 1); - am->table = table; - am->id = 0; - am->ask = TRUE; + /* Update the expander label */ + if (gtk_expander_get_expanded (expander)) + text = _("Hide _Attachment Bar"); + else + text = _("Show _Attachment Bar"); - return am; + gtk_label_set_text_with_mnemonic (label, text); } static void -autosave_manager_register (AutosaveManager *am, EMsgComposer *composer) +msg_composer_subject_changed_cb (EMsgComposer *composer) { - char *key; - EMsgComposerPrivate *p = composer->priv; + EComposerHeaderTable *table; + const gchar *subject; - g_return_if_fail (composer != NULL); + table = e_msg_composer_get_header_table (composer); + subject = e_composer_header_table_get_subject (table); - if (autosave_init_file (composer)) { - key = g_path_get_basename (p->autosave_file); - g_hash_table_insert (am->table, key, composer); - if (am->ask) { - /* keep recursion out of our bedrooms. */ - am->ask = FALSE; - autosave_manager_query_load_orphans (am, (GtkWindow *)composer); - am->ask = TRUE; - } - } - autosave_manager_start (am); + if (subject == NULL || *subject == '\0') + subject = _("Compose Message"); + + gtk_window_set_title (GTK_WINDOW (composer), subject); } +enum { + UPDATE_AUTO_CC, + UPDATE_AUTO_BCC, +}; + static void -autosave_manager_unregister (AutosaveManager *am, EMsgComposer *composer) +update_auto_recipients (EComposerHeaderTable *table, + gint mode, + const gchar *auto_addrs) { - EMsgComposerPrivate *p = composer->priv; - gchar *key; + EDestination *dest, **destv = NULL; + CamelInternetAddress *iaddr; + GList *list = NULL; + guint length; + gint i; - if (!p->autosave_file) - return; + if (auto_addrs) { + iaddr = camel_internet_address_new (); + if (camel_address_decode (CAMEL_ADDRESS (iaddr), auto_addrs) != -1) { + for (i = 0; i < camel_address_length (CAMEL_ADDRESS (iaddr)); i++) { + const gchar *name, *addr; - key = g_path_get_basename (p->autosave_file); - g_hash_table_remove (am->table, key); - g_free (key); + if (!camel_internet_address_get (iaddr, i, &name, &addr)) + continue; - /* only remove the file if we can successfully save it */ - /* FIXME this test could probably be more efficient */ - if (autosave_save_draft (composer)) { - /* Close before unlinking necessary on Win32 */ - close (p->autosave_fd); - g_unlink (p->autosave_file); - } else { - close (p->autosave_fd); - } - g_free (p->autosave_file); - p->autosave_file = NULL; + dest = e_destination_new (); + e_destination_set_auto_recipient (dest, TRUE); - if (g_hash_table_size (am->table) == 0) - autosave_manager_stop (am); -} + if (name) + e_destination_set_name (dest, name); -static void -menu_file_save_draft_cb (BonoboUIComponent *uic, void *data, const char *path) -{ - g_signal_emit (data, signals[SAVE_DRAFT], 0, FALSE); - e_msg_composer_unset_changed (E_MSG_COMPOSER (data)); - e_msg_composer_unset_autosaved (E_MSG_COMPOSER (data)); -} + if (addr) + e_destination_set_email (dest, addr); -/* Exit dialog. (Displays a "Save composition to 'Drafts' before exiting?" warning before actually exiting.) */ + list = g_list_prepend (list, dest); + } + } -static void -do_exit (EMsgComposer *composer) -{ - const char *subject; - int button; - EMsgComposerPrivate *p = composer->priv; + camel_object_unref (iaddr); + } - if (!e_msg_composer_is_dirty (composer) && !e_msg_composer_is_autosaved (composer)) { - gtk_widget_destroy (GTK_WIDGET (composer)); - return; + switch (mode) { + case UPDATE_AUTO_CC: + destv = e_composer_header_table_get_destinations_cc (table); + break; + case UPDATE_AUTO_BCC: + destv = e_composer_header_table_get_destinations_bcc (table); + break; + default: + g_return_if_reached (); + } + + if (destv) { + for (i = 0; destv[i]; i++) { + if (!e_destination_is_auto_recipient (destv[i])) { + dest = e_destination_copy (destv[i]); + list = g_list_prepend (list, dest); + } + } + + e_destination_freev (destv); } - gdk_window_raise (GTK_WIDGET (composer)->window); + list = g_list_reverse (list); - subject = e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (p->hdrs)); + length = g_list_length (list); + destv = destination_list_to_vector_sized (list, length); - button = e_error_run((GtkWindow *)composer, "mail-composer:exit-unsaved", - subject && subject[0] ? subject : _("Untitled Message"), NULL); + g_list_free (list); - switch (button) { - case GTK_RESPONSE_YES: - /* Save */ - g_signal_emit (GTK_OBJECT (composer), signals[SAVE_DRAFT], 0, TRUE); - e_msg_composer_unset_changed (composer); - e_msg_composer_unset_autosaved (composer); - gtk_widget_destroy (GTK_WIDGET (composer)); + switch (mode) { + case UPDATE_AUTO_CC: + e_composer_header_table_set_destinations_cc (table, destv); break; - case GTK_RESPONSE_NO: - /* Don't save */ - gtk_widget_destroy (GTK_WIDGET (composer)); - break; - case GTK_RESPONSE_CANCEL: + case UPDATE_AUTO_BCC: + e_composer_header_table_set_destinations_bcc (table, destv); break; + default: + g_return_if_reached (); } -} -/* Menu callbacks. */ -static void -file_open_response(EMsgComposer *composer, const char *name) -{ - load (composer, name); + e_destination_freev (destv); } static void -menu_file_open_cb (BonoboUIComponent *uic, - void *data, - const char *path) +msg_composer_account_changed_cb (EMsgComposer *composer) { - EMsgComposer *composer = E_MSG_COMPOSER(data); EMsgComposerPrivate *p = composer->priv; + EComposerHeaderTable *table; + GtkToggleAction *action; + ESignature *signature; + EAccount *account; + gboolean active; + const gchar *cc_addrs = NULL; + const gchar *bcc_addrs = NULL; + const gchar *uid; - /* NB: This function is never used anymore */ + table = e_msg_composer_get_header_table (composer); + account = e_composer_header_table_get_account (table); - e_msg_composer_select_file(composer, &p->load, file_open_response, _("Open File"), FALSE); -} + if (account == NULL) + goto exit; -static void -menu_file_save_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - EMsgComposer *composer = E_MSG_COMPOSER (data); - EMsgComposerPrivate *p = composer->priv; - CORBA_char *file_name; - CORBA_Environment ev; + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + active = account->pgp_always_sign && + (!account->pgp_no_imip_sign || p->mime_type == NULL || + g_ascii_strncasecmp (p->mime_type, "text/calendar", 13) != 0); + gtk_toggle_action_set_active (action, active); - CORBA_exception_init (&ev); + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + active = account->smime_sign_default; + gtk_toggle_action_set_active (action, active); - file_name = Bonobo_PersistFile_getCurrentFile (p->persist_file_interface, &ev); + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + active = account->smime_encrypt_default; + gtk_toggle_action_set_active (action, active); - if (ev._major != CORBA_NO_EXCEPTION) { - saveas (composer); - } else { - save (composer, file_name); - CORBA_free (file_name); - } - CORBA_exception_free (&ev); -} + if (account->always_cc) + cc_addrs = account->cc_addrs; + if (account->always_bcc) + bcc_addrs = account->bcc_addrs; -static void -menu_file_save_as_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - saveas (E_MSG_COMPOSER(data)); -} + uid = account->id->sig_uid; + signature = uid ? mail_config_get_signature_by_uid (uid) : NULL; + e_composer_header_table_set_signature (table, signature); -static void -menu_file_send_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - g_signal_emit (GTK_OBJECT (data), signals[SEND], 0); +exit: + update_auto_recipients (table, UPDATE_AUTO_CC, cc_addrs); + update_auto_recipients (table, UPDATE_AUTO_BCC, bcc_addrs); + + e_msg_composer_show_sig_file (composer); } static void -menu_file_close_cb (BonoboUIComponent *uic, - void *data, - const char *path) +msg_composer_attach_message (EMsgComposer *composer, + CamelMimeMessage *msg) { - EMsgComposer *composer; + CamelMimePart *mime_part; + GString *description; + const gchar *subject; + EMsgComposerPrivate *p = composer->priv; - composer = E_MSG_COMPOSER (data); - do_exit (composer); -} + mime_part = camel_mime_part_new (); + camel_mime_part_set_disposition (mime_part, "inline"); + subject = camel_mime_message_get_subject (msg); -/* this is the callback for the help menu */ -static void -menu_help_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - EMsgComposer *composer = (EMsgComposer *) data; + description = g_string_new (_("Attached message")); + if (subject != NULL) + g_string_append_printf (description, " - %s", subject); + camel_mime_part_set_description (mime_part, description->str); + g_string_free (description, TRUE); - e_msg_composer_show_help (composer); -} + camel_medium_set_content_object ( + (CamelMedium *) mime_part, (CamelDataWrapper *) msg); + camel_mime_part_set_content_type (mime_part, "message/rfc822"); + + e_attachment_bar_attach_mime_part ( + E_ATTACHMENT_BAR (p->attachment_bar), mime_part); + camel_object_unref (mime_part); +} static void -add_to_bar (EMsgComposer *composer, GSList *names, int is_inline) +msg_composer_update_preferences (GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + gboolean enable; + GError *error = NULL; - while (names) { - CamelURL *url; + editor = GTKHTML_EDITOR (composer); - if (!(url = camel_url_new (names->data, NULL))) - continue; + enable = gconf_client_get_bool ( + client, COMPOSER_GCONF_INLINE_SPELLING_KEY, &error); + if (error == NULL) + gtkhtml_editor_set_inline_spelling (editor, enable); + else { + g_warning ("%s", error->message); + g_clear_error (&error); + } - if (!g_ascii_strcasecmp (url->protocol, "file")) { - e_attachment_bar_attach((EAttachmentBar *)p->attachment_bar, url->path, is_inline ? "inline" : "attachment"); - } else { - e_attachment_bar_attach_remote_file ((EAttachmentBar *)p->attachment_bar, names->data, is_inline ? "inline" : "attachment"); - } + enable = gconf_client_get_bool ( + client, COMPOSER_GCONF_MAGIC_LINKS_KEY, &error); + if (error == NULL) + gtkhtml_editor_set_magic_links (editor, enable); + else { + g_warning ("%s", error->message); + g_clear_error (&error); + } - camel_url_free (url); - names = names->next; + enable = gconf_client_get_bool ( + client, COMPOSER_GCONF_MAGIC_SMILEYS_KEY, &error); + if (error == NULL) + gtkhtml_editor_set_magic_smileys (editor, enable); + else { + g_warning ("%s", error->message); + g_clear_error (&error); } } -static void -menu_file_add_attachment_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - EMsgComposer *composer = E_MSG_COMPOSER (data); - EMsgComposerPrivate *p = composer->priv; - EMsgComposer *toplevel = E_MSG_COMPOSER (gtk_widget_get_toplevel (GTK_WIDGET (p->attachment_bar))); - GtkWidget **attachment_selector = e_attachment_bar_get_selector(E_ATTACHMENT_BAR(p->attachment_bar)); - - e_msg_composer_select_file_attachments (toplevel, attachment_selector, add_to_bar); -} +struct _drop_data { + EMsgComposer *composer; -static void -menu_edit_cut_cb (BonoboUIComponent *uic, void *data, const char *path) -{ - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + GdkDragContext *context; + /* Only selection->data and selection->length are valid */ + GtkSelectionData *selection; - g_return_if_fail (p->focused_entry != NULL); + guint32 action; + guint info; + guint time; - if (GTK_IS_ENTRY (p->focused_entry)) { - gtk_editable_cut_clipboard (GTK_EDITABLE (p->focused_entry)); - } else { - /* happy happy joy joy, an EEntry. */ - g_return_if_reached (); - } -} + unsigned int move:1; + unsigned int moved:1; + unsigned int aborted:1; +}; -static void -menu_edit_copy_cb (BonoboUIComponent *uic, void *data, const char *path) +int +e_msg_composer_get_remote_download_count (EMsgComposer *composer) { - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + EAttachmentBar *attachment_bar; - g_return_if_fail (p->focused_entry != NULL); + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), 0); - if (GTK_IS_ENTRY (p->focused_entry)) { - gtk_editable_copy_clipboard (GTK_EDITABLE (p->focused_entry)); - } else { - /* happy happy joy joy, an EEntry. */ - g_return_if_reached (); - } + attachment_bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar); + return e_attachment_bar_get_download_count (attachment_bar); } static void -menu_edit_paste_cb (BonoboUIComponent *uic, void *data, const char *path) +drop_action (EMsgComposer *composer, + GdkDragContext *context, + guint32 action, + GtkSelectionData *selection, + guint info, + guint time, + gboolean html_dnd) { - EMsgComposer *composer = data; + char *tmp, *str, **urls; + CamelMimePart *mime_part; + CamelStream *stream; + CamelMimeMessage *msg; + char *content_type; + int i, success = FALSE, delete = FALSE; EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (p->focused_entry != NULL); + switch (info) { + case DND_TYPE_MESSAGE_RFC822: + d (printf ("dropping a message/rfc822\n")); + /* write the message (s) out to a CamelStream so we can use it */ + stream = camel_stream_mem_new (); + camel_stream_write (stream, (const gchar *)selection->data, selection->length); + camel_stream_reset (stream); - if (GTK_IS_ENTRY (p->focused_entry)) { - gtk_editable_paste_clipboard (GTK_EDITABLE (p->focused_entry)); - } else { - /* happy happy joy joy, an EEntry. */ - g_return_if_reached (); - } -} + msg = camel_mime_message_new (); + if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *)msg, stream) != -1) { + msg_composer_attach_message (composer, msg); + success = TRUE; + delete = action == GDK_ACTION_MOVE; + } -static void -menu_send_options_cb (BonoboUIComponent *component, void *data, const char *path) -{ - EMEvent *e = em_event_peek(); - EMEventTargetComposer *target; - EMsgComposer *composer = data; - - target = em_event_target_new_composer (e, composer, EM_EVENT_COMPOSER_SEND_OPTION); - e_msg_composer_set_send_options (composer, FALSE); - e_event_emit((EEvent *)e, "composer.selectsendoption", (EEventTarget *)target); - if (!composer->priv->send_invoked) { - e_error_run ((GtkWindow *)composer, "mail-composer:send-options-support", NULL); - } -} + camel_object_unref (msg); + camel_object_unref (stream); + break; + case DND_TYPE_NETSCAPE_URL: + d (printf ("dropping a _NETSCAPE_URL\n")); + tmp = g_strndup ((const gchar *) selection->data, selection->length); + urls = g_strsplit (tmp, "\n", 2); + g_free (tmp); -static void -menu_edit_select_all_cb (BonoboUIComponent *uic, void *data, const char *path) -{ - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + /* _NETSCAPE_URL is represented as "URI\nTITLE" */ + handle_uri (composer, urls[0], html_dnd); - g_return_if_fail (p->focused_entry != NULL); + g_strfreev (urls); + success = TRUE; + break; + case DND_TYPE_TEXT_URI_LIST: + d (printf ("dropping a text/uri-list\n")); + tmp = g_strndup ((const gchar *) selection->data, selection->length); + urls = g_strsplit (tmp, "\n", 0); + g_free (tmp); - if (GTK_IS_ENTRY (p->focused_entry)) { - gtk_editable_set_position (GTK_EDITABLE (p->focused_entry), -1); - gtk_editable_select_region (GTK_EDITABLE (p->focused_entry), 0, -1); - } else { - /* happy happy joy joy, an EEntry. */ - g_return_if_reached (); - } -} + for (i = 0; urls[i] != NULL; i++) { + str = g_strstrip (urls[i]); + if (str[0] == '#' || str[0] == '\0') + continue; -static void -menu_edit_delete_all_cb (BonoboUIComponent *uic, void *data, const char *path) -{ - EMsgComposer *composer = E_MSG_COMPOSER (data); - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - GNOME_GtkHTML_Editor_Engine_undoBegin (p->eeditor_engine, "Delete all but signature", "Undelete all", &ev); - GNOME_GtkHTML_Editor_Engine_freeze (p->eeditor_engine, &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "disable-selection", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "text-default-color", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "bold-off", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "underline-off", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "strikeout-off", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "select-all", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "delete", &ev); - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "signature", "0", &ev); - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "orig", "0", &ev); - e_msg_composer_show_sig_file (composer); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "style-normal", &ev); - GNOME_GtkHTML_Editor_Engine_thaw (p->eeditor_engine, &ev); - GNOME_GtkHTML_Editor_Engine_undoEnd (p->eeditor_engine, &ev); + handle_uri (composer, str, html_dnd); + } - CORBA_exception_free (&ev); - /* printf ("delete all\n"); */ -} + g_strfreev (urls); + success = TRUE; + break; + case DND_TYPE_TEXT_VCARD: + case DND_TYPE_TEXT_CALENDAR: + content_type = gdk_atom_name (selection->type); + d (printf ("dropping a %s\n", content_type)); -static void -menu_format_html_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) + mime_part = camel_mime_part_new (); + camel_mime_part_set_content (mime_part, (const gchar *)selection->data, selection->length, content_type); + camel_mime_part_set_disposition (mime_part, "inline"); -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + e_attachment_bar_attach_mime_part (E_ATTACHMENT_BAR (p->attachment_bar), mime_part); - e_msg_composer_set_send_html (E_MSG_COMPOSER (user_data), atoi (state)); -} + camel_object_unref (mime_part); + g_free (content_type); -static void -menu_security_pgp_sign_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) + success = TRUE; + break; + case DND_TYPE_X_UID_LIST: { + GPtrArray *uids; + char *inptr, *inend; + CamelFolder *folder; + CamelException ex = CAMEL_EXCEPTION_INITIALISER; -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + /* NB: This all runs synchronously, could be very slow/hang/block the ui */ - e_msg_composer_set_pgp_sign (E_MSG_COMPOSER (composer), atoi (state)); -} + uids = g_ptr_array_new (); -static void -menu_security_pgp_encrypt_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) + inptr = (char*)selection->data; + inend = (char*)(selection->data + selection->length); + while (inptr < inend) { + char *start = inptr; -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + while (inptr < inend && *inptr) + inptr++; - e_msg_composer_set_pgp_encrypt (E_MSG_COMPOSER (composer), atoi (state)); -} + if (start > (char *)selection->data) + g_ptr_array_add (uids, g_strndup (start, inptr-start)); -static void -menu_security_smime_sign_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) + inptr++; + } -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + if (uids->len > 0) { + folder = mail_tool_uri_to_folder ((const gchar *)selection->data, 0, &ex); + if (folder) { + if (uids->len == 1) { + msg = camel_folder_get_message (folder, uids->pdata[0], &ex); + if (msg == NULL) + goto fail; + msg_composer_attach_message (composer, msg); + } else { + CamelMultipart *mp = camel_multipart_new (); + char *desc; - e_msg_composer_set_smime_sign (E_MSG_COMPOSER (composer), atoi (state)); -} + camel_data_wrapper_set_mime_type ((CamelDataWrapper *)mp, "multipart/digest"); + camel_multipart_set_boundary (mp, NULL); + for (i=0;ilen;i++) { + msg = camel_folder_get_message (folder, uids->pdata[i], &ex); + if (msg) { + mime_part = camel_mime_part_new (); + camel_mime_part_set_disposition (mime_part, "inline"); + camel_medium_set_content_object ((CamelMedium *)mime_part, (CamelDataWrapper *)msg); + camel_mime_part_set_content_type (mime_part, "message/rfc822"); + camel_multipart_add_part (mp, mime_part); + camel_object_unref (mime_part); + camel_object_unref (msg); + } else { + camel_object_unref (mp); + goto fail; + } + } + mime_part = camel_mime_part_new (); + camel_medium_set_content_object ((CamelMedium *)mime_part, (CamelDataWrapper *)mp); + /* translators, this count will always be >1 */ + desc = g_strdup_printf (ngettext ("Attached message", "%d attached messages", uids->len), uids->len); + camel_mime_part_set_description (mime_part, desc); + g_free (desc); + e_attachment_bar_attach_mime_part (E_ATTACHMENT_BAR(p->attachment_bar), mime_part); + camel_object_unref (mime_part); + camel_object_unref (mp); + } + success = TRUE; + delete = action == GDK_ACTION_MOVE; + fail: + if (camel_exception_is_set (&ex)) { + char *name; -static void -menu_security_smime_encrypt_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) + camel_object_get (folder, NULL, CAMEL_FOLDER_NAME, &name, NULL); + e_error_run ((GtkWindow *)composer, "mail-composer:attach-nomessages", + name?name:(char *)selection->data, camel_exception_get_description (&ex), NULL); + camel_object_free (folder, CAMEL_FOLDER_NAME, name); + } + camel_object_unref (folder); + } else { + e_error_run ((GtkWindow *)composer, "mail-composer:attach-nomessages", + (const gchar*)selection->data, camel_exception_get_description (&ex), NULL); + } -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + camel_exception_clear (&ex); + } - e_msg_composer_set_smime_encrypt (E_MSG_COMPOSER (composer), atoi (state)); -} + g_ptr_array_free (uids, TRUE); + + break; } + default: + d (printf ("dropping an unknown\n")); + break; + } + gtk_drag_finish (context, success, delete, time); +} static void -menu_view_from_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +drop_popup_copy (EPopup *ep, EPopupItem *item, gpointer data) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + struct _drop_data *m = data; - e_msg_composer_set_view_from (E_MSG_COMPOSER (user_data), atoi (state)); + drop_action ( + m->composer, m->context, GDK_ACTION_COPY, + m->selection, m->info, m->time, FALSE); } static void -menu_view_replyto_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +drop_popup_move (EPopup *ep, EPopupItem *item, gpointer data) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + struct _drop_data *m = data; - e_msg_composer_set_view_replyto (E_MSG_COMPOSER (user_data), atoi (state)); + drop_action ( + m->composer, m->context, GDK_ACTION_MOVE, + m->selection, m->info, m->time, FALSE); } static void -menu_view_to_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +drop_popup_cancel (EPopup *ep, EPopupItem *item, gpointer data) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + struct _drop_data *m = data; - e_msg_composer_set_view_to (E_MSG_COMPOSER (user_data), atoi (state)); + gtk_drag_finish (m->context, FALSE, FALSE, m->time); } +static EPopupItem drop_popup_menu[] = { + { E_POPUP_ITEM, "00.emc.02", N_("_Copy"), drop_popup_copy, NULL, "mail-copy", 0 }, + { E_POPUP_ITEM, "00.emc.03", N_("_Move"), drop_popup_move, NULL, "mail-move", 0 }, + { E_POPUP_BAR, "10.emc" }, + { E_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), drop_popup_cancel, NULL, NULL, 0 }, +}; + static void -menu_view_postto_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +drop_popup_free (EPopup *ep, GSList *items, gpointer data) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + struct _drop_data *m = data; - e_msg_composer_set_view_postto (E_MSG_COMPOSER (user_data), atoi (state)); + g_slist_free (items); + + g_object_unref (m->context); + g_object_unref (m->composer); + g_free (m->selection->data); + g_free (m->selection); + g_free (m); } static void -menu_view_cc_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +msg_composer_notify_header_cb (EMsgComposer *composer) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + GtkhtmlEditor *editor; - e_msg_composer_set_view_cc (E_MSG_COMPOSER (user_data), atoi (state)); + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_set_changed (editor, TRUE); } -static void -menu_view_bcc_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +static GObject * +msg_composer_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_bcc (E_MSG_COMPOSER (user_data), atoi (state)); + GObject *object; + EMsgComposer *composer; + GtkToggleAction *action; + GConfClient *client; + GArray *array; + gboolean active; + guint binding_id; + + /* Chain up to parent's constructor() method. */ + object = G_OBJECT_CLASS (parent_class)->constructor ( + type, n_construct_properties, construct_properties); + + composer = E_MSG_COMPOSER (object); + client = gconf_client_get_default (); + array = composer->priv->gconf_bridge_binding_ids; + + /* Restore Persistent State */ + + binding_id = gconf_bridge_bind_property ( + gconf_bridge_get (), + COMPOSER_GCONF_CURRENT_FOLDER_KEY, + G_OBJECT (composer), "current-folder"); + g_array_append_val (array, binding_id); + + binding_id = gconf_bridge_bind_property ( + gconf_bridge_get (), + COMPOSER_GCONF_VIEW_BCC_KEY, + G_OBJECT (ACTION (VIEW_BCC)), "active"); + g_array_append_val (array, binding_id); + + binding_id = gconf_bridge_bind_property ( + gconf_bridge_get (), + COMPOSER_GCONF_VIEW_CC_KEY, + G_OBJECT (ACTION (VIEW_CC)), "active"); + g_array_append_val (array, binding_id); + + binding_id = gconf_bridge_bind_property ( + gconf_bridge_get (), + COMPOSER_GCONF_VIEW_FROM_KEY, + G_OBJECT (ACTION (VIEW_FROM)), "active"); + g_array_append_val (array, binding_id); + + binding_id = gconf_bridge_bind_property ( + gconf_bridge_get (), + COMPOSER_GCONF_VIEW_POST_TO_KEY, + G_OBJECT (ACTION (VIEW_POST_TO)), "active"); + g_array_append_val (array, binding_id); + + binding_id = gconf_bridge_bind_property ( + gconf_bridge_get (), + COMPOSER_GCONF_VIEW_REPLY_TO_KEY, + G_OBJECT (ACTION (VIEW_REPLY_TO)), "active"); + g_array_append_val (array, binding_id); + + binding_id = gconf_bridge_bind_window ( + gconf_bridge_get (), + COMPOSER_GCONF_WINDOW_PREFIX, + GTK_WINDOW (composer), TRUE, FALSE); + g_array_append_val (array, binding_id); + + /* Honor User Preferences */ + + active = gconf_client_get_bool ( + client, COMPOSER_GCONF_SEND_HTML_KEY, NULL); + gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (composer), active); + + action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT)); + active = gconf_client_get_bool ( + client, COMPOSER_GCONF_REQUEST_RECEIPT_KEY, NULL); + gtk_toggle_action_set_active (action, active); + + gconf_client_add_dir ( + client, COMPOSER_GCONF_PREFIX, + GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); + composer->priv->notify_id = gconf_client_notify_add ( + client, COMPOSER_GCONF_PREFIX, (GConfClientNotifyFunc) + msg_composer_update_preferences, composer, NULL, NULL); + msg_composer_update_preferences (client, 0, NULL, composer); + + g_object_unref (client); + + return object; } static void -menu_insert_receipt_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +msg_composer_dispose (GObject *object) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + EMsgComposer *composer = E_MSG_COMPOSER (object); - e_msg_composer_set_request_receipt (E_MSG_COMPOSER (user_data), atoi (state)); + e_composer_autosave_unregister (composer); + e_composer_private_dispose (composer); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); } static void -menu_insert_priority_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +msg_composer_finalize (GObject *object) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + EMsgComposer *composer = E_MSG_COMPOSER (object); + + e_composer_private_finalize (composer); - e_msg_composer_set_priority (E_MSG_COMPOSER (user_data), atoi (state)); + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); } static void -menu_changed_charset_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) +msg_composer_destroy (GtkObject *object) { - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; + EMsgComposer *composer = E_MSG_COMPOSER (object); - if (atoi (state)) { - EMsgComposer *composer = E_MSG_COMPOSER (user_data); - EMsgComposerPrivate *p = composer->priv; - /* Charset menu names are "Charset-%s" where %s is the charset name */ - g_free (p->charset); - p->charset = g_strdup (path + strlen ("Charset-")); + all_composers = g_slist_remove (all_composers, object); + +#if 0 /* GTKHTML-EDITOR */ + if (composer->priv->menu) { + e_menu_update_target ((EMenu *)composer->priv->menu, NULL); + g_object_unref (composer->priv->menu); + composer->priv->menu = NULL; } -} +#endif - -static BonoboUIVerb verbs [] = { + if (composer->priv->address_dialog != NULL) { + gtk_widget_destroy (composer->priv->address_dialog); + composer->priv->address_dialog = NULL; + } - BONOBO_UI_VERB ("FileOpen", menu_file_open_cb), - BONOBO_UI_VERB ("FileSave", menu_file_save_cb), - BONOBO_UI_VERB ("FileSaveAs", menu_file_save_as_cb), - BONOBO_UI_VERB ("FileSaveDraft", menu_file_save_draft_cb), - BONOBO_UI_VERB ("FileClose", menu_file_close_cb), - BONOBO_UI_VERB ("Help", menu_help_cb), - BONOBO_UI_VERB ("FileAttach", menu_file_add_attachment_cb), + if (composer->priv->notify_id) { + GConfClient *client; - BONOBO_UI_VERB ("FileSend", menu_file_send_cb), + client = gconf_client_get_default (); + gconf_client_notify_remove (client, composer->priv->notify_id); + composer->priv->notify_id = 0; + g_object_unref (client); + } - BONOBO_UI_VERB ("DeleteAll", menu_edit_delete_all_cb), - BONOBO_UI_VERB ("InsertXSendOptions", menu_send_options_cb), + /* Chain up to parent's destroy() method. */ + GTK_OBJECT_CLASS (parent_class)->destroy (object); +} - BONOBO_UI_VERB_END -}; +static void +msg_composer_map (GtkWidget *widget) +{ + EComposerHeaderTable *table; + GtkWidget *input_widget; + const gchar *text; -static EPixmap pixcache [] = { - E_PIXMAP ("/commands/DeleteAll", "edit-delete", E_ICON_SIZE_MENU), - E_PIXMAP ("/commands/FileAttach", "mail-attachment", E_ICON_SIZE_MENU), - E_PIXMAP ("/commands/FileClose", "window-close", E_ICON_SIZE_MENU), - E_PIXMAP ("/commands/FileOpen", "document-open", E_ICON_SIZE_MENU), - E_PIXMAP ("/commands/FileSave", "document-save", E_ICON_SIZE_MENU), - E_PIXMAP ("/commands/FileSaveAs", "document-save-as", E_ICON_SIZE_MENU), - E_PIXMAP ("/commands/FileSend", "mail-send", E_ICON_SIZE_MENU), + /* Chain up to parent's map() method. */ + GTK_WIDGET_CLASS (parent_class)->map (widget); - E_PIXMAP ("/Toolbar/FileSend", "mail-send", E_ICON_SIZE_LARGE_TOOLBAR), - E_PIXMAP ("/Toolbar/FileSaveDraft", "document-save", E_ICON_SIZE_LARGE_TOOLBAR) , - E_PIXMAP ("/Toolbar/FileAttach", "mail-attachment", E_ICON_SIZE_LARGE_TOOLBAR), + table = e_msg_composer_get_header_table (E_MSG_COMPOSER (widget)); - E_PIXMAP_END -}; + /* If the 'To' field is empty, focus it. */ + input_widget = + e_composer_header_table_get_header ( + table, E_COMPOSER_HEADER_TO)->input_widget; + text = gtk_entry_get_text (GTK_ENTRY (input_widget)); + if (text == NULL || *text == '\0') { + gtk_widget_grab_focus (input_widget); + return; + } + /* If not, check the 'Subject' field. */ + input_widget = + e_composer_header_table_get_header ( + table, E_COMPOSER_HEADER_SUBJECT)->input_widget; + text = gtk_entry_get_text (GTK_ENTRY (input_widget)); + if (text == NULL || *text == '\0') { + gtk_widget_grab_focus (input_widget); + return; + } -static void -setup_ui (EMsgComposer *composer) + /* Jump to the editor as a last resort. */ + gtkhtml_editor_run_command (GTKHTML_EDITOR (widget), "grab-focus"); +} + +static gint +msg_composer_delete_event (GtkWidget *widget, + GdkEventAny *event) { - EMMenuTargetWidget *target; - EMsgComposerPrivate *p = composer->priv; - BonoboUIContainer *container; - gboolean hide_smime; - char *charset; - char *xmlfile; - - container = bonobo_window_get_ui_container (BONOBO_WINDOW (composer)); - - p->uic = bonobo_ui_component_new_default (); - /* FIXME: handle bonobo exceptions */ - bonobo_ui_component_set_container (p->uic, bonobo_object_corba_objref (BONOBO_OBJECT (container)), NULL); - - bonobo_ui_component_add_verb_list_with_data (p->uic, verbs, composer); - - bonobo_ui_component_freeze (p->uic, NULL); - - xmlfile = g_build_filename (EVOLUTION_UIDIR, "evolution-message-composer.xml", NULL); - bonobo_ui_util_set_ui (p->uic, PREFIX, - xmlfile, - "evolution-message-composer", NULL); - g_free (xmlfile); - - e_pixmaps_update (p->uic, pixcache); - - /* Populate the Charset Encoding menu and default it to whatever the user - chose as his default charset in the mailer */ - charset = composer_get_default_charset_setting (); - e_charset_picker_bonobo_ui_populate (p->uic, "/menu/Edit/EncodingPlaceholder", - charset, - menu_changed_charset_cb, - composer); - g_free (charset); - - /* Format -> HTML */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/FormatHtml", - "state", p->send_html ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "FormatHtml", - menu_format_html_cb, composer); - - /* View/From */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/ViewFrom", - "state", p->view_from ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "ViewFrom", - menu_view_from_cb, composer); - - /* View/ReplyTo */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/ViewReplyTo", - "state", p->view_replyto ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "ViewReplyTo", - menu_view_replyto_cb, composer); - - /* View/To */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/ViewTo", - "state", p->view_to ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "ViewTo", - menu_view_to_cb, composer); - - /* View/PostTo */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/ViewPostTo", - "state", p->view_postto ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "ViewPostTo", - menu_view_postto_cb, composer); - - /* View/CC */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/ViewCC", - "state", p->view_cc ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "ViewCC", - menu_view_cc_cb, composer); - - /* View/BCC */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/ViewBCC", - "state", p->view_bcc ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "ViewBCC", - menu_view_bcc_cb, composer); - - /* Insert/Request Receipt */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/RequestReceipt", - "state", p->request_receipt ? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "RequestReceipt", - menu_insert_receipt_cb, composer); - - /* Insert/Exchange Send Options */ -/* bonobo_ui_component_set_prop ( - p->uic, "/commands/XSendOptions", - "state", "1", NULL); - bonobo_ui_component_add_listener ( - p->uic, "XSendOptions", - menu_send_options_cb, composer);*/ - - /* Insert/Set Priority*/ - bonobo_ui_component_set_prop ( - p->uic, "/commands/SetPriority", - "state", p->set_priority? "1" : "0", NULL); - bonobo_ui_component_add_listener ( - p->uic, "SetPriority", - menu_insert_priority_cb, composer); - - /* Security -> PGP Sign */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/SecurityPGPSign", - "state", p->pgp_sign ? "1" : "0", NULL); - - bonobo_ui_component_add_listener ( - p->uic, "SecurityPGPSign", - menu_security_pgp_sign_cb, composer); - - /* Security -> PGP Encrypt */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/SecurityPGPEncrypt", - "state", p->pgp_encrypt ? "1" : "0", NULL); - - bonobo_ui_component_add_listener ( - p->uic, "SecurityPGPEncrypt", - menu_security_pgp_encrypt_cb, composer); - -#if defined(HAVE_NSS) && defined(SMIME_SUPPORTED) - hide_smime = FALSE; -#else - hide_smime = TRUE; -#endif + /* This is needed for the ACTION macro. */ + EMsgComposer *composer = E_MSG_COMPOSER (widget); - /* Security -> S/MIME Sign */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/SecuritySMimeSign", - "state", p->smime_sign ? "1" : "0", NULL); - bonobo_ui_component_set_prop ( - p->uic, "/commands/SecuritySMimeSign", - "hidden", hide_smime ? "1" : "0", NULL); + gtk_action_activate (ACTION (CLOSE)); - bonobo_ui_component_add_listener ( - p->uic, "SecuritySMimeSign", - menu_security_smime_sign_cb, composer); + return TRUE; +} - /* Security -> S/MIME Encrypt */ - bonobo_ui_component_set_prop ( - p->uic, "/commands/SecuritySMimeEncrypt", - "state", p->smime_encrypt ? "1" : "0", NULL); - bonobo_ui_component_set_prop ( - p->uic, "/commands/SecuritySMimeEncrypt", - "hidden", hide_smime ? "1" : "0", NULL); +static gboolean +msg_composer_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + EMsgComposer *composer = E_MSG_COMPOSER (widget); + GtkWidget *input_widget; - bonobo_ui_component_add_listener ( - p->uic, "SecuritySMimeEncrypt", - menu_security_smime_encrypt_cb, composer); + input_widget = + e_composer_header_table_get_header ( + e_msg_composer_get_header_table (composer), + E_COMPOSER_HEADER_SUBJECT)->input_widget; - bonobo_ui_component_thaw (p->uic, NULL); +#ifdef HAVE_XFREE + if (event->keyval == XF86XK_Send) { + g_signal_emit (G_OBJECT (composer), signals[SEND], 0); + return TRUE; + } +#endif /* HAVE_XFREE */ - /* Create the UIComponent for the non-control entries */ + if (event->keyval == GDK_Escape) { + gtk_action_activate (ACTION (CLOSE)); + return TRUE; + } - p->entry_uic = bonobo_ui_component_new_default (); + if (event->keyval == GDK_Tab && gtk_widget_is_focus (input_widget)) { + gtkhtml_editor_run_command ( + GTKHTML_EDITOR (composer), "grab-focus"); + return TRUE; + } - /* Setup main menu plugin mechanism */ - target = em_menu_target_new_widget(p->menu, (GtkWidget *)composer); - e_menu_update_target((EMenu *)p->menu, target); - e_menu_activate((EMenu *)p->menu, p->uic, TRUE); + /* Chain up to parent's key_press_event() method. */ + return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event); } - -/* Miscellaneous callbacks. */ - -static void -attachment_bar_changed_cb (EAttachmentBar *bar, - void *data) +static gboolean +msg_composer_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) { - EMsgComposer *composer = E_MSG_COMPOSER (data); - EMsgComposerPrivate *p = composer->priv; - - guint attachment_num = e_attachment_bar_get_num_attachments ( - E_ATTACHMENT_BAR (p->attachment_bar)); - if (attachment_num) { - gchar *num_text = g_strdup_printf ( - ngettext ("%d Attachment", "%d Attachments", attachment_num), - attachment_num); - gtk_label_set_markup (GTK_LABEL (p->attachment_expander_num), - num_text); - g_free (num_text); - - gtk_widget_show (p->attachment_expander_icon); - show_attachments (composer, TRUE); - } else { - gtk_label_set_text (GTK_LABEL (p->attachment_expander_num), ""); - gtk_widget_hide (p->attachment_expander_icon); - show_attachments (composer, FALSE); - } - + GList *targets; + GdkDragAction actions = 0; + GdkDragAction chosen_action; - /* Mark the composer as changed so it prompts about unsaved - changes on close */ - e_msg_composer_set_changed (composer); -} -static void -attachment_expander_activate_cb (EExpander *expander, - void *data) -{ - EMsgComposer *composer = E_MSG_COMPOSER (data); - gboolean show = e_expander_get_expanded (expander); + targets = context->targets; + while (targets != NULL) { + gint ii; - /* Update the expander label */ - if (show) - gtk_label_set_text_with_mnemonic (GTK_LABEL (composer->priv->attachment_expander_label), - _("Hide _Attachment Bar")); - else - gtk_label_set_text_with_mnemonic (GTK_LABEL (composer->priv->attachment_expander_label), - _("Show _Attachment Bar")); - } -static void -subject_changed_cb (EMsgComposerHdrs *hdrs, - gchar *subject, - void *data) -{ - EMsgComposer *composer; + for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++) + if (targets->data == (gpointer) drag_info[ii].atom) + actions |= drag_info[ii].actions; - composer = E_MSG_COMPOSER (data); + targets = g_list_next (targets); + } - gtk_window_set_title (GTK_WINDOW (composer), subject[0] ? subject : _("Compose Message")); -} + actions &= context->actions; + chosen_action = context->suggested_action; -static void -hdrs_changed_cb (EMsgComposerHdrs *hdrs, - void *data) -{ - EMsgComposer *composer; + /* we default to copy */ + if (chosen_action == GDK_ACTION_ASK && + (actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) != + (GDK_ACTION_MOVE|GDK_ACTION_COPY)) + chosen_action = GDK_ACTION_COPY; - composer = E_MSG_COMPOSER (data); + gdk_drag_status (context, chosen_action, time); - /* Mark the composer as changed so it prompts about unsaved changes on close */ - e_msg_composer_set_changed (composer); + return (chosen_action != 0); } -enum { - UPDATE_AUTO_CC, - UPDATE_AUTO_BCC, -}; - static void -update_auto_recipients (EMsgComposerHdrs *hdrs, int mode, const char *auto_addrs) +msg_composer_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time) { - EDestination *dest, **destv = NULL; - CamelInternetAddress *iaddr; - GList *list, *tail, *node; - int i, n = 0; - - tail = list = NULL; - - if (auto_addrs) { - iaddr = camel_internet_address_new (); - if (camel_address_decode (CAMEL_ADDRESS (iaddr), auto_addrs) != -1) { - for (i = 0; i < camel_address_length (CAMEL_ADDRESS (iaddr)); i++) { - const char *name, *addr; - - if (!camel_internet_address_get (iaddr, i, &name, &addr)) - continue; + EMsgComposer *composer; - dest = e_destination_new (); - e_destination_set_auto_recipient (dest, TRUE); + /* Widget may be EMsgComposer or GtkHTML. */ + composer = E_MSG_COMPOSER (gtk_widget_get_toplevel (widget)); - if (name) - e_destination_set_name (dest, name); + if (selection->data == NULL) + return; - if (addr) - e_destination_set_email (dest, addr); + if (selection->length == -1) + return; - node = g_list_alloc (); - node->data = dest; - node->next = NULL; + if (context->action == GDK_ACTION_ASK) { + EMPopup *emp; + GSList *menus = NULL; + GtkMenu *menu; + gint ii; + struct _drop_data *m; - if (tail) { - node->prev = tail; - tail->next = node; - } else { - node->prev = NULL; - list = node; - } + m = g_malloc0(sizeof (*m)); + m->context = g_object_ref (context); + m->composer = g_object_ref (composer); + m->action = context->action; + m->info = info; + m->time = time; + m->selection = g_malloc0(sizeof (*m->selection)); + m->selection->data = g_malloc (selection->length); + memcpy (m->selection->data, selection->data, selection->length); + m->selection->length = selection->length; - tail = node; - n++; - } - } + emp = em_popup_new ("org.gnome.evolution.mail.composer.popup.drop"); + for (ii = 0; ii < G_N_ELEMENTS (drop_popup_menu); ii++) + menus = g_slist_append (menus, &drop_popup_menu[ii]); - camel_object_unref (iaddr); + e_popup_add_items ((EPopup *)emp, menus, NULL, drop_popup_free, m); + menu = e_popup_create_menu_once ((EPopup *)emp, NULL, 0); + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, time); + } else { + drop_action ( + composer, context, context->action, selection, + info, time, !GTK_WIDGET_TOPLEVEL (widget)); } +} - switch (mode) { - case UPDATE_AUTO_CC: - destv = e_msg_composer_hdrs_get_cc (hdrs); - break; - case UPDATE_AUTO_BCC: - destv = e_msg_composer_hdrs_get_bcc (hdrs); - break; - default: - g_return_if_reached (); - } +static void +msg_composer_cut_clipboard (GtkhtmlEditor *editor) +{ + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; - if (destv) { - for (i = 0; destv[i]; i++) { - if (!e_destination_is_auto_recipient (destv[i])) { - node = g_list_alloc (); - node->data = e_destination_copy (destv[i]); - node->next = NULL; + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); - if (tail) { - node->prev = tail; - tail->next = node; - } else { - node->prev = NULL; - list = node; - } + if (parent == composer->priv->header_table) { + gtk_editable_cut_clipboard (GTK_EDITABLE (widget)); + return; + } - tail = node; - n++; - } - } + /* Chain up to parent's cut_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->cut_clipboard (editor); +} - e_destination_freev (destv); - } +static void +msg_composer_copy_clipboard (GtkhtmlEditor *editor) +{ + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; - destv = destination_list_to_vector_sized (list, n); - g_list_free (list); + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); - switch (mode) { - case UPDATE_AUTO_CC: - e_msg_composer_hdrs_set_cc (hdrs, destv); - break; - case UPDATE_AUTO_BCC: - e_msg_composer_hdrs_set_bcc (hdrs, destv); - break; - default: - g_return_if_reached (); + if (parent == composer->priv->header_table) { + gtk_editable_copy_clipboard (GTK_EDITABLE (widget)); + return; } - e_destination_freev (destv); + /* Chain up to parent's copy_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->copy_clipboard (editor); } static void -from_changed_cb (EMsgComposerHdrs *hdrs, void *data) +msg_composer_paste_clipboard (GtkhtmlEditor *editor) { - EMsgComposer *composer = E_MSG_COMPOSER (data); - EMsgComposerPrivate *p = composer->priv; - EAccount *account; + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; - account = e_msg_composer_hdrs_get_from_account (hdrs); - - if (account) { - e_msg_composer_set_pgp_sign (composer, - account->pgp_always_sign && - (!account->pgp_no_imip_sign || !p->mime_type || - g_ascii_strncasecmp (p->mime_type, "text/calendar", 13) != 0)); - e_msg_composer_set_smime_sign (composer, account->smime_sign_default); - e_msg_composer_set_smime_encrypt (composer, account->smime_encrypt_default); - update_auto_recipients (hdrs, UPDATE_AUTO_CC, account->always_cc ? account->cc_addrs : NULL); - update_auto_recipients (hdrs, UPDATE_AUTO_BCC, account->always_bcc ? account->bcc_addrs : NULL); - } else { - update_auto_recipients (hdrs, UPDATE_AUTO_CC, NULL); - update_auto_recipients (hdrs, UPDATE_AUTO_BCC, NULL); + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); + + if (parent == composer->priv->header_table) { + gtk_editable_paste_clipboard (GTK_EDITABLE (widget)); + return; } - set_editor_signature (composer); - e_msg_composer_show_sig_file (composer); + /* Chain up to parent's paste_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor); } - -/* GObject methods. */ - static void -composer_finalise (GObject *object) +msg_composer_select_all (GtkhtmlEditor *editor) { - EMsgComposer *composer = E_MSG_COMPOSER (object); - EMsgComposerPrivate *p = composer->priv; - - if (p->extra_hdr_names) { - int i; + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; - for (i = 0; i < p->extra_hdr_names->len; i++) { - g_free (p->extra_hdr_names->pdata[i]); - g_free (p->extra_hdr_values->pdata[i]); - } - g_ptr_array_free (p->extra_hdr_names, TRUE); - g_ptr_array_free (p->extra_hdr_values, TRUE); - } + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); - g_hash_table_destroy (p->inline_images); - g_hash_table_destroy (p->inline_images_by_url); + if (parent == composer->priv->header_table) { + gtk_editable_set_position (GTK_EDITABLE (widget), -1); + gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); + } else + /* Chain up to the parent's select_all() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->select_all (editor); +} - g_free (p->charset); - g_free (p->mime_type); - g_free (p->mime_body); +static void +msg_composer_command_before (GtkhtmlEditor *editor, + const gchar *command) +{ + EMsgComposer *composer; + const gchar *data; - if (p->redirect) - camel_object_unref (p->redirect); + composer = E_MSG_COMPOSER (editor); + if (strcmp (command, "insert-paragraph") != 0) + return; - g_free (p); + if (composer->priv->in_signature_insert) + return; - p = NULL; - composer->priv = NULL; + data = gtkhtml_editor_get_paragraph_data (editor, "orig"); + if (data != NULL && *data == '1') { + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); + return; + }; - if (G_OBJECT_CLASS (parent_class)->finalize != NULL) - (* G_OBJECT_CLASS (parent_class)->finalize) (object); + data = gtkhtml_editor_get_paragraph_data (editor, "signature"); + if (data != NULL && *data == '1') { + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); + } } static void -composer_dispose(GObject *object) +msg_composer_command_after (GtkhtmlEditor *editor, + const gchar *command) { - /* When destroy() is called, the contents of the window - * (including the remote editor control) will already have - * been destroyed, so we have to do this here. - */ - autosave_manager_unregister (am, E_MSG_COMPOSER (object)); + EMsgComposer *composer; + const gchar *data; - if (G_OBJECT_CLASS (parent_class)->dispose != NULL) - (* G_OBJECT_CLASS (parent_class)->dispose) (object); -} + composer = E_MSG_COMPOSER (editor); -/* GtkObject methods */ -static void -destroy (GtkObject *object) -{ - EMsgComposer *composer = (EMsgComposer *)object; - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; + if (strcmp (command, "insert-paragraph") != 0) + return; - CORBA_exception_init (&ev); + if (composer->priv->in_signature_insert) + return; - if (p->menu) { - e_menu_update_target((EMenu *)p->menu, NULL); - g_object_unref(p->menu); - p->menu = NULL; - } + gtkhtml_editor_run_command (editor, "italic-off"); - if (p->load) { - gtk_widget_destroy(p->load); - p->load = NULL; - } + data = gtkhtml_editor_get_paragraph_data (editor, "orig"); + if (data != NULL && *data == '1') + e_msg_composer_reply_indent (composer); + gtkhtml_editor_set_paragraph_data (editor, "orig", "0"); - if (p->saveas) { - gtk_widget_destroy(p->saveas); - p->saveas = NULL; - } + data = gtkhtml_editor_get_paragraph_data (editor, "signature"); + if (data == NULL || *data != '1') + return; - if (p->uic) { - bonobo_object_unref (BONOBO_OBJECT (p->uic)); - p->uic = NULL; - } + /* Clear the signature. */ + if (gtkhtml_editor_is_paragraph_empty (editor)) + gtkhtml_editor_set_paragraph_data (editor, "signature" ,"0"); - if (p->entry_uic) { - bonobo_object_unref (BONOBO_OBJECT (p->entry_uic)); - p->entry_uic = NULL; - } + else if (gtkhtml_editor_is_previous_paragraph_empty (editor) && + gtkhtml_editor_run_command (editor, "cursor-backward")) { - /* FIXME? I assume the Bonobo widget will get destroyed - normally? */ - if (p->address_dialog != NULL) { - gtk_widget_destroy (p->address_dialog); - p->address_dialog = NULL; - } - if (p->hdrs != NULL) { - gtk_widget_destroy (p->hdrs); - p->hdrs = NULL; + gtkhtml_editor_set_paragraph_data (editor, "signature", "0"); + gtkhtml_editor_run_command (editor, "cursor-forward"); } - if (p->notify_id) { - GConfClient *gconf = gconf_client_get_default (); - gconf_client_notify_remove (gconf, p->notify_id); - p->notify_id = 0; - g_object_unref (gconf); - } + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); +} - if (p->persist_stream_interface != CORBA_OBJECT_NIL) { - Bonobo_Unknown_unref (p->persist_stream_interface, &ev); - CORBA_Object_release (p->persist_stream_interface, &ev); - p->persist_stream_interface = CORBA_OBJECT_NIL; - } +static gchar * +msg_composer_image_uri (GtkhtmlEditor *editor, + const gchar *uri) +{ + EMsgComposer *composer; + GHashTable *hash_table; + CamelMimePart *part; + const gchar *cid; - if (p->persist_file_interface != CORBA_OBJECT_NIL) { - Bonobo_Unknown_unref (p->persist_file_interface, &ev); - CORBA_Object_release (p->persist_file_interface, &ev); - p->persist_file_interface = CORBA_OBJECT_NIL; - } + composer = E_MSG_COMPOSER (editor); - if (p->eeditor_engine != CORBA_OBJECT_NIL) { - Bonobo_Unknown_unref (p->eeditor_engine, &ev); - CORBA_Object_release (p->eeditor_engine, &ev); - p->eeditor_engine = CORBA_OBJECT_NIL; - } + hash_table = composer->priv->inline_images_by_url; + part = g_hash_table_lookup (hash_table, uri); - CORBA_exception_free (&ev); + if (part == NULL && g_str_has_prefix (uri, "file:")) + part = e_msg_composer_add_inline_image_from_file ( + composer, uri + 5); - if (p->eeditor_listener) { - bonobo_object_unref (p->eeditor_listener); - p->eeditor_listener = NULL; + if (part == NULL && g_str_has_prefix (uri, "cid:")) { + hash_table = composer->priv->inline_images; + part = g_hash_table_lookup (hash_table, uri); } - if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); + if (part == NULL) + return NULL; + + composer->priv->current_images = + g_list_prepend (composer->priv->current_images, part); + + cid = camel_mime_part_get_content_id (part); + if (cid == NULL) + return NULL; + + return g_strconcat ("cid:", cid, NULL); } - - /* show the help menu item of the composer */ static void -e_msg_composer_show_help (EMsgComposer *composer) +msg_composer_link_clicked (GtkhtmlEditor *editor, + const gchar *uri) { GError *error = NULL; - gnome_help_display ( - "evolution.xml", "usage-composer", &error); - if (error != NULL) { + if (uri == NULL || *uri == '\0') + return; + + if (g_ascii_strncasecmp (uri, "mailto:", 7) == 0) + return; + + if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) + return; + + if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) + return; + + gnome_url_show (uri, &error); + + if (error) { g_warning ("%s", error->message); g_error_free (error); } } +static void +msg_composer_object_deleted (GtkhtmlEditor *editor) +{ + const gchar *data; -/* GtkWidget methods. */ + if (!gtkhtml_editor_is_paragraph_empty (editor)) + return; -static int -delete_event (GtkWidget *widget, - GdkEventAny *event) -{ - do_exit (E_MSG_COMPOSER (widget)); + data = gtkhtml_editor_get_paragraph_data (editor, "orig"); + if (data != NULL && *data == '1') { + gtkhtml_editor_set_paragraph_data (editor, "orig", "0"); + gtkhtml_editor_run_command (editor, "indent-zero"); + gtkhtml_editor_run_command (editor, "style-normal"); + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); + gtkhtml_editor_run_command (editor, "insert-paragraph"); + gtkhtml_editor_run_command (editor, "delete-back"); + } - return TRUE; + data = gtkhtml_editor_get_paragraph_data (editor, "signature"); + if (data != NULL && *data == '1') + gtkhtml_editor_set_paragraph_data (editor, "signature", "0"); } static void -attach_message(EMsgComposer *composer, CamelMimeMessage *msg) +msg_composer_uri_requested (GtkhtmlEditor *editor, + const gchar *uri, + GtkHTMLStream *stream) { - CamelMimePart *mime_part; - const char *subject; - EMsgComposerPrivate *p = composer->priv; + EMsgComposer *composer; + GHashTable *hash_table; + GByteArray *array; + CamelDataWrapper *wrapper; + CamelStream *camel_stream; + CamelMimePart *part; + GtkHTML *html; - mime_part = camel_mime_part_new(); - camel_mime_part_set_disposition(mime_part, "inline"); - subject = camel_mime_message_get_subject(msg); - if (subject) { - char *desc = g_strdup_printf(_("Attached message - %s"), subject); + /* XXX It's unfortunate we have to expose GtkHTML structs here. + * Maybe we could rework this to use a GOutputStream. */ - camel_mime_part_set_description(mime_part, desc); - g_free(desc); - } else - camel_mime_part_set_description(mime_part, _("Attached message")); + composer = E_MSG_COMPOSER (editor); + html = gtkhtml_editor_get_html (editor); - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)msg); - camel_mime_part_set_content_type(mime_part, "message/rfc822"); - e_attachment_bar_attach_mime_part(E_ATTACHMENT_BAR(p->attachment_bar), mime_part); - camel_object_unref(mime_part); -} + hash_table = composer->priv->inline_images_by_url; + part = g_hash_table_lookup (hash_table, uri); -struct _drop_data { - EMsgComposer *composer; + if (part == NULL) { + hash_table = composer->priv->inline_images; + part = g_hash_table_lookup (hash_table, uri); + } - GdkDragContext *context; - /* Only selection->data and selection->length are valid */ - GtkSelectionData *selection; + if (part == NULL) { + gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR); + return; + } - guint32 action; - guint info; - guint time; + array = g_byte_array_new (); + camel_stream = camel_stream_mem_new_with_byte_array (array); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); + camel_data_wrapper_decode_to_stream (wrapper, camel_stream); - unsigned int move:1; - unsigned int moved:1; - unsigned int aborted:1; -}; + gtk_html_write ( + gtkhtml_editor_get_html (editor), stream, + (gchar *) array->data, array->len); -int -e_msg_composer_get_remote_download_count (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - return e_attachment_bar_get_download_count - (E_ATTACHMENT_BAR (p->attachment_bar)); + camel_object_unref (camel_stream); + + gtk_html_end (html, stream, GTK_HTML_STREAM_OK); } -static gchar * -attachment_guess_mime_type (const char *file_name) +static void +msg_composer_class_init (EMsgComposerClass *class) { - GnomeVFSFileInfo *info; - GnomeVFSResult result; - gchar *type = NULL; - - info = gnome_vfs_file_info_new (); - result = gnome_vfs_get_file_info (file_name, info, - GNOME_VFS_FILE_INFO_GET_MIME_TYPE | - GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE | - GNOME_VFS_FILE_INFO_FOLLOW_LINKS); - if (result == GNOME_VFS_OK) - type = g_strdup (gnome_vfs_file_info_get_mime_type (info)); - - gnome_vfs_file_info_unref (info); - - return type; + GObjectClass *object_class; + GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; + GtkhtmlEditorClass *editor_class; + gint ii; + + for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++) + drag_info[ii].atom = + gdk_atom_intern (drag_info[ii].target, FALSE); + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMsgComposerPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructor = msg_composer_constructor; + object_class->dispose = msg_composer_dispose; + object_class->finalize = msg_composer_finalize; + + gtk_object_class = GTK_OBJECT_CLASS (class); + gtk_object_class->destroy = msg_composer_destroy; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->map = msg_composer_map; + widget_class->delete_event = msg_composer_delete_event; + widget_class->key_press_event = msg_composer_key_press_event; + widget_class->drag_motion = msg_composer_drag_motion; + widget_class->drag_data_received = msg_composer_drag_data_received; + + editor_class = GTKHTML_EDITOR_CLASS (class); + editor_class->cut_clipboard = msg_composer_cut_clipboard; + editor_class->copy_clipboard = msg_composer_copy_clipboard; + editor_class->paste_clipboard = msg_composer_paste_clipboard; + editor_class->select_all = msg_composer_select_all; + editor_class->command_before = msg_composer_command_before; + editor_class->command_after = msg_composer_command_after; + editor_class->image_uri = msg_composer_image_uri; + editor_class->link_clicked = msg_composer_link_clicked; + editor_class->object_deleted = msg_composer_object_deleted; + editor_class->uri_requested = msg_composer_uri_requested; + + signals[SEND] = g_signal_new ( + "send", + E_TYPE_MSG_COMPOSER, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[SAVE_DRAFT] = g_signal_new ( + "save-draft", + E_TYPE_MSG_COMPOSER, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); } static void -drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, GtkSelectionData *selection, guint info, guint time, gboolean html_dnd) +msg_composer_init (EMsgComposer *composer) { - char *tmp, *str, **urls; - CamelMimePart *mime_part; - CamelStream *stream; - CamelMimeMessage *msg; - char *content_type; - int i, success = FALSE, delete = FALSE; - EMsgComposerPrivate *p = composer->priv; + EComposerHeaderTable *table; +#if 0 /* GTKHTML-EDITOR */ + EMMenuTargetWidget *target; +#endif + GtkHTML *html; - switch (info) { - case DND_TYPE_MESSAGE_RFC822: - d(printf ("dropping a message/rfc822\n")); - /* write the message(s) out to a CamelStream so we can use it */ - stream = camel_stream_mem_new (); - camel_stream_write (stream, (const gchar *)selection->data, selection->length); - camel_stream_reset (stream); - - msg = camel_mime_message_new (); - if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)msg, stream) != -1) { - attach_message(composer, msg); - success = TRUE; - delete = action == GDK_ACTION_MOVE; - } - - camel_object_unref(msg); - camel_object_unref(stream); - break; - case DND_TYPE_NETSCAPE_URL: - d(printf ("dropping a _NETSCAPE_URL\n")); - tmp = g_strndup ((const gchar *) selection->data, selection->length); - urls = g_strsplit (tmp, "\n", 2); - g_free (tmp); - - /* _NETSCAPE_URL is represented as "URI\nTITLE" */ - handle_uri (composer, urls[0], html_dnd); + composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer); - g_strfreev (urls); - success = TRUE; - break; - case DND_TYPE_TEXT_URI_LIST: - d(printf ("dropping a text/uri-list\n")); - tmp = g_strndup ((const gchar *) selection->data, selection->length); - urls = g_strsplit (tmp, "\n", 0); - g_free (tmp); + e_composer_private_init (composer); - for (i = 0; urls[i] != NULL; i++) { - str = g_strstrip (urls[i]); - if (str[0] == '#' || str[0] == '\0') - continue; + all_composers = g_slist_prepend (all_composers, composer); + html = gtkhtml_editor_get_html (GTKHTML_EDITOR (composer)); + table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table); - handle_uri (composer, str, html_dnd); - } + gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message")); + gtk_window_set_icon_name (GTK_WINDOW (composer), "mail-message-new"); - g_strfreev (urls); - success = TRUE; - break; - case DND_TYPE_TEXT_VCARD: - case DND_TYPE_TEXT_CALENDAR: - content_type = gdk_atom_name (selection->type); - d(printf ("dropping a %s\n", content_type)); + /* Drag-and-Drop Support */ - mime_part = camel_mime_part_new (); - camel_mime_part_set_content (mime_part, (const gchar *)selection->data, selection->length, content_type); - camel_mime_part_set_disposition (mime_part, "inline"); + gtk_drag_dest_set ( + GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL, + drop_types, G_N_ELEMENTS (drop_types), + GDK_ACTION_COPY | GDK_ACTION_ASK | GDK_ACTION_MOVE); - e_attachment_bar_attach_mime_part (E_ATTACHMENT_BAR (p->attachment_bar), mime_part); + g_signal_connect ( + html, "drag-data-received", + G_CALLBACK (msg_composer_drag_data_received), NULL); - camel_object_unref (mime_part); - g_free (content_type); + /* Plugin Support */ - success = TRUE; - break; - case DND_TYPE_X_UID_LIST: { - GPtrArray *uids; - char *inptr, *inend; - CamelFolder *folder; - CamelException ex = CAMEL_EXCEPTION_INITIALISER; +#if 0 /* GTKHTML-EDITOR */ + /** @HookPoint-EMMenu: Main Mail Menu + * @Id: org.gnome.evolution.mail.composer + * @Class: org.gnome.evolution.mail.bonobomenu:1.0 + * @Target: EMMenuTargetWidget + * + * The main menu of the composer window. The widget of the + * target will point to the EMsgComposer object. + */ + composer->priv->menu = em_menu_new ("org.gnome.evolution.mail.composer"); + target = em_menu_target_new_widget (p->menu, (GtkWidget *)composer); + e_menu_update_target ((EMenu *)p->menu, target); + e_menu_activate ((EMenu *)p->menu, p->uic, TRUE); - /* NB: This all runs synchronously, could be very slow/hang/block the ui */ +#endif - uids = g_ptr_array_new(); + /* Configure Headers */ - inptr = (char*)selection->data; - inend = (char*)(selection->data + selection->length); - while (inptr < inend) { - char *start = inptr; + e_composer_header_table_set_account_list ( + table, mail_config_get_accounts ()); + e_composer_header_table_set_signature_list ( + table, mail_config_get_signatures ()); - while (inptr < inend && *inptr) - inptr++; + g_signal_connect_swapped ( + table, "notify::account", + G_CALLBACK (msg_composer_account_changed_cb), composer); + g_signal_connect_swapped ( + table, "notify::destinations-bcc", + G_CALLBACK (msg_composer_notify_header_cb), composer); + g_signal_connect_swapped ( + table, "notify::destinations-cc", + G_CALLBACK (msg_composer_notify_header_cb), composer); + g_signal_connect_swapped ( + table, "notify::destinations-to", + G_CALLBACK (msg_composer_notify_header_cb), composer); + g_signal_connect_swapped ( + table, "notify::reply-to", + G_CALLBACK (msg_composer_notify_header_cb), composer); + g_signal_connect_swapped ( + table, "notify::signature", + G_CALLBACK (e_msg_composer_show_sig_file), composer); + g_signal_connect_swapped ( + table, "notify::subject", + G_CALLBACK (msg_composer_subject_changed_cb), composer); + g_signal_connect_swapped ( + table, "notify::subject", + G_CALLBACK (msg_composer_notify_header_cb), composer); - if (start > (char *)selection->data) - g_ptr_array_add(uids, g_strndup(start, inptr-start)); + msg_composer_account_changed_cb (composer); - inptr++; - } + /* Attachment Bar */ - if (uids->len > 0) { - folder = mail_tool_uri_to_folder((const gchar *)selection->data, 0, &ex); - if (folder) { - if (uids->len == 1) { - msg = camel_folder_get_message(folder, uids->pdata[0], &ex); - if (msg == NULL) - goto fail; - attach_message(composer, msg); - } else { - CamelMultipart *mp = camel_multipart_new(); - char *desc; + g_signal_connect ( + composer->priv->attachment_bar, "button_press_event", + G_CALLBACK (attachment_bar_button_press_event_cb), NULL); + g_signal_connect ( + composer->priv->attachment_bar, "key_press_event", + G_CALLBACK (attachment_bar_key_press_event_cb), NULL); + g_signal_connect ( + composer->priv->attachment_bar, "popup-menu", + G_CALLBACK (attachment_bar_popup_menu_cb), NULL); + g_signal_connect ( + composer->priv->attachment_bar, "changed", + G_CALLBACK (attachment_bar_changed_cb), composer); + g_signal_connect_after ( + composer->priv->attachment_expander, "notify::expanded", + G_CALLBACK (attachment_expander_notify_cb), composer); - camel_data_wrapper_set_mime_type((CamelDataWrapper *)mp, "multipart/digest"); - camel_multipart_set_boundary(mp, NULL); - for (i=0;ilen;i++) { - msg = camel_folder_get_message(folder, uids->pdata[i], &ex); - if (msg) { - mime_part = camel_mime_part_new(); - camel_mime_part_set_disposition(mime_part, "inline"); - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)msg); - camel_mime_part_set_content_type(mime_part, "message/rfc822"); - camel_multipart_add_part(mp, mime_part); - camel_object_unref(mime_part); - camel_object_unref(msg); - } else { - camel_object_unref(mp); - goto fail; - } - } - mime_part = camel_mime_part_new(); - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)mp); - /* translators, this count will always be >1 */ - desc = g_strdup_printf(ngettext("Attached message", "%d attached messages", uids->len), uids->len); - camel_mime_part_set_description(mime_part, desc); - g_free(desc); - e_attachment_bar_attach_mime_part(E_ATTACHMENT_BAR(p->attachment_bar), mime_part); - camel_object_unref(mime_part); - camel_object_unref(mp); - } - success = TRUE; - delete = action == GDK_ACTION_MOVE; - fail: - if (camel_exception_is_set(&ex)) { - char *name; + e_composer_autosave_register (composer); - camel_object_get(folder, NULL, CAMEL_FOLDER_NAME, &name, NULL); - e_error_run((GtkWindow *)composer, "mail-composer:attach-nomessages", - name?name:(char *)selection->data, camel_exception_get_description(&ex), NULL); - camel_object_free(folder, CAMEL_FOLDER_NAME, name); - } - camel_object_unref(folder); - } else { - e_error_run((GtkWindow *)composer, "mail-composer:attach-nomessages", - (const gchar*)selection->data, camel_exception_get_description(&ex), NULL); - } + /* Initialization may have tripped the "changed" state. */ + gtkhtml_editor_set_changed (GTKHTML_EDITOR (composer), FALSE); +} - camel_exception_clear(&ex); - } +GType +e_msg_composer_get_type (void) +{ + static GType type = 0; - g_ptr_array_free(uids, TRUE); + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMsgComposerClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) msg_composer_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMsgComposer), + 0, /* n_preallocs */ + (GInstanceInitFunc) msg_composer_init, + NULL /* value_table */ + }; - break; } - default: - d(printf ("dropping an unknown\n")); - break; + type = g_type_register_static ( + GTKHTML_TYPE_EDITOR, "EMsgComposer", &type_info, 0); } - if (e_attachment_bar_get_num_attachments(E_ATTACHMENT_BAR(p->attachment_bar))) - show_attachments (composer, TRUE); - - gtk_drag_finish(context, success, delete, time); -} - -static void -drop_popup_copy(EPopup *ep, EPopupItem *item, void *data) -{ - struct _drop_data *m = data; - drop_action(m->composer, m->context, GDK_ACTION_COPY, m->selection, m->info, m->time, FALSE); + return type; } -static void -drop_popup_move(EPopup *ep, EPopupItem *item, void *data) -{ - struct _drop_data *m = data; - drop_action(m->composer, m->context, GDK_ACTION_MOVE, m->selection, m->info, m->time, FALSE); -} +/* Callbacks. */ -static void -drop_popup_cancel(EPopup *ep, EPopupItem *item, void *data) +static EMsgComposer * +create_composer (gint visible_mask) { - struct _drop_data *m = data; - gtk_drag_finish(m->context, FALSE, FALSE, m->time); -} - -static EPopupItem drop_popup_menu[] = { - { E_POPUP_ITEM, "00.emc.02", N_("_Copy"), drop_popup_copy, NULL, "mail-copy", 0 }, - { E_POPUP_ITEM, "00.emc.03", N_("_Move"), drop_popup_move, NULL, "mail-move", 0 }, - { E_POPUP_BAR, "10.emc" }, - { E_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), drop_popup_cancel, NULL, NULL, 0 }, -}; + EMsgComposer *composer; + EMsgComposerPrivate *p; + EComposerHeaderTable *table; + GtkToggleAction *action; + gboolean active; -static void -drop_popup_free(EPopup *ep, GSList *items, void *data) -{ - struct _drop_data *m = data; + composer = g_object_new (E_TYPE_MSG_COMPOSER, NULL); + table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table); + p = composer->priv; - g_slist_free(items); + /* Configure View Menu */ - g_object_unref(m->context); - g_object_unref(m->composer); - g_free(m->selection->data); - g_free(m->selection); - g_free(m); -} + /* If we're mailing, you cannot disable "To". */ + action = GTK_TOGGLE_ACTION (ACTION (VIEW_TO)); + active = visible_mask & E_MSG_COMPOSER_VISIBLE_TO; + gtk_action_set_sensitive (ACTION (VIEW_TO), active); + gtk_toggle_action_set_active (action, active); -static void -drag_data_received (GtkWidget *w, GdkDragContext *context, - int x, int y, GtkSelectionData *selection, - guint info, guint time, - EMsgComposer *composer) -{ - if (selection->data == NULL || selection->length == -1) - return; + /* Ditto for "Post-To". */ + action = GTK_TOGGLE_ACTION (ACTION (VIEW_POST_TO)); + active = visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO; + gtk_action_set_sensitive (ACTION (VIEW_POST_TO), active); + gtk_toggle_action_set_active (action, active); - if (context->action == GDK_ACTION_ASK) { - EMPopup *emp; - GSList *menus = NULL; - GtkMenu *menu; - int i; - struct _drop_data *m; + /* Disable "Cc" if we're posting. */ + if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_CC)) { + action = GTK_TOGGLE_ACTION (ACTION (VIEW_CC)); + gtk_toggle_action_set_active (action, FALSE); + } - m = g_malloc0(sizeof(*m)); - m->context = context; - g_object_ref(context); - m->composer = composer; - g_object_ref(composer); - m->action = context->action; - m->info = info; - m->time = time; - m->selection = g_malloc0(sizeof(*m->selection)); - m->selection->data = g_malloc(selection->length); - memcpy(m->selection->data, selection->data, selection->length); - m->selection->length = selection->length; + /* Disable "Bcc" if we're posting. */ + if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_BCC)) { + action = GTK_TOGGLE_ACTION (ACTION (VIEW_BCC)); + gtk_toggle_action_set_active (action, FALSE); + } - emp = em_popup_new("org.gnome.evolution.mail.composer.popup.drop"); - for (i=0;iaction, selection, info, time, w != GTK_WIDGET (composer)); - } + return composer; } +/** + * e_msg_composer_new_with_type: + * + * Create a new message composer widget. The type can be + * E_MSG_COMPOSER_MAIL, E_MSG_COMPOSER_POST or E_MSG_COMPOSER_MAIL_POST. + * + * Returns: A pointer to the newly created widget + **/ -static gboolean -drag_motion(GObject *o, GdkDragContext *context, gint x, gint y, guint time, EMsgComposer *composer) +EMsgComposer * +e_msg_composer_new_with_type (int type) { - GList *targets; - GdkDragAction action, actions = 0; + EMsgComposer *composer; + gint visible_mask; + + switch (type) { + case E_MSG_COMPOSER_MAIL: + visible_mask = E_MSG_COMPOSER_VISIBLE_MASK_MAIL; + break; - for (targets = context->targets; targets; targets = targets->next) { - int i; + case E_MSG_COMPOSER_POST: + visible_mask = E_MSG_COMPOSER_VISIBLE_MASK_POST; + break; - for (i=0;idata == (void *)drag_info[i].atom) - actions |= drag_info[i].actions; + default: + visible_mask = + E_MSG_COMPOSER_VISIBLE_MASK_MAIL | + E_MSG_COMPOSER_VISIBLE_MASK_POST; + break; } - actions &= context->actions; - action = context->suggested_action; - /* we default to copy */ - if (action == GDK_ACTION_ASK && (actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) != (GDK_ACTION_MOVE|GDK_ACTION_COPY)) - action = GDK_ACTION_COPY; + composer = create_composer (visible_mask); - gdk_drag_status(context, action, time); + set_editor_text (composer, "", TRUE); - return action != 0; + return composer; } -static void -class_init (EMsgComposerClass *klass) +/** + * e_msg_composer_new: + * + * Create a new message composer widget. + * + * Returns: A pointer to the newly created widget + **/ +EMsgComposer * +e_msg_composer_new (void) { - GtkObjectClass *object_class; - GtkWidgetClass *widget_class; - GObjectClass *gobject_class; - int i; - - for (i=0;ifinalize = composer_finalise; - gobject_class->dispose = composer_dispose; - object_class->destroy = destroy; - widget_class->delete_event = delete_event; - - parent_class = g_type_class_ref(bonobo_window_get_type ()); - - signals[SEND] = - g_signal_new ("send", - E_TYPE_MSG_COMPOSER, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EMsgComposerClass, send), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[SAVE_DRAFT] = - g_signal_new ("save-draft", - E_TYPE_MSG_COMPOSER, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EMsgComposerClass, save_draft), - NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, - 1, G_TYPE_BOOLEAN); + return e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL); } -static void -init (EMsgComposer *composer) +static gboolean +is_special_header (const gchar *hdr_name) { - EMsgComposerPrivate *p = g_new0(EMsgComposerPrivate,1); - GHashTable *inline_images; - GHashTable *inline_images_by_url; - - inline_images = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); - - inline_images_by_url = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) camel_object_unref); - - p->uic = NULL; - - p->hdrs = NULL; - p->extra_hdr_names = g_ptr_array_new (); - p->extra_hdr_values = g_ptr_array_new (); - - p->focused_entry = NULL; - - p->eeditor = NULL; - - p->address_dialog = NULL; - - p->attachment_bar = NULL; - p->attachment_scrolled_window = NULL; - - p->persist_file_interface = CORBA_OBJECT_NIL; - p->persist_stream_interface = CORBA_OBJECT_NIL; - - p->eeditor_engine = CORBA_OBJECT_NIL; - p->inline_images = inline_images; - p->inline_images_by_url = inline_images_by_url; - p->current_images = NULL; - - p->attachment_bar_visible = FALSE; - p->send_html = FALSE; - p->pgp_sign = FALSE; - p->pgp_encrypt = FALSE; - p->smime_sign = FALSE; - p->smime_encrypt = FALSE; - - p->has_changed = FALSE; - p->autosaved = FALSE; - - p->redirect = NULL; - p->send_invoked = FALSE; - p->charset = NULL; + /* Note: a header is a "special header" if it has any meaning: + 1. it's not a X-* header or + 2. it's an X-Evolution* header + */ + if (g_ascii_strncasecmp (hdr_name, "X-", 2)) + return TRUE; - p->enable_autosave = TRUE; - p->autosave_file = NULL; - p->autosave_fd = -1; + if (!g_ascii_strncasecmp (hdr_name, "X-Evolution", 11)) + return TRUE; - /** @HookPoint-EMMenu: Main Mail Menu - * @Id: org.gnome.evolution.mail.composer - * @Class: org.gnome.evolution.mail.bonobomenu:1.0 - * @Target: EMMenuTargetWidget - * - * The main menu of the composer window. The widget of the - * target will point to the EMsgComposer object. - */ - p->menu = em_menu_new("org.gnome.evolution.mail.composer"); + /* we can keep all other X-* headers */ - composer->priv = p; + return FALSE; } - -GtkType -e_msg_composer_get_type (void) +static void +e_msg_composer_set_pending_body (EMsgComposer *composer, + gchar *text, + gssize length) { - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof (EMsgComposerClass), - NULL, NULL, - (GClassInitFunc) class_init, - NULL, NULL, - sizeof (EMsgComposer), - 0, - (GInstanceInitFunc) init, - }; - - type = g_type_register_static (bonobo_window_get_type (), "EMsgComposer", &info, 0); - } + g_object_set_data_full ( + G_OBJECT (composer), "body:text", + text, (GDestroyNotify) g_free); - return type; + g_object_set_data ( + G_OBJECT (composer), "body:length", + GSIZE_TO_POINTER (length)); } static void -e_msg_composer_load_config (EMsgComposer *composer, int visible_mask) +e_msg_composer_flush_pending_body (EMsgComposer *composer) { - GConfClient *gconf; - EMsgComposerPrivate *p = composer->priv; - - gconf = gconf_client_get_default (); - - p->view_from = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/From", NULL); - p->view_replyto = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/ReplyTo", NULL); - p->view_to = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/To", NULL); - p->view_postto = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/PostTo", NULL); - p->view_cc = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/Cc", NULL); - p->view_bcc = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/Bcc", NULL); - p->view_subject = gconf_client_get_bool ( - gconf, "/apps/evolution/mail/composer/view/Subject", NULL); - - /* if we're mailing, you cannot disable to so it should appear checked */ - if (visible_mask & E_MSG_COMPOSER_VISIBLE_TO) - p->view_to = TRUE; - else - p->view_to = FALSE; - - /* ditto for post-to */ - if (visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO) - p->view_postto = TRUE; - else - p->view_postto = FALSE; + const gchar *body; + gpointer data; + gssize length; - /* we set these to false initially if we're posting */ - if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_CC)) - p->view_cc = FALSE; + body = g_object_get_data (G_OBJECT (composer), "body:text"); + data = g_object_get_data (G_OBJECT (composer), "body:length"); + length = GPOINTER_TO_SIZE (data); - if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_BCC)) - p->view_bcc = FALSE; + if (body != NULL) + set_editor_text (composer, body, FALSE); - g_object_unref (gconf); + g_object_set_data (G_OBJECT (composer), "body:text", NULL); } -static int -e_msg_composer_get_visible_flags (EMsgComposer *composer) +static void +add_attachments_handle_mime_part (EMsgComposer *composer, + CamelMimePart *mime_part, + gboolean just_inlines, + gboolean related, + gint depth) { - int flags = 0; - EMsgComposerPrivate *p = composer->priv; - - if (p->view_from) - flags |= E_MSG_COMPOSER_VISIBLE_FROM; - if (p->view_replyto) - flags |= E_MSG_COMPOSER_VISIBLE_REPLYTO; - if (p->view_to) - flags |= E_MSG_COMPOSER_VISIBLE_TO; - if (p->view_postto) - flags |= E_MSG_COMPOSER_VISIBLE_POSTTO; - if (p->view_cc) - flags |= E_MSG_COMPOSER_VISIBLE_CC; - if (p->view_bcc) - flags |= E_MSG_COMPOSER_VISIBLE_BCC; - if (p->view_subject) - flags |= E_MSG_COMPOSER_VISIBLE_SUBJECT; + CamelContentType *content_type; + CamelDataWrapper *wrapper; - /* - * Until we have a GUI way, lets make sure that - * even if the user screws up, we will do the right - * thing (screws up == edit the config file manually - * and screw up). - */ - flags |= E_MSG_COMPOSER_VISIBLE_SUBJECT; - return flags; -} + if (!mime_part) + return; + content_type = camel_mime_part_get_content_type (mime_part); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); -static void -map_default_cb (EMsgComposer *composer, gpointer user_data) -{ - GtkWidget *widget; - CORBA_Environment ev; - const char *subject; - const char *text; - EMsgComposerPrivate *p = composer->priv; + if (CAMEL_IS_MULTIPART (wrapper)) { + /* another layer of multipartness... */ + add_attachments_from_multipart ( + composer, (CamelMultipart *) wrapper, + just_inlines, depth + 1); + } else if (just_inlines) { + if (camel_mime_part_get_content_id (mime_part) || + camel_mime_part_get_content_location (mime_part)) + e_msg_composer_add_inline_image_from_mime_part ( + composer, mime_part); + } else if (CAMEL_IS_MIME_MESSAGE (wrapper)) { + /* do nothing */ + } else if (related && camel_content_type_is (content_type, "image", "*")) { + e_msg_composer_add_inline_image_from_mime_part (composer, mime_part); + } else if (camel_content_type_is (content_type, "text", "*")) { + /* do nothing */ + } else { + e_msg_composer_attach (composer, mime_part); + } +} - /* If the 'To:' field is empty, focus it */ +static void +add_attachments_from_multipart (EMsgComposer *composer, + CamelMultipart *multipart, + gboolean just_inlines, + gint depth) +{ + /* find appropriate message attachments to add to the composer */ + CamelMimePart *mime_part; + gboolean related; + gint i, nparts; - widget = e_msg_composer_hdrs_get_to_entry (E_MSG_COMPOSER_HDRS (p->hdrs)); - text = gtk_entry_get_text (GTK_ENTRY (widget)); + related = camel_content_type_is ( + CAMEL_DATA_WRAPPER (multipart)->mime_type, + "multipart", "related"); - if (!text || text[0] == '\0') { - gtk_widget_grab_focus (widget); + if (CAMEL_IS_MULTIPART_SIGNED (multipart)) { + mime_part = camel_multipart_get_part ( + multipart, CAMEL_MULTIPART_SIGNED_CONTENT); + add_attachments_handle_mime_part ( + composer, mime_part, just_inlines, related, depth); + } else if (CAMEL_IS_MULTIPART_ENCRYPTED (multipart)) { + /* XXX What should we do in this case? */ + } else { + nparts = camel_multipart_get_number (multipart); - return; + for (i = 0; i < nparts; i++) { + mime_part = camel_multipart_get_part (multipart, i); + add_attachments_handle_mime_part ( + composer, mime_part, just_inlines, + related, depth); + } } +} - /* If not, check the subject field */ - - subject = e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (p->hdrs)); +/** + * e_msg_composer_add_message_attachments: + * @composer: the composer to add the attachments to. + * @message: the source message to copy the attachments from. + * @just_inlines: whether to attach all attachments or just add + * inline images. + * + * Walk through all the mime parts in @message and add them to the composer + * specified in @composer. + */ +void +e_msg_composer_add_message_attachments (EMsgComposer *composer, + CamelMimeMessage *message, + gboolean just_inlines) +{ + CamelDataWrapper *wrapper; - if (!subject || subject[0] == '\0') { - widget = e_msg_composer_hdrs_get_subject_entry (E_MSG_COMPOSER_HDRS (p->hdrs)); - gtk_widget_grab_focus (widget); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message)); + if (!CAMEL_IS_MULTIPART (wrapper)) return; - } - - /* Jump to the editor as a last resort. */ - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "grab-focus", &ev); - CORBA_exception_free (&ev); + add_attachments_from_multipart ( + composer, (CamelMultipart *) wrapper, just_inlines, 0); } static void -msg_composer_destroy_notify (void *data) +handle_multipart_signed (EMsgComposer *composer, + CamelMultipart *multipart, + gint depth) { - EMsgComposer *composer = E_MSG_COMPOSER (data); + CamelContentType *content_type; + CamelDataWrapper *content; + CamelMimePart *mime_part; + GtkToggleAction *action; - all_composers = g_slist_remove (all_composers, composer); -} + /* FIXME: make sure this isn't an s/mime signed part?? */ + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, TRUE); -static int -composer_key_pressed (EMsgComposer *composer, GdkEventKey *event, void *user_data) -{ - GtkWidget *widget; - EMsgComposerPrivate *p = composer->priv; - widget = e_msg_composer_hdrs_get_subject_entry (E_MSG_COMPOSER_HDRS (p->hdrs)); + mime_part = camel_multipart_get_part ( + multipart, CAMEL_MULTIPART_SIGNED_CONTENT); -#ifdef HAVE_XFREE - if (event->keyval == XF86XK_Send) { - g_signal_emit (G_OBJECT (composer), signals[SEND], 0); - g_signal_stop_emission_by_name (composer, "key-press-event"); - return TRUE; - } -#endif /* HAVE_XFREE */ + if (mime_part != NULL) + return; - if (event->keyval == GDK_Escape) { - do_exit (composer); - g_signal_stop_emission_by_name (composer, "key-press-event"); - return TRUE; - } else if ((event->keyval == GDK_Tab) && (gtk_widget_is_focus(widget))) { - CORBA_Environment ev; - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "grab-focus", &ev); - CORBA_exception_free (&ev); - g_signal_stop_emission_by_name (composer, "key-press-event"); - return TRUE; - } - return FALSE; -} + content_type = camel_mime_part_get_content_type (mime_part); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); -/* Verbs for non-control entries */ -static BonoboUIVerb entry_verbs [] = { - BONOBO_UI_VERB ("EditCut", menu_edit_cut_cb), - BONOBO_UI_VERB ("EditCopy", menu_edit_copy_cb), - BONOBO_UI_VERB ("EditPaste", menu_edit_paste_cb), - BONOBO_UI_VERB ("EditSelectAll", menu_edit_select_all_cb), - BONOBO_UI_VERB_END -}; + if (CAMEL_IS_MULTIPART (content)) { + multipart = CAMEL_MULTIPART (content); -/* All this snot is so that Cut/Copy/Paste work. */ -static gboolean -composer_entry_focus_in_event_cb (GtkWidget *widget, GdkEventFocus *event, gpointer user_data) -{ - EMsgComposer *composer = user_data; - EMsgComposerPrivate *p = composer->priv; - BonoboUIContainer *container; - char *xmlfile; + /* Note: depth is preserved here because we're not + counting multipart/signed as a multipart, instead + we want to treat the content part as our mime part + here. */ + + if (CAMEL_IS_MULTIPART_SIGNED (content)) { + /* handle the signed content and configure the composer to sign outgoing messages */ + handle_multipart_signed (composer, multipart, depth); + } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { + /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ + handle_multipart_encrypted (composer, mime_part, depth); + } else if (camel_content_type_is (content_type, "multipart", "alternative")) { + /* this contains the text/plain and text/html versions of the message body */ + handle_multipart_alternative (composer, multipart, depth); + } else { + /* there must be attachments... */ + handle_multipart (composer, multipart, depth); + } + } else if (camel_content_type_is (content_type, "text", "*")) { + gchar *html; + gssize length; - p->focused_entry = widget; + html = em_utils_part_to_html (mime_part, &length, NULL); + e_msg_composer_set_pending_body (composer, html, length); + } else { + e_msg_composer_attach (composer, mime_part); + } +} - container = bonobo_window_get_ui_container (BONOBO_WINDOW (composer)); - bonobo_ui_component_set_container (p->entry_uic, bonobo_object_corba_objref (BONOBO_OBJECT (container)), NULL); +static void +handle_multipart_encrypted (EMsgComposer *composer, + CamelMimePart *multipart, + gint depth) +{ + CamelContentType *content_type; + CamelCipherContext *cipher; + CamelDataWrapper *content; + CamelMimePart *mime_part; + CamelException ex; + CamelCipherValidity *valid; + GtkToggleAction *action; - bonobo_ui_component_add_verb_list_with_data (p->entry_uic, entry_verbs, composer); + /* FIXME: make sure this is a PGP/MIME encrypted part?? */ + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + gtk_toggle_action_set_active (action, TRUE); - bonobo_ui_component_freeze (p->entry_uic, NULL); + camel_exception_init (&ex); + cipher = mail_crypto_get_pgp_cipher_context (NULL); + mime_part = camel_mime_part_new (); + valid = camel_cipher_decrypt (cipher, multipart, mime_part, &ex); + camel_object_unref (cipher); + camel_exception_clear (&ex); + if (valid == NULL) + return; + camel_cipher_validity_free (valid); - xmlfile = g_build_filename (EVOLUTION_UIDIR, "evolution-composer-entries.xml", NULL); - bonobo_ui_util_set_ui (p->entry_uic, PREFIX, - xmlfile, - "evolution-composer-entries", NULL); - g_free (xmlfile); + content_type = camel_mime_part_get_content_type (mime_part); - bonobo_ui_component_thaw (p->entry_uic, NULL); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - return FALSE; -} + if (CAMEL_IS_MULTIPART (content)) { + CamelMultipart *content_multipart = CAMEL_MULTIPART (content); -static gboolean -composer_entry_focus_out_event_cb (GtkWidget *widget, GdkEventFocus *event, gpointer user_data) -{ - EMsgComposer *composer = user_data; - EMsgComposerPrivate *p = composer->priv; + /* Note: depth is preserved here because we're not + counting multipart/encrypted as a multipart, instead + we want to treat the content part as our mime part + here. */ - g_return_val_if_fail (p->focused_entry == widget, FALSE); - p->focused_entry = NULL; + if (CAMEL_IS_MULTIPART_SIGNED (content)) { + /* handle the signed content and configure the composer to sign outgoing messages */ + handle_multipart_signed (composer, content_multipart, depth); + } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { + /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ + handle_multipart_encrypted (composer, mime_part, depth); + } else if (camel_content_type_is (content_type, "multipart", "alternative")) { + /* this contains the text/plain and text/html versions of the message body */ + handle_multipart_alternative (composer, content_multipart, depth); + } else { + /* there must be attachments... */ + handle_multipart (composer, content_multipart, depth); + } + } else if (camel_content_type_is (content_type, "text", "*")) { + gchar *html; + gssize length; - bonobo_ui_component_unset_container (p->entry_uic, NULL); + html = em_utils_part_to_html (mime_part, &length, NULL); + e_msg_composer_set_pending_body (composer, html, length); + } else { + e_msg_composer_attach (composer, mime_part); + } - return FALSE; + camel_object_unref (mime_part); } static void -setup_cut_copy_paste (EMsgComposer *composer) +handle_multipart_alternative (EMsgComposer *composer, + CamelMultipart *multipart, + gint depth) { - EMsgComposerHdrs *hdrs; - EMsgComposerPrivate *p = composer->priv; - GtkWidget *entry; + /* Find the text/html part and set the composer body to it's contents */ + CamelMimePart *text_part = NULL; + gint i, nparts; + + nparts = camel_multipart_get_number (multipart); + + for (i = 0; i < nparts; i++) { + CamelContentType *content_type; + CamelDataWrapper *content; + CamelMimePart *mime_part; + + mime_part = camel_multipart_get_part (multipart, i); - hdrs = (EMsgComposerHdrs *) p->hdrs; + if (!mime_part) + continue; - entry = e_msg_composer_hdrs_get_subject_entry (hdrs); - g_signal_connect (entry, "focus_in_event", G_CALLBACK (composer_entry_focus_in_event_cb), composer); - g_signal_connect (entry, "focus_out_event", G_CALLBACK (composer_entry_focus_out_event_cb), composer); + content_type = camel_mime_part_get_content_type (mime_part); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - entry = e_msg_composer_hdrs_get_reply_to_entry (hdrs); - g_signal_connect (entry, "focus_in_event", G_CALLBACK (composer_entry_focus_in_event_cb), composer); - g_signal_connect (entry, "focus_out_event", G_CALLBACK (composer_entry_focus_out_event_cb), composer); + if (CAMEL_IS_MULTIPART (content)) { + CamelMultipart *mp; - entry = e_msg_composer_hdrs_get_to_entry (hdrs); - g_signal_connect (entry, "focus_in_event", G_CALLBACK (composer_entry_focus_in_event_cb), composer); - g_signal_connect (entry, "focus_out_event", G_CALLBACK (composer_entry_focus_out_event_cb), composer); + mp = CAMEL_MULTIPART (content); - entry = e_msg_composer_hdrs_get_cc_entry (hdrs); - g_signal_connect (entry, "focus_in_event", G_CALLBACK (composer_entry_focus_in_event_cb), composer); - g_signal_connect (entry, "focus_out_event", G_CALLBACK (composer_entry_focus_out_event_cb), composer); + if (CAMEL_IS_MULTIPART_SIGNED (content)) { + /* handle the signed content and configure the composer to sign outgoing messages */ + handle_multipart_signed (composer, mp, depth + 1); + } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { + /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ + handle_multipart_encrypted (composer, mime_part, depth + 1); + } else { + /* depth doesn't matter so long as we don't pass 0 */ + handle_multipart (composer, mp, depth + 1); + } + } else if (camel_content_type_is (content_type, "text", "html")) { + /* text/html is preferable, so once we find it we're done... */ + text_part = mime_part; + break; + } else if (camel_content_type_is (content_type, "text", "*")) { + /* anyt text part not text/html is second rate so the first + text part we find isn't necessarily the one we'll use. */ + if (!text_part) + text_part = mime_part; + } else { + e_msg_composer_attach (composer, mime_part); + } + } - entry = e_msg_composer_hdrs_get_bcc_entry (hdrs); - g_signal_connect (entry, "focus_in_event", G_CALLBACK (composer_entry_focus_in_event_cb), composer); - g_signal_connect (entry, "focus_out_event", G_CALLBACK (composer_entry_focus_out_event_cb), composer); + if (text_part) { + gchar *html; + gssize length; - entry = e_msg_composer_hdrs_get_post_to_label (hdrs); - g_signal_connect (entry, "focus_in_event", G_CALLBACK (composer_entry_focus_in_event_cb), composer); - g_signal_connect (entry, "focus_out_event", G_CALLBACK (composer_entry_focus_out_event_cb), composer); + html = em_utils_part_to_html (text_part, &length, NULL); + e_msg_composer_set_pending_body (composer, html, length); + } } static void -composer_settings_update (GConfClient *gconf, guint cnxn_id, GConfEntry *entry, gpointer data) +handle_multipart (EMsgComposer *composer, + CamelMultipart *multipart, + gint depth) { - gboolean bool; - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + gint i, nparts; - bool = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/magic_smileys", NULL); - bonobo_widget_set_property (BONOBO_WIDGET (p->eeditor), - "MagicSmileys", TC_CORBA_boolean, bool, - NULL); + nparts = camel_multipart_get_number (multipart); - bool = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/magic_links", NULL); - bonobo_widget_set_property (BONOBO_WIDGET (p->eeditor), - "MagicLinks", TC_CORBA_boolean, bool, - NULL); + for (i = 0; i < nparts; i++) { + CamelContentType *content_type; + CamelDataWrapper *content; + CamelMimePart *mime_part; - bool = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/inline_spelling", NULL); - bonobo_widget_set_property (BONOBO_WIDGET (p->eeditor), - "InlineSpelling", TC_CORBA_boolean, bool, - NULL); -} + mime_part = camel_multipart_get_part (multipart, i); -static void -e_msg_composer_unrealize (GtkWidget *widget, gpointer data) -{ - EMsgComposer *composer = E_MSG_COMPOSER (widget); - GConfClient *gconf; - GError *error = NULL; - int width, height; + if (!mime_part) + continue; - gtk_window_get_size (GTK_WINDOW (composer), &width, &height); + content_type = camel_mime_part_get_content_type (mime_part); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - gconf = gconf_client_get_default (); - if (!gconf_client_set_int (gconf, "/apps/evolution/mail/composer/width", width, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_clear_error(&error); - } - if (!gconf_client_set_int (gconf, "/apps/evolution/mail/composer/height", height, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - g_object_unref (gconf); -} + if (CAMEL_IS_MULTIPART (content)) { + CamelMultipart *mp; -/* Callbacks. */ + mp = CAMEL_MULTIPART (content); -static void -emcab_add(EPopup *ep, EPopupItem *item, void *data) -{ - EAttachmentBar *bar = data; - EMsgComposer *toplevel = E_MSG_COMPOSER (gtk_widget_get_toplevel (GTK_WIDGET (bar))); - GtkWidget **attachment_selector = e_attachment_bar_get_selector(E_ATTACHMENT_BAR(bar)); + if (CAMEL_IS_MULTIPART_SIGNED (content)) { + /* handle the signed content and configure the composer to sign outgoing messages */ + handle_multipart_signed (composer, mp, depth + 1); + } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { + /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ + handle_multipart_encrypted (composer, mime_part, depth + 1); + } else if (camel_content_type_is (content_type, "multipart", "alternative")) { + handle_multipart_alternative (composer, mp, depth + 1); + } else { + /* depth doesn't matter so long as we don't pass 0 */ + handle_multipart (composer, mp, depth + 1); + } + } else if (depth == 0 && i == 0) { + gchar *html; + gssize length; - e_msg_composer_select_file_attachments (toplevel, attachment_selector, add_to_bar); + /* Since the first part is not multipart/alternative, + * this must be the body. */ + html = em_utils_part_to_html (mime_part, &length, NULL); + e_msg_composer_set_pending_body (composer, html, length); + } else if (camel_mime_part_get_content_id (mime_part) || + camel_mime_part_get_content_location (mime_part)) { + /* special in-line attachment */ + e_msg_composer_add_inline_image_from_mime_part (composer, mime_part); + } else { + /* normal attachment */ + e_msg_composer_attach (composer, mime_part); + } + } } static void -emcab_properties(EPopup *ep, EPopupItem *item, void *data) +set_signature_gui (EMsgComposer *composer) { - EAttachmentBar *bar = data; + GtkhtmlEditor *editor; + EComposerHeaderTable *table; + ESignature *signature = NULL; + const gchar *data; + gchar *decoded; - e_attachment_bar_edit_selected(bar); -} + editor = GTKHTML_EDITOR (composer); + table = e_msg_composer_get_header_table (composer); -static void -emcab_remove(EPopup *ep, EPopupItem *item, void *data) -{ - EAttachmentBar *bar = data; + if (!gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1")) + return; + + data = gtkhtml_editor_get_paragraph_data (editor, "signature_name"); + if (g_str_has_prefix (data, "uid:")) { + decoded = decode_signature_name (data + 4); + signature = mail_config_get_signature_by_uid (decoded); + g_free (decoded); + } else if (g_str_has_prefix (data, "name:")) { + decoded = decode_signature_name (data + 5); + signature = mail_config_get_signature_by_name (decoded); + g_free (decoded); + } - e_attachment_bar_remove_selected(bar); + e_composer_header_table_set_signature (table, signature); } -/* Popup menu handling. */ -static EPopupItem emcab_popups[] = { - { E_POPUP_ITEM, "10.attach", N_("_Remove"), emcab_remove, NULL, GTK_STOCK_REMOVE, EM_POPUP_ATTACHMENTS_MANY }, - { E_POPUP_ITEM, "20.attach", N_("_Properties"), emcab_properties, NULL, GTK_STOCK_PROPERTIES, EM_POPUP_ATTACHMENTS_ONE }, - { E_POPUP_BAR, "30.attach.00", NULL, NULL, NULL, NULL, EM_POPUP_ATTACHMENTS_MANY|EM_POPUP_ATTACHMENTS_ONE }, - { E_POPUP_ITEM, "30.attach.01", N_("_Add attachment..."), emcab_add, NULL, GTK_STOCK_ADD, 0 }, -}; - -static void -emcab_popup_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data) -{ - EAttachmentBar *bar = user_data; - GnomeIconList *icon_list = user_data; - GList *selection; - GnomeCanvasPixbuf *image; - - gdk_window_get_origin (((GtkWidget*) bar)->window, x, y); - - selection = gnome_icon_list_get_selection (icon_list); - if (selection == NULL) - return; - - image = gnome_icon_list_get_icon_pixbuf_item (icon_list, GPOINTER_TO_INT(selection->data)); - if (image == NULL) - return; - - /* Put menu to the center of icon. */ - *x += (int)(image->item.x1 + image->item.x2) / 2; - *y += (int)(image->item.y1 + image->item.y2) / 2; -} - -static void -emcab_popups_free(EPopup *ep, GSList *l, void *data) -{ - g_slist_free(l); -} - -/* if id != -1, then use it as an index for target of the popup */ - -static void -emcab_popup(EAttachmentBar *bar, GdkEventButton *event, int id) -{ - GSList *attachments = NULL, *menus = NULL; - int i; - EMPopup *emp; - EMPopupTargetAttachments *t; - GtkMenu *menu; - - attachments = e_attachment_bar_get_attachment(bar, id); - - for (i=0;itarget.widget = (GtkWidget *)bar; - menu = e_popup_create_menu_once((EPopup *)emp, (EPopupTarget *)t, 0); - - if (event == NULL) - gtk_menu_popup(menu, NULL, NULL, emcab_popup_position, bar, 0, gtk_get_current_event_time()); - else - gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time); -} - -static gboolean -popup_menu_event (GtkWidget *widget) -{ - emcab_popup((EAttachmentBar *)widget, NULL, -1); - return TRUE; -} - - -static int -button_press_event (GtkWidget *widget, GdkEventButton *event) -{ - EAttachmentBar *bar = (EAttachmentBar *)widget; - GnomeIconList *icon_list = GNOME_ICON_LIST(widget); - int icon_number; - - if (event->button != 3) - return FALSE; - - icon_number = gnome_icon_list_get_icon_at (icon_list, event->x, event->y); - if (icon_number >= 0) { - gnome_icon_list_unselect_all(icon_list); - gnome_icon_list_select_icon (icon_list, icon_number); - } - - emcab_popup(bar, event, icon_number); - - return TRUE; -} - -static gint -key_press_event(GtkWidget *widget, GdkEventKey *event) -{ - EAttachmentBar *bar = E_ATTACHMENT_BAR(widget); - - if (event->keyval == GDK_Delete) { - e_attachment_bar_remove_selected (bar); - return TRUE; - } - - return FALSE; -} - -static EMsgComposer * -create_composer (int visible_mask) +/** + * e_msg_composer_new_with_message: + * @message: The message to use as the source + * + * Create a new message composer widget. + * + * Note: Designed to work only for messages constructed using Evolution. + * + * Returns: A pointer to the newly created widget + **/ +EMsgComposer * +e_msg_composer_new_with_message (CamelMimeMessage *message) { + const CamelInternetAddress *to, *cc, *bcc; + GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL; + const gchar *format, *subject; + EDestination **Tov, **Ccv, **Bccv; + GHashTable *auto_cc, *auto_bcc; + CamelContentType *content_type; + struct _camel_header_raw *headers; + CamelDataWrapper *content; + EAccount *account = NULL; + gchar *account_name; EMsgComposer *composer; - GtkWidget *vbox, *expander_hbox; - Bonobo_Unknown editor_server; - CORBA_Environment ev; - GConfClient *gconf; - int vis; - GList *icon_list; - BonoboControlFrame *control_frame; - GtkWidget *html_widget = NULL; - gpointer servant; - BonoboObject *impl; + EComposerHeaderTable *table; + GtkToggleAction *action; + struct _camel_header_raw *xev; + gint len, i; EMsgComposerPrivate *p; - composer = g_object_new (E_TYPE_MSG_COMPOSER, "win_name", _("Compose Message"), NULL); - p = composer->priv; - - gtk_window_set_title ((GtkWindow *) composer, _("Compose Message")); - - all_composers = g_slist_prepend (all_composers, composer); - - g_signal_connect (composer, "key-press-event", - G_CALLBACK (composer_key_pressed), - NULL); - - g_signal_connect (composer, "destroy", - G_CALLBACK (msg_composer_destroy_notify), - NULL); - - icon_list = e_icon_factory_get_icon_list ("mail-message-new"); - if (icon_list) { - gtk_window_set_icon_list (GTK_WINDOW (composer), icon_list); - g_list_foreach (icon_list, (GFunc) g_object_unref, NULL); - g_list_free (icon_list); - } - - /* DND support */ - gtk_drag_dest_set (GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL, drop_types, num_drop_types, GDK_ACTION_COPY|GDK_ACTION_ASK|GDK_ACTION_MOVE); - g_signal_connect(composer, "drag_data_received", G_CALLBACK (drag_data_received), composer); - g_signal_connect(composer, "drag-motion", G_CALLBACK(drag_motion), composer); - e_msg_composer_load_config (composer, visible_mask); - - setup_ui (composer); - - vbox = gtk_vbox_new (FALSE, 0); - - vis = e_msg_composer_get_visible_flags (composer); - p->hdrs = e_msg_composer_hdrs_new (p->uic, visible_mask, vis); - if (!p->hdrs) { - e_error_run (GTK_WINDOW (composer), "mail-composer:no-address-control", NULL); - gtk_object_destroy (GTK_OBJECT (composer)); - return NULL; - } - - gtk_box_set_spacing (GTK_BOX (vbox), 6); - gtk_box_pack_start (GTK_BOX (vbox), p->hdrs, FALSE, FALSE, 0); - g_signal_connect (p->hdrs, "subject_changed", - G_CALLBACK (subject_changed_cb), composer); - g_signal_connect (p->hdrs, "hdrs_changed", - G_CALLBACK (hdrs_changed_cb), composer); - g_signal_connect (p->hdrs, "from_changed", - G_CALLBACK (from_changed_cb), composer); - g_signal_connect_swapped ( - p->hdrs, "signature_changed", - G_CALLBACK (e_msg_composer_show_sig_file), composer); - gtk_widget_show (p->hdrs); - - from_changed_cb((EMsgComposerHdrs *)p->hdrs, composer); - - /* Editor component. */ - p->eeditor = bonobo_widget_new_control ( - GNOME_GTKHTML_EDITOR_CONTROL_ID, - bonobo_ui_component_get_container (p->uic)); - if (!p->eeditor) { - e_error_run (GTK_WINDOW (composer), "mail-composer:no-editor-control", NULL); - gtk_object_destroy (GTK_OBJECT (composer)); - return NULL; + for (headers = CAMEL_MIME_PART (message)->headers;headers;headers = headers->next) { + if (!strcmp (headers->name, "X-Evolution-PostTo")) + postto = g_list_append (postto, g_strstrip (g_strdup (headers->value))); } - control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (p->eeditor)); - bonobo_control_frame_set_autoactivate (control_frame, TRUE); - - /* let the editor know which mode we are in */ - bonobo_widget_set_property (BONOBO_WIDGET (p->eeditor), - "FormatHTML", TC_CORBA_boolean, p->send_html, - NULL); - - gconf = gconf_client_get_default (); - composer_settings_update (gconf, 0, NULL, composer); - e_msg_composer_set_request_receipt (composer, gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/request_receipt", NULL)); - gconf_client_add_dir (gconf, "/apps/evolution/mail/composer", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); - p->notify_id = gconf_client_notify_add (gconf, "/apps/evolution/mail/composer", - composer_settings_update, composer, NULL, NULL); - gtk_window_set_default_size (GTK_WINDOW (composer), - gconf_client_get_int (gconf, "/apps/evolution/mail/composer/width", NULL), - gconf_client_get_int (gconf, "/apps/evolution/mail/composer/height", NULL)); - g_signal_connect (composer, "unrealize", G_CALLBACK (e_msg_composer_unrealize), NULL); - g_object_unref (gconf); + if (postto != NULL) + composer = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_POST); + else + composer = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL); + p = composer->priv; - editor_server = bonobo_widget_get_objref (BONOBO_WIDGET (p->eeditor)); - - /* FIXME: handle exceptions */ - CORBA_exception_init (&ev); - p->persist_file_interface - = Bonobo_Unknown_queryInterface (editor_server, "IDL:Bonobo/PersistFile:1.0", &ev); - p->persist_stream_interface - = Bonobo_Unknown_queryInterface (editor_server, "IDL:Bonobo/PersistStream:1.0", &ev); - CORBA_exception_free (&ev); - - gtk_box_pack_start (GTK_BOX (vbox), p->eeditor, TRUE, TRUE, 0); - - /* Attachment editor, wrapped into an EScrollFrame. It's - hidden in an EExpander. */ - - p->attachment_scrolled_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (p->attachment_scrolled_window), - GTK_SHADOW_IN); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (p->attachment_scrolled_window), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - p->attachment_bar = e_attachment_bar_new (NULL); - - g_signal_connect (p->attachment_bar, "button_press_event", G_CALLBACK (button_press_event), NULL); - g_signal_connect (p->attachment_bar, "key_press_event", G_CALLBACK (key_press_event), NULL); - g_signal_connect (p->attachment_bar, "popup-menu", G_CALLBACK (popup_menu_event), NULL); - - GTK_WIDGET_SET_FLAGS (p->attachment_bar, GTK_CAN_FOCUS); - gtk_container_add (GTK_CONTAINER (p->attachment_scrolled_window), - p->attachment_bar); - gtk_widget_show (p->attachment_bar); - g_signal_connect (p->attachment_bar, "changed", - G_CALLBACK (attachment_bar_changed_cb), composer); - - p->attachment_expander_label = - gtk_label_new_with_mnemonic (_("Show _Attachment Bar")); - p->attachment_expander_num = gtk_label_new (""); - gtk_label_set_use_markup (GTK_LABEL (p->attachment_expander_num), TRUE); - gtk_misc_set_alignment (GTK_MISC (p->attachment_expander_label), 0.0, 0.5); - gtk_misc_set_alignment (GTK_MISC (p->attachment_expander_num), 1.0, 0.5); - expander_hbox = gtk_hbox_new (FALSE, 0); - - p->attachment_expander_icon = e_icon_factory_get_image ("mail-attachment", E_ICON_SIZE_MENU); - gtk_misc_set_alignment (GTK_MISC (p->attachment_expander_icon), 1, 0.5); - gtk_widget_set_size_request (p->attachment_expander_icon, 100, -1); - - gtk_box_pack_start (GTK_BOX (expander_hbox), p->attachment_expander_label, - TRUE, TRUE, GNOME_PAD_SMALL); - gtk_box_pack_start (GTK_BOX (expander_hbox), p->attachment_expander_icon, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (expander_hbox), p->attachment_expander_num, - FALSE, FALSE, GNOME_PAD_SMALL); - gtk_widget_show_all (expander_hbox); - gtk_widget_hide (p->attachment_expander_icon); - - p->attachment_expander = e_expander_new (""); - e_expander_set_label_widget (E_EXPANDER (p->attachment_expander), expander_hbox); - gtk_container_add (GTK_CONTAINER (p->attachment_expander), p->attachment_scrolled_window); - - gtk_box_pack_start (GTK_BOX (vbox), p->attachment_expander, FALSE, FALSE, GNOME_PAD_SMALL); - gtk_widget_show (p->attachment_expander); - e_expander_set_expanded (E_EXPANDER (p->attachment_expander), FALSE); - g_signal_connect_after (p->attachment_expander, "activate", - G_CALLBACK (attachment_expander_activate_cb), composer); - - bonobo_window_set_contents (BONOBO_WINDOW (composer), vbox); - gtk_widget_show (vbox); - - /* If we show this widget earlier, we lose network transparency. i.e. the - component appears on the machine evo is running on, ignoring any DISPLAY - variable. */ - gtk_widget_show (p->eeditor); - - prepare_engine (composer); - if (p->eeditor_engine == CORBA_OBJECT_NIL) { - e_error_run (GTK_WINDOW (composer), "mail-composer:no-editor-control", NULL); - gtk_object_destroy (GTK_OBJECT (composer)); + if (!composer) { + g_list_foreach (postto, (GFunc)g_free, NULL); + g_list_free (postto); return NULL; } - /* The engine would have the GtkHTML widget stored in "html-widget" - * We'll use that to listen for DnD signals - */ - - servant = ORBit_small_get_servant (p->eeditor_engine); - if (servant && (impl = bonobo_object (servant))) - html_widget = g_object_get_data (G_OBJECT(impl), "html-widget"); + table = e_msg_composer_get_header_table (composer); - if (html_widget) { - g_signal_connect (html_widget, "drag_data_received", G_CALLBACK (drag_data_received), composer); + if (postto) { + e_composer_header_table_set_post_to_list (table, postto); + g_list_foreach (postto, (GFunc)g_free, NULL); + g_list_free (postto); + postto = NULL; } - setup_cut_copy_paste (composer); - - g_signal_connect (composer, "map", (GCallback) map_default_cb, NULL); - - if (am == NULL) - am = autosave_manager_new (); - - autosave_manager_register (am, composer); - - p->has_changed = FALSE; - - return composer; -} - -static void -set_editor_signature (EMsgComposer *composer) -{ - EMsgComposerHdrs *hdrs; - ESignature *signature; - EAccount *account; - const gchar *uid; - - hdrs = E_MSG_COMPOSER_HDRS (composer->priv->hdrs); - account = e_msg_composer_hdrs_get_from_account (hdrs); - g_return_if_fail (account != NULL); - - uid = account->id->sig_uid; - signature = uid ? mail_config_get_signature_by_uid (uid) : NULL; - e_msg_composer_hdrs_set_signature (hdrs, signature); -} - -/** - * e_msg_composer_new_with_type: - * - * Create a new message composer widget. The type can be - * E_MSG_COMPOSER_MAIL, E_MSG_COMPOSER_POST or E_MSG_COMPOSER_MAIL_POST. - * - * Return value: A pointer to the newly created widget - **/ - -EMsgComposer * -e_msg_composer_new_with_type (int type) -{ - gboolean send_html; - GConfClient *gconf; - EMsgComposer *new; - - gconf = gconf_client_get_default (); - send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL); - g_object_unref (gconf); - - switch (type) { - case E_MSG_COMPOSER_MAIL: - new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL); - break; - case E_MSG_COMPOSER_POST: - new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_POST); - break; - default: - new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL | E_MSG_COMPOSER_VISIBLE_MASK_POST); - } + /* Restore the Account preference */ + account_name = (char *) camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Account"); + if (account_name) { + account_name = g_strdup (account_name); + g_strstrip (account_name); - if (new) { - e_msg_composer_set_send_html (new, send_html); - set_editor_signature (new); - set_editor_text (new, "", 0, TRUE, TRUE); + if ((account = mail_config_get_account_by_uid (account_name)) == NULL) + /* 'old' setting */ + account = mail_config_get_account_by_name (account_name); + if (account) { + g_free (account_name); + account_name = g_strdup (account->name); + } } - return new; -} - -/** - * e_msg_composer_new: - * - * Create a new message composer widget. - * - * Return value: A pointer to the newly created widget - **/ -EMsgComposer * -e_msg_composer_new (void) -{ - return e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL); -} - -static gboolean -is_special_header (const char *hdr_name) -{ - /* Note: a header is a "special header" if it has any meaning: - 1. it's not a X-* header or - 2. it's an X-Evolution* header - */ - if (g_ascii_strncasecmp (hdr_name, "X-", 2)) - return TRUE; - - if (!g_ascii_strncasecmp (hdr_name, "X-Evolution", 11)) - return TRUE; + if (postto == NULL) { + auto_cc = g_hash_table_new_full ( + camel_strcase_hash, camel_strcase_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); - /* we can keep all other X-* headers */ + auto_bcc = g_hash_table_new_full ( + camel_strcase_hash, camel_strcase_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); - return FALSE; -} + if (account) { + CamelInternetAddress *iaddr; -static void -e_msg_composer_set_pending_body (EMsgComposer *composer, char *text, ssize_t len) -{ - char *old; + /* hash our auto-recipients for this account */ + if (account->always_cc) { + iaddr = camel_internet_address_new (); + if (camel_address_decode (CAMEL_ADDRESS (iaddr), account->cc_addrs) != -1) { + for (i = 0; i < camel_address_length (CAMEL_ADDRESS (iaddr)); i++) { + const gchar *name, *addr; - old = g_object_get_data ((GObject *) composer, "body:text"); - g_free (old); - g_object_set_data ((GObject *) composer, "body:text", text); - g_object_set_data ((GObject *) composer, "body:len", GSIZE_TO_POINTER (len)); -} + if (!camel_internet_address_get (iaddr, i, &name, &addr)) + continue; -static void -e_msg_composer_flush_pending_body (EMsgComposer *composer, gboolean apply) -{ - char *body; - ssize_t len; + g_hash_table_insert (auto_cc, g_strdup (addr), GINT_TO_POINTER (TRUE)); + } + } + camel_object_unref (iaddr); + } - body = g_object_get_data ((GObject *) composer, "body:text"); - len = GPOINTER_TO_SIZE (g_object_get_data ((GObject *) composer, "body:len")); - if (body) { - if (apply) - set_editor_text (composer, body, len, FALSE, FALSE); + if (account->always_bcc) { + iaddr = camel_internet_address_new (); + if (camel_address_decode (CAMEL_ADDRESS (iaddr), account->bcc_addrs) != -1) { + for (i = 0; i < camel_address_length (CAMEL_ADDRESS (iaddr)); i++) { + const gchar *name, *addr; - g_object_set_data ((GObject *) composer, "body:text", NULL); - g_free (body); - } -} + if (!camel_internet_address_get (iaddr, i, &name, &addr)) + continue; -static void -add_attachments_handle_mime_part (EMsgComposer *composer, CamelMimePart *mime_part, - gboolean just_inlines, gboolean related, int depth) -{ - CamelContentType *content_type; - CamelDataWrapper *wrapper; + g_hash_table_insert (auto_bcc, g_strdup (addr), GINT_TO_POINTER (TRUE)); + } + } + camel_object_unref (iaddr); + } + } - if (!mime_part) - return; - - content_type = camel_mime_part_get_content_type (mime_part); - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - - if (CAMEL_IS_MULTIPART (wrapper)) { - /* another layer of multipartness... */ - add_attachments_from_multipart (composer, (CamelMultipart *) wrapper, just_inlines, depth + 1); - } else if (just_inlines) { - if (camel_mime_part_get_content_id (mime_part) || - camel_mime_part_get_content_location (mime_part)) - e_msg_composer_add_inline_image_from_mime_part (composer, mime_part); - } else if (CAMEL_IS_MIME_MESSAGE (wrapper)) { - /* do nothing */ - } else if (related && camel_content_type_is (content_type, "image", "*")) { - e_msg_composer_add_inline_image_from_mime_part (composer, mime_part); - } else { - if (camel_content_type_is (content_type, "text", "*")) { - /* do nothing */ - } else { - e_msg_composer_attach (composer, mime_part); - } - } -} - -static void -add_attachments_from_multipart (EMsgComposer *composer, CamelMultipart *multipart, - gboolean just_inlines, int depth) -{ - /* find appropriate message attachments to add to the composer */ - CamelMimePart *mime_part; - gboolean related; - int i, nparts; - - related = camel_content_type_is (CAMEL_DATA_WRAPPER (multipart)->mime_type, "multipart", "related"); - - if (CAMEL_IS_MULTIPART_SIGNED (multipart)) { - mime_part = camel_multipart_get_part (multipart, CAMEL_MULTIPART_SIGNED_CONTENT); - add_attachments_handle_mime_part (composer, mime_part, just_inlines, related, depth); - } else if (CAMEL_IS_MULTIPART_ENCRYPTED (multipart)) { - /* what should we do in this case? */ - } else { - nparts = camel_multipart_get_number (multipart); - - for (i = 0; i < nparts; i++) { - mime_part = camel_multipart_get_part (multipart, i); - add_attachments_handle_mime_part (composer, mime_part, just_inlines, related, depth); - } - } -} - - -/** - * e_msg_composer_add_message_attachments: - * @composer: the composer to add the attachments to. - * @message: the source message to copy the attachments from. - * @just_inlines: whether to attach all attachments or just add - * inline images. - * - * Walk through all the mime parts in @message and add them to the composer - * specified in @composer. - */ -void -e_msg_composer_add_message_attachments (EMsgComposer *composer, CamelMimeMessage *message, - gboolean just_inlines) -{ - CamelDataWrapper *wrapper; - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message)); - if (!CAMEL_IS_MULTIPART (wrapper)) - return; - - /* there must be attachments... */ - add_attachments_from_multipart (composer, (CamelMultipart *) wrapper, just_inlines, 0); -} - - -static void -handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, int depth) -{ - CamelContentType *content_type; - CamelDataWrapper *content; - CamelMimePart *mime_part; - - /* FIXME: make sure this isn't an s/mime signed part?? */ - e_msg_composer_set_pgp_sign (composer, TRUE); - - mime_part = camel_multipart_get_part (multipart, CAMEL_MULTIPART_SIGNED_CONTENT); - - if (!mime_part) - return; - - content_type = camel_mime_part_get_content_type (mime_part); - - content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - - if (CAMEL_IS_MULTIPART (content)) { - multipart = CAMEL_MULTIPART (content); - - /* Note: depth is preserved here because we're not - counting multipart/signed as a multipart, instead - we want to treat the content part as our mime part - here. */ - - if (CAMEL_IS_MULTIPART_SIGNED (content)) { - /* handle the signed content and configure the composer to sign outgoing messages */ - handle_multipart_signed (composer, multipart, depth); - } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { - /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ - handle_multipart_encrypted (composer, mime_part, depth); - } else if (camel_content_type_is (content_type, "multipart", "alternative")) { - /* this contains the text/plain and text/html versions of the message body */ - handle_multipart_alternative (composer, multipart, depth); - } else { - /* there must be attachments... */ - handle_multipart (composer, multipart, depth); - } - } else if (camel_content_type_is (content_type, "text", "*")) { - ssize_t len; - char *html; - - html = em_utils_part_to_html (mime_part, &len, NULL); - e_msg_composer_set_pending_body (composer, html, len); - } else { - e_msg_composer_attach (composer, mime_part); - } -} - -static void -handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, int depth) -{ - CamelContentType *content_type; - CamelCipherContext *cipher; - CamelDataWrapper *content; - CamelMimePart *mime_part; - CamelException ex; - CamelCipherValidity *valid; - - /* FIXME: make sure this is a PGP/MIME encrypted part?? */ - e_msg_composer_set_pgp_encrypt (composer, TRUE); - - camel_exception_init (&ex); - cipher = mail_crypto_get_pgp_cipher_context (NULL); - mime_part = camel_mime_part_new(); - valid = camel_cipher_decrypt(cipher, multipart, mime_part, &ex); - camel_object_unref(cipher); - camel_exception_clear (&ex); - if (valid == NULL) - return; - camel_cipher_validity_free(valid); - - content_type = camel_mime_part_get_content_type (mime_part); - - content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - - if (CAMEL_IS_MULTIPART (content)) { - CamelMultipart *content_multipart = CAMEL_MULTIPART (content); - - /* Note: depth is preserved here because we're not - counting multipart/encrypted as a multipart, instead - we want to treat the content part as our mime part - here. */ - - if (CAMEL_IS_MULTIPART_SIGNED (content)) { - /* handle the signed content and configure the composer to sign outgoing messages */ - handle_multipart_signed (composer, content_multipart, depth); - } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { - /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ - handle_multipart_encrypted (composer, mime_part, depth); - } else if (camel_content_type_is (content_type, "multipart", "alternative")) { - /* this contains the text/plain and text/html versions of the message body */ - handle_multipart_alternative (composer, content_multipart, depth); - } else { - /* there must be attachments... */ - handle_multipart (composer, content_multipart, depth); - } - } else if (camel_content_type_is (content_type, "text", "*")) { - ssize_t len; - char *html; - - html = em_utils_part_to_html (mime_part, &len, NULL); - e_msg_composer_set_pending_body (composer, html, len); - } else { - e_msg_composer_attach (composer, mime_part); - } - - camel_object_unref (mime_part); -} - -static void -handle_multipart_alternative (EMsgComposer *composer, CamelMultipart *multipart, int depth) -{ - /* Find the text/html part and set the composer body to it's contents */ - CamelMimePart *text_part = NULL; - int i, nparts; - - nparts = camel_multipart_get_number (multipart); - - for (i = 0; i < nparts; i++) { - CamelContentType *content_type; - CamelDataWrapper *content; - CamelMimePart *mime_part; - - mime_part = camel_multipart_get_part (multipart, i); - - if (!mime_part) - continue; - - content_type = camel_mime_part_get_content_type (mime_part); - content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - - if (CAMEL_IS_MULTIPART (content)) { - CamelMultipart *mp; - - mp = CAMEL_MULTIPART (content); - - if (CAMEL_IS_MULTIPART_SIGNED (content)) { - /* handle the signed content and configure the composer to sign outgoing messages */ - handle_multipart_signed (composer, mp, depth + 1); - } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { - /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ - handle_multipart_encrypted (composer, mime_part, depth + 1); - } else { - /* depth doesn't matter so long as we don't pass 0 */ - handle_multipart (composer, mp, depth + 1); - } - } else if (camel_content_type_is (content_type, "text", "html")) { - /* text/html is preferable, so once we find it we're done... */ - text_part = mime_part; - break; - } else if (camel_content_type_is (content_type, "text", "*")) { - /* anyt text part not text/html is second rate so the first - text part we find isn't necessarily the one we'll use. */ - if (!text_part) - text_part = mime_part; - } else { - e_msg_composer_attach (composer, mime_part); - } - } - - if (text_part) { - ssize_t len; - char *html; - - html = em_utils_part_to_html(text_part, &len, NULL); - e_msg_composer_set_pending_body(composer, html, len); - } -} - -static void -handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, int depth) -{ - int i, nparts; - - nparts = camel_multipart_get_number (multipart); - - for (i = 0; i < nparts; i++) { - CamelContentType *content_type; - CamelDataWrapper *content; - CamelMimePart *mime_part; - - mime_part = camel_multipart_get_part (multipart, i); - - if (!mime_part) - continue; - - content_type = camel_mime_part_get_content_type (mime_part); - content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - - if (CAMEL_IS_MULTIPART (content)) { - CamelMultipart *mp; - - mp = CAMEL_MULTIPART (content); - - if (CAMEL_IS_MULTIPART_SIGNED (content)) { - /* handle the signed content and configure the composer to sign outgoing messages */ - handle_multipart_signed (composer, mp, depth + 1); - } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { - /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ - handle_multipart_encrypted (composer, mime_part, depth + 1); - } else if (camel_content_type_is (content_type, "multipart", "alternative")) { - handle_multipart_alternative (composer, mp, depth + 1); - } else { - /* depth doesn't matter so long as we don't pass 0 */ - handle_multipart (composer, mp, depth + 1); - } - } else if (depth == 0 && i == 0) { - ssize_t len; - char *html; - - /* Since the first part is not multipart/alternative, then this must be the body */ - html = em_utils_part_to_html(mime_part, &len, NULL); - e_msg_composer_set_pending_body(composer, html, len); - } else if (camel_mime_part_get_content_id (mime_part) || - camel_mime_part_get_content_location (mime_part)) { - /* special in-line attachment */ - e_msg_composer_add_inline_image_from_mime_part (composer, mime_part); - } else { - /* normal attachment */ - e_msg_composer_attach (composer, mime_part); - } - } -} - -static void -set_signature_gui (EMsgComposer *composer) -{ - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; - EMsgComposerHdrs *hdrs; - ESignature *signature = NULL; - - hdrs = E_MSG_COMPOSER_HDRS (composer->priv->hdrs); - - CORBA_exception_init (&ev); - if (GNOME_GtkHTML_Editor_Engine_searchByData (p->eeditor_engine, 1, "ClueFlow", "signature", "1", &ev)) { - char *name, *str = NULL; - - str = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "signature_name", &ev); - if (ev._major == CORBA_NO_EXCEPTION && str) { - if (!strncmp (str, "uid:", 4)) { - name = decode_signature_name (str + 4); - signature = mail_config_get_signature_by_uid (name); - g_free (name); - } else if (!strncmp (str, "name:", 5)) { - name = decode_signature_name (str + 4); - signature = mail_config_get_signature_by_name (name); - g_free (name); - } - CORBA_free (str); - } - - e_msg_composer_hdrs_set_signature (hdrs, signature); - } - CORBA_exception_free (&ev); -} - - -/** - * e_msg_composer_new_with_message: - * @message: The message to use as the source - * - * Create a new message composer widget. - * - * Note: Designed to work only for messages constructed using Evolution. - * - * Return value: A pointer to the newly created widget - **/ -EMsgComposer * -e_msg_composer_new_with_message (CamelMimeMessage *message) -{ - const CamelInternetAddress *to, *cc, *bcc; - GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL; - const char *format, *subject; - EDestination **Tov, **Ccv, **Bccv; - GHashTable *auto_cc, *auto_bcc; - CamelContentType *content_type; - struct _camel_header_raw *headers; - CamelDataWrapper *content; - EAccount *account = NULL; - char *account_name; - EMsgComposer *new; - struct _camel_header_raw *xev; - int len, i; - EMsgComposerPrivate *p; - - for (headers = CAMEL_MIME_PART (message)->headers;headers;headers = headers->next) { - if (!strcmp(headers->name, "X-Evolution-PostTo")) - postto = g_list_append(postto, g_strstrip(g_strdup(headers->value))); - } - - new = create_composer (postto ? E_MSG_COMPOSER_VISIBLE_MASK_POST : E_MSG_COMPOSER_VISIBLE_MASK_MAIL); - p = new->priv; - - if (!new) { - g_list_foreach(postto, (GFunc)g_free, NULL); - g_list_free(postto); - return NULL; - } - - if (postto) { - e_msg_composer_hdrs_set_post_to_list(E_MSG_COMPOSER_HDRS (p->hdrs), postto); - g_list_foreach(postto, (GFunc)g_free, NULL); - g_list_free(postto); - postto = NULL; - } - - /* Restore the Account preference */ - account_name = (char *) camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Account"); - if (account_name) { - account_name = g_strdup (account_name); - g_strstrip (account_name); - - if ((account = mail_config_get_account_by_uid(account_name)) == NULL) - /* 'old' setting */ - account = mail_config_get_account_by_name(account_name); - if (account) { - g_free (account_name); - account_name = g_strdup (account->name); - } - } - - if (postto == NULL) { - auto_cc = g_hash_table_new_full ( - camel_strcase_hash, camel_strcase_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); - - auto_bcc = g_hash_table_new_full ( - camel_strcase_hash, camel_strcase_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); - - if (account) { - CamelInternetAddress *iaddr; - - /* hash our auto-recipients for this account */ - if (account->always_cc) { - iaddr = camel_internet_address_new (); - if (camel_address_decode (CAMEL_ADDRESS (iaddr), account->cc_addrs) != -1) { - for (i = 0; i < camel_address_length (CAMEL_ADDRESS (iaddr)); i++) { - const char *name, *addr; - - if (!camel_internet_address_get (iaddr, i, &name, &addr)) - continue; - - g_hash_table_insert (auto_cc, g_strdup (addr), GINT_TO_POINTER (TRUE)); - } - } - camel_object_unref (iaddr); - } - - if (account->always_bcc) { - iaddr = camel_internet_address_new (); - if (camel_address_decode (CAMEL_ADDRESS (iaddr), account->bcc_addrs) != -1) { - for (i = 0; i < camel_address_length (CAMEL_ADDRESS (iaddr)); i++) { - const char *name, *addr; - - if (!camel_internet_address_get (iaddr, i, &name, &addr)) - continue; - - g_hash_table_insert (auto_bcc, g_strdup (addr), GINT_TO_POINTER (TRUE)); - } - } - camel_object_unref (iaddr); - } - } - - to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO); - cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC); - bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC); - - len = CAMEL_ADDRESS (to)->addresses->len; - for (i = 0; i < len; i++) { - const char *name, *addr; - - if (camel_internet_address_get (to, i, &name, &addr)) { - EDestination *dest = e_destination_new (); - e_destination_set_name (dest, name); - e_destination_set_email (dest, addr); - To = g_list_append (To, dest); - } - } - Tov = destination_list_to_vector (To); - g_list_free (To); - - len = CAMEL_ADDRESS (cc)->addresses->len; - for (i = 0; i < len; i++) { - const char *name, *addr; - - if (camel_internet_address_get (cc, i, &name, &addr)) { - EDestination *dest = e_destination_new (); - e_destination_set_name (dest, name); - e_destination_set_email (dest, addr); - - if (g_hash_table_lookup (auto_cc, addr)) - e_destination_set_auto_recipient (dest, TRUE); - - Cc = g_list_append (Cc, dest); - } - } - - Ccv = destination_list_to_vector (Cc); - g_hash_table_destroy (auto_cc); - g_list_free (Cc); - - len = CAMEL_ADDRESS (bcc)->addresses->len; - for (i = 0; i < len; i++) { - const char *name, *addr; - - if (camel_internet_address_get (bcc, i, &name, &addr)) { - EDestination *dest = e_destination_new (); - e_destination_set_name (dest, name); - e_destination_set_email (dest, addr); - - if (g_hash_table_lookup (auto_bcc, addr)) - e_destination_set_auto_recipient (dest, TRUE); - - Bcc = g_list_append (Bcc, dest); - } - } - - Bccv = destination_list_to_vector (Bcc); - g_hash_table_destroy (auto_bcc); - g_list_free (Bcc); - } else { - Tov = NULL; - Ccv = NULL; - Bccv = NULL; - } - - subject = camel_mime_message_get_subject (message); - - e_msg_composer_set_headers (new, account_name, Tov, Ccv, Bccv, subject); - - g_free (account_name); - - e_destination_freev (Tov); - e_destination_freev (Ccv); - e_destination_freev (Bccv); - - /* Restore the format editing preference */ - format = camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Format"); - if (format) { - char **flags; - - while (*format && camel_mime_is_lwsp(*format)) - format++; - - flags = g_strsplit(format, ", ", 0); - for (i=0;flags[i];i++) { - printf("restoring draft flag '%s'\n", flags[i]); - - if (g_ascii_strcasecmp(flags[i], "text/html") == 0) - e_msg_composer_set_send_html (new, TRUE); - else if (g_ascii_strcasecmp(flags[i], "text/plain") == 0) - e_msg_composer_set_send_html (new, FALSE); - else if (g_ascii_strcasecmp(flags[i], "pgp-sign") == 0) - e_msg_composer_set_pgp_sign(new, TRUE); - else if (g_ascii_strcasecmp(flags[i], "pgp-encrypt") == 0) - e_msg_composer_set_pgp_encrypt(new, TRUE); - else if (g_ascii_strcasecmp(flags[i], "smime-sign") == 0) - e_msg_composer_set_smime_sign(new, TRUE); - else if (g_ascii_strcasecmp(flags[i], "smime-encrypt") == 0) - e_msg_composer_set_smime_encrypt(new, TRUE); - } - g_strfreev(flags); - } - - /* Remove any other X-Evolution-* headers that may have been set */ - xev = mail_tool_remove_xevolution_headers (message); - camel_header_raw_clear(&xev); - - /* Check for receipt request */ - if (camel_medium_get_header(CAMEL_MEDIUM(message), "Disposition-Notification-To")) { - e_msg_composer_set_request_receipt (new, TRUE); - } - - /* Check for mail priority */ - if (camel_medium_get_header(CAMEL_MEDIUM(message), "X-Priority")) { - e_msg_composer_set_priority (new, TRUE); - } - - /* set extra headers */ - headers = CAMEL_MIME_PART (message)->headers; - while (headers) { - if (!is_special_header (headers->name) || - !g_ascii_strcasecmp (headers->name, "References") || - !g_ascii_strcasecmp (headers->name, "In-Reply-To")) { - g_ptr_array_add (p->extra_hdr_names, g_strdup (headers->name)); - g_ptr_array_add (p->extra_hdr_values, g_strdup (headers->value)); - } - - headers = headers->next; - } - - /* Restore the attachments and body text */ - content = camel_medium_get_content_object (CAMEL_MEDIUM (message)); - if (CAMEL_IS_MULTIPART (content)) { - CamelMultipart *multipart; - - multipart = CAMEL_MULTIPART (content); - content_type = camel_mime_part_get_content_type (CAMEL_MIME_PART (message)); - - if (CAMEL_IS_MULTIPART_SIGNED (content)) { - /* handle the signed content and configure the composer to sign outgoing messages */ - handle_multipart_signed (new, multipart, 0); - } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { - /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ - handle_multipart_encrypted (new, CAMEL_MIME_PART (message), 0); - } else if (camel_content_type_is (content_type, "multipart", "alternative")) { - /* this contains the text/plain and text/html versions of the message body */ - handle_multipart_alternative (new, multipart, 0); - } else { - /* there must be attachments... */ - handle_multipart (new, multipart, 0); - } - } else { - ssize_t length; - char *html; - - html = em_utils_part_to_html((CamelMimePart *)message, &length, NULL); - e_msg_composer_set_pending_body(new, html, length); - } - - /* We wait until now to set the body text because we need to ensure that - * the attachment bar has all the attachments, before we request them. - */ - e_msg_composer_flush_pending_body (new, TRUE); - - set_signature_gui (new); - - return new; -} - -static void -disable_editor (EMsgComposer *composer) -{ - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; - - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "editable-off", &ev); - CORBA_exception_free (&ev); - - gtk_widget_set_sensitive (p->attachment_bar, FALSE); - - bonobo_ui_component_set_prop (p->uic, "/menu/Edit", "sensitive", "0", NULL); - bonobo_ui_component_set_prop (p->uic, "/menu/Format", "sensitive", "0", NULL); - bonobo_ui_component_set_prop (p->uic, "/menu/Insert", "sensitive", "0", NULL); -} - -/** - * e_msg_composer_new_redirect: - * @message: The message to use as the source - * - * Create a new message composer widget. - * - * Return value: A pointer to the newly created widget - **/ -EMsgComposer * -e_msg_composer_new_redirect (CamelMimeMessage *message, const char *resent_from) -{ - EMsgComposer *composer; - EMsgComposerPrivate *p; - const char *subject; - - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); - - composer = e_msg_composer_new_with_message (message); - p = composer->priv; - - subject = camel_mime_message_get_subject (message); - - p->redirect = message; - camel_object_ref (message); - - e_msg_composer_set_headers (composer, resent_from, NULL, NULL, NULL, subject); - - disable_editor (composer); - - return composer; -} - - -static GList * -add_recipients (GList *list, const char *recips) -{ - CamelInternetAddress *cia; - const char *name, *addr; - int num, i; - - cia = camel_internet_address_new (); - num = camel_address_decode (CAMEL_ADDRESS (cia), recips); - - for (i = 0; i < num; i++) { - if (camel_internet_address_get (cia, i, &name, &addr)) { - EDestination *dest = e_destination_new (); - e_destination_set_name (dest, name); - e_destination_set_email (dest, addr); - - list = g_list_append (list, dest); - } - } - - return list; -} - -static void -handle_mailto (EMsgComposer *composer, const char *mailto) -{ - EMsgComposerHdrs *hdrs; - EMsgComposerPrivate *priv = composer->priv; - GList *to = NULL, *cc = NULL, *bcc = NULL; - EDestination **tov, **ccv, **bccv; - char *subject = NULL, *body = NULL; - char *header, *content, *buf; - size_t nread, nwritten; - const char *p; - int len, clen; - CamelURL *url; - - buf = g_strdup (mailto); - - /* Parse recipients (everything after ':' until '?' or eos). */ - p = buf + 7; - len = strcspn (p, "?"); - if (len) { - content = g_strndup (p, len); - camel_url_decode (content); - to = add_recipients (to, content); - g_free (content); - } - - p += len; - if (*p == '?') { - p++; - - while (*p) { - len = strcspn (p, "=&"); - - /* If it's malformed, give up. */ - if (p[len] != '=') - break; - - header = (char *) p; - header[len] = '\0'; - p += len + 1; - - clen = strcspn (p, "&"); - - content = g_strndup (p, clen); - camel_url_decode (content); - - if (!g_ascii_strcasecmp (header, "to")) { - to = add_recipients (to, content); - } else if (!g_ascii_strcasecmp (header, "cc")) { - cc = add_recipients (cc, content); - } else if (!g_ascii_strcasecmp (header, "bcc")) { - bcc = add_recipients (bcc, content); - } else if (!g_ascii_strcasecmp (header, "subject")) { - g_free (subject); - if (g_utf8_validate (content, -1, NULL)) { - subject = content; - content = NULL; - } else { - subject = g_locale_to_utf8 (content, clen, &nread, - &nwritten, NULL); - if (subject) { - subject = g_realloc (subject, nwritten + 1); - subject[nwritten] = '\0'; - } - } - } else if (!g_ascii_strcasecmp (header, "body")) { - g_free (body); - if (g_utf8_validate (content, -1, NULL)) { - body = content; - content = NULL; - } else { - body = g_locale_to_utf8 (content, clen, &nread, - &nwritten, NULL); - if (body) { - body = g_realloc (body, nwritten + 1); - body[nwritten] = '\0'; - } - } - } else if (!g_ascii_strcasecmp (header, "attach") || - !g_ascii_strcasecmp (header, "attachment")) { - /* Change file url to absolute path */ - if (!g_ascii_strncasecmp (content, "file:", 5)) { - url = camel_url_new (content, NULL); - e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar), - url->path, - "attachment"); - camel_url_free (url); - } else { - e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar), - content, - "attachment"); - } - gtk_widget_show (priv->attachment_expander); - gtk_widget_show (priv->attachment_scrolled_window); - } else if (!g_ascii_strcasecmp (header, "from")) { - /* Ignore */ - } else if (!g_ascii_strcasecmp (header, "reply-to")) { - /* ignore */ - } else { - /* add an arbitrary header? */ - e_msg_composer_add_header (composer, header, content); - } - - g_free (content); - - p += clen; - if (*p == '&') { - p++; - if (!strcmp (p, "amp;")) - p += 4; - } - } - } - - g_free (buf); - - tov = destination_list_to_vector (to); - ccv = destination_list_to_vector (cc); - bccv = destination_list_to_vector (bcc); - - g_list_free (to); - g_list_free (cc); - g_list_free (bcc); - - hdrs = E_MSG_COMPOSER_HDRS (priv->hdrs); - - e_msg_composer_hdrs_set_to (hdrs, tov); - e_msg_composer_hdrs_set_cc (hdrs, ccv); - e_msg_composer_hdrs_set_bcc (hdrs, bccv); - - e_destination_freev (tov); - e_destination_freev (ccv); - e_destination_freev (bccv); - - if (subject) { - e_msg_composer_hdrs_set_subject (hdrs, subject); - g_free (subject); - } - - if (body) { - char *htmlbody; - - htmlbody = camel_text_to_html (body, CAMEL_MIME_FILTER_TOHTML_PRE, 0); - set_editor_text (composer, htmlbody, -1, FALSE, FALSE); - g_free (htmlbody); - } -} - -static void -handle_uri (EMsgComposer *composer, const char *uri, gboolean html_dnd) -{ - EMsgComposerPrivate *p = composer->priv; - - if (!g_ascii_strncasecmp (uri, "mailto:", 7)) { - handle_mailto (composer, uri); - } else { - CamelURL *url = camel_url_new (uri, NULL); - gchar *type; - - if (!url) - return; - - if (!g_ascii_strcasecmp (url->protocol, "file")) { - type = attachment_guess_mime_type (uri); - if (!type) - return; - - if (strncmp (type, "image", 5) || !html_dnd || (!p->send_html && !strncmp (type, "image", 5))) { - e_attachment_bar_attach (E_ATTACHMENT_BAR (p->attachment_bar), - url->path, "attachment"); - } - g_free (type); - } else { - e_attachment_bar_attach_remote_file (E_ATTACHMENT_BAR (p->attachment_bar), - uri, "attachment"); - } - camel_url_free (url); - } -} - -/** - * e_msg_composer_new_from_url: - * @url: a mailto URL - * - * Create a new message composer widget, and fill in fields as - * defined by the provided URL. - **/ -EMsgComposer * -e_msg_composer_new_from_url (const char *url) -{ - EMsgComposer *composer; - - g_return_val_if_fail (g_ascii_strncasecmp (url, "mailto:", 7) == 0, NULL); - - composer = e_msg_composer_new (); - if (!composer) - return NULL; - - handle_mailto (composer, url); - - return composer; -} - -/** - * e_msg_composer_set_headers: - * @composer: a composer object - * @from: the name of the account the user will send from, - * or %NULL for the default account - * @to: the values for the "To" header - * @cc: the values for the "Cc" header - * @bcc: the values for the "Bcc" header - * @subject: the value for the "Subject" header - * - * Sets the headers in the composer to the given values. - **/ -void -e_msg_composer_set_headers (EMsgComposer *composer, - const char *from, - EDestination **to, - EDestination **cc, - EDestination **bcc, - const char *subject) -{ - EMsgComposerHdrs *hdrs; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - hdrs = E_MSG_COMPOSER_HDRS (p->hdrs); - - e_msg_composer_hdrs_set_to (hdrs, to); - e_msg_composer_hdrs_set_cc (hdrs, cc); - e_msg_composer_hdrs_set_bcc (hdrs, bcc); - e_msg_composer_hdrs_set_subject (hdrs, subject); - e_msg_composer_hdrs_set_from_account (hdrs, from); -} - - -/** - * e_msg_composer_set_body_text: - * @composer: a composer object - * @text: the HTML text to initialize the editor with - * - * Loads the given HTML text into the editor. - **/ -void -e_msg_composer_set_body_text (EMsgComposer *composer, const char *text, ssize_t len) -{ - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (text != NULL); - - set_editor_text (composer, text, len, TRUE, *text == '\0'); -} - -/** - * e_msg_composer_set_body: - * @composer: a composer object - * @body: the data to initialize the composer with - * @mime_type: the MIME type of data - * - * Loads the given data into the composer as the message body. - * This function should only be used by the CORBA composer factory. - **/ -void -e_msg_composer_set_body (EMsgComposer *composer, const char *body, - const char *mime_type) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - set_editor_text (composer, _("(The composer contains a non-text message body, which cannot be edited.)"), -1, FALSE, FALSE); - e_msg_composer_set_send_html (composer, FALSE); - disable_editor (composer); - - g_free (p->mime_body); - p->mime_body = g_strdup (body); - g_free (p->mime_type); - p->mime_type = g_strdup (mime_type); - - if (g_ascii_strncasecmp (p->mime_type, "text/calendar", 13) == 0) { - EMsgComposerHdrs *hdrs = E_MSG_COMPOSER_HDRS (p->hdrs); - EAccount *account; - - account = e_msg_composer_hdrs_get_from_account (hdrs); - if (account && account->pgp_no_imip_sign) - e_msg_composer_set_pgp_sign (composer, FALSE); - } -} - - -/** - * e_msg_composer_add_header: - * @composer: a composer object - * @name: the header name - * @value: the header value - * - * Adds a header with @name and @value to the message. This header - * may not be displayed by the composer, but will be included in - * the message it outputs. - **/ -void -e_msg_composer_add_header (EMsgComposer *composer, const char *name, - const char *value) -{ - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (name != NULL); - g_return_if_fail (value != NULL); - - g_ptr_array_add (p->extra_hdr_names, g_strdup (name)); - g_ptr_array_add (p->extra_hdr_values, g_strdup (value)); -} -/** - * e_msg_composer_modify_header : - * @composer : a composer object - * @name: the header name - * @change_value: the header value to put in place of the previous - * value - * - * Searches for a header with name=@name ,if found it removes - * that header and adds a new header with the @name and @change_value . - * If not found then it creates a new header with @name and @change_value . - **/ -void -e_msg_composer_modify_header (EMsgComposer *composer, const char *name, - const char *change_value) -{ - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (name != NULL); - g_return_if_fail (change_value != NULL); - - e_msg_composer_remove_header (composer, name); - e_msg_composer_add_header (composer, name, change_value); -} - -/** - * e_msg_composer_modify_header : - * @composer : a composer object - * @name: the header name - * - * Searches for the header and if found it removes it . - **/ -void -e_msg_composer_remove_header (EMsgComposer *composer, const char *name) -{ - EMsgComposerPrivate *p; - int i; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (name != NULL); - - p = composer->priv; - - for (i = 0; i < p->extra_hdr_names->len; i++) { - if (strcmp (p->extra_hdr_names->pdata[i], name) == 0) { - g_print ("Hit : %s",name); - g_ptr_array_remove_index (p->extra_hdr_names, i); - g_ptr_array_remove_index (p->extra_hdr_values, i); - } - } -} -/** - * e_msg_composer_attach: - * @composer: a composer object - * @attachment: the CamelMimePart to attach - * - * Attaches @attachment to the message being composed in the composer. - **/ -void -e_msg_composer_attach (EMsgComposer *composer, CamelMimePart *attachment) -{ - EAttachmentBar *bar; - EMsgComposerPrivate *p = composer->priv; + to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO); + cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC); + bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC); - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (CAMEL_IS_MIME_PART (attachment)); + len = CAMEL_ADDRESS (to)->addresses->len; + for (i = 0; i < len; i++) { + const gchar *name, *addr; - bar = E_ATTACHMENT_BAR (p->attachment_bar); - e_attachment_bar_attach_mime_part (bar, attachment); + if (camel_internet_address_get (to, i, &name, &addr)) { + EDestination *dest = e_destination_new (); + e_destination_set_name (dest, name); + e_destination_set_email (dest, addr); + To = g_list_append (To, dest); + } + } + Tov = destination_list_to_vector (To); + g_list_free (To); - show_attachments (composer, TRUE); -} + len = CAMEL_ADDRESS (cc)->addresses->len; + for (i = 0; i < len; i++) { + const gchar *name, *addr; + if (camel_internet_address_get (cc, i, &name, &addr)) { + EDestination *dest = e_destination_new (); + e_destination_set_name (dest, name); + e_destination_set_email (dest, addr); -/** - * e_msg_composer_add_inline_image_from_file: - * @composer: a composer object - * @file_name: the name of the file containing the image - * - * This reads in the image in @file_name and adds it to @composer - * as an inline image, to be wrapped in a multipart/related. - * - * Return value: the newly-created CamelMimePart (which must be reffed - * if the caller wants to keep its own reference), or %NULL on error. - **/ -CamelMimePart * -e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, - const char *file_name) -{ - char *mime_type, *cid, *url, *name, *dec_file_name; - CamelStream *stream; - CamelDataWrapper *wrapper; - CamelMimePart *part; - struct stat statbuf; - EMsgComposerPrivate *p = composer->priv; + if (g_hash_table_lookup (auto_cc, addr)) + e_destination_set_auto_recipient (dest, TRUE); - dec_file_name = g_strdup(file_name); - camel_url_decode(dec_file_name); + Cc = g_list_append (Cc, dest); + } + } - /* check for regular file */ - if (g_stat (dec_file_name, &statbuf) < 0 || !S_ISREG (statbuf.st_mode)) - return NULL; + Ccv = destination_list_to_vector (Cc); + g_hash_table_destroy (auto_cc); + g_list_free (Cc); - stream = camel_stream_fs_new_with_name (dec_file_name, O_RDONLY, 0); - if (!stream) - return NULL; + len = CAMEL_ADDRESS (bcc)->addresses->len; + for (i = 0; i < len; i++) { + const gchar *name, *addr; - wrapper = camel_data_wrapper_new (); - camel_data_wrapper_construct_from_stream (wrapper, stream); - camel_object_unref (CAMEL_OBJECT (stream)); + if (camel_internet_address_get (bcc, i, &name, &addr)) { + EDestination *dest = e_destination_new (); + e_destination_set_name (dest, name); + e_destination_set_email (dest, addr); - mime_type = e_msg_composer_guess_mime_type (dec_file_name); - camel_data_wrapper_set_mime_type (wrapper, mime_type ? mime_type : "application/octet-stream"); - g_free (mime_type); + if (g_hash_table_lookup (auto_bcc, addr)) + e_destination_set_auto_recipient (dest, TRUE); - part = camel_mime_part_new (); - camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper); - camel_object_unref (wrapper); + Bcc = g_list_append (Bcc, dest); + } + } - cid = camel_header_msgid_generate (); - camel_mime_part_set_content_id (part, cid); - name = g_path_get_basename(dec_file_name); - camel_mime_part_set_filename (part, name); - g_free(name); - camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64); + Bccv = destination_list_to_vector (Bcc); + g_hash_table_destroy (auto_bcc); + g_list_free (Bcc); + } else { + Tov = NULL; + Ccv = NULL; + Bccv = NULL; + } - url = g_strdup_printf ("file:%s", dec_file_name); - g_hash_table_insert (p->inline_images_by_url, url, part); + subject = camel_mime_message_get_subject (message); - url = g_strdup_printf ("cid:%s", cid); - g_hash_table_insert (p->inline_images, url, part); - g_free (cid); + e_composer_header_table_set_account_name (table, account_name); + e_composer_header_table_set_destinations_to (table, Tov); + e_composer_header_table_set_destinations_cc (table, Ccv); + e_composer_header_table_set_destinations_bcc (table, Bccv); + e_composer_header_table_set_subject (table, subject); - g_free(dec_file_name); + g_free (account_name); - return part; -} + e_destination_freev (Tov); + e_destination_freev (Ccv); + e_destination_freev (Bccv); + /* Restore the format editing preference */ + format = camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Format"); + if (format) { + gchar **flags; -/** - * e_msg_composer_add_inline_image_from_mime_part: - * @composer: a composer object - * @part: a CamelMimePart containing image data - * - * This adds the mime part @part to @composer as an inline image, to - * be wrapped in a multipart/related. - **/ -void -e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer, - CamelMimePart *part) -{ - char *url; - const char *location, *cid; - EMsgComposerPrivate *p = composer->priv; + while (*format && camel_mime_is_lwsp (*format)) + format++; - cid = camel_mime_part_get_content_id (part); - if (!cid) { - camel_mime_part_set_content_id (part, NULL); - cid = camel_mime_part_get_content_id (part); + flags = g_strsplit (format, ", ", 0); + for (i=0;flags[i];i++) { + printf ("restoring draft flag '%s'\n", flags[i]); + + if (g_ascii_strcasecmp (flags[i], "text/html") == 0) + gtkhtml_editor_set_html_mode ( + GTKHTML_EDITOR (composer), TRUE); + else if (g_ascii_strcasecmp (flags[i], "text/plain") == 0) + gtkhtml_editor_set_html_mode ( + GTKHTML_EDITOR (composer), FALSE); + else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) { + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, TRUE); + } else if (g_ascii_strcasecmp (flags[i], "pgp-encrypt") == 0) { + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + gtk_toggle_action_set_active (action, TRUE); + } else if (g_ascii_strcasecmp (flags[i], "smime-sign") == 0) { + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + gtk_toggle_action_set_active (action, TRUE); + } else if (g_ascii_strcasecmp (flags[i], "smime-encrypt") == 0) { + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + gtk_toggle_action_set_active (action, TRUE); + } + } + g_strfreev (flags); } - url = g_strdup_printf ("cid:%s", cid); - g_hash_table_insert (p->inline_images, url, part); - camel_object_ref (part); + /* Remove any other X-Evolution-* headers that may have been set */ + xev = mail_tool_remove_xevolution_headers (message); + camel_header_raw_clear (&xev); + + /* Check for receipt request */ + if (camel_medium_get_header (CAMEL_MEDIUM (message), "Disposition-Notification-To")) { + action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT)); + gtk_toggle_action_set_active (action, TRUE); + } + + /* Check for mail priority */ + if (camel_medium_get_header (CAMEL_MEDIUM (message), "X-Priority")) { + action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE)); + gtk_toggle_action_set_active (action, TRUE); + } - location = camel_mime_part_get_content_location (part); - if (location) { - g_hash_table_insert (p->inline_images_by_url, - g_strdup (location), part); + /* set extra headers */ + headers = CAMEL_MIME_PART (message)->headers; + while (headers) { + if (!is_special_header (headers->name) || + !g_ascii_strcasecmp (headers->name, "References") || + !g_ascii_strcasecmp (headers->name, "In-Reply-To")) { + g_ptr_array_add (p->extra_hdr_names, g_strdup (headers->name)); + g_ptr_array_add (p->extra_hdr_values, g_strdup (headers->value)); + } + + headers = headers->next; } -} + /* Restore the attachments and body text */ + content = camel_medium_get_content_object (CAMEL_MEDIUM (message)); + if (CAMEL_IS_MULTIPART (content)) { + CamelMultipart *multipart; + + multipart = CAMEL_MULTIPART (content); + content_type = camel_mime_part_get_content_type (CAMEL_MIME_PART (message)); -/** - * e_msg_composer_get_message: - * @composer: A message composer widget - * - * Retrieve the message edited by the user as a CamelMimeMessage. The - * CamelMimeMessage object is created on the fly; subsequent calls to this - * function will always create new objects from scratch. - * - * Return value: A pointer to the new CamelMimeMessage object - **/ -CamelMimeMessage * -e_msg_composer_get_message (EMsgComposer *composer, gboolean save_html_object_data) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - if ( e_msg_composer_get_remote_download_count (composer) != 0) { - if (!em_utils_prompt_user((GtkWindow *)composer, NULL, "mail-composer:ask-send-message-pending-download", NULL)) { - return NULL; + if (CAMEL_IS_MULTIPART_SIGNED (content)) { + /* handle the signed content and configure the composer to sign outgoing messages */ + handle_multipart_signed (composer, multipart, 0); + } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) { + /* decrypt the encrypted content and configure the composer to encrypt outgoing messages */ + handle_multipart_encrypted (composer, CAMEL_MIME_PART (message), 0); + } else if (camel_content_type_is (content_type, "multipart", "alternative")) { + /* this contains the text/plain and text/html versions of the message body */ + handle_multipart_alternative (composer, multipart, 0); + } else { + /* there must be attachments... */ + handle_multipart (composer, multipart, 0); } + } else { + gchar *html; + gssize length; + + html = em_utils_part_to_html ((CamelMimePart *)message, &length, NULL); + e_msg_composer_set_pending_body (composer, html, length); } - return build_message (composer, save_html_object_data); + /* We wait until now to set the body text because we need to + * ensure that the attachment bar has all the attachments before + * we request them. */ + e_msg_composer_flush_pending_body (composer); + + set_signature_gui (composer); + + return composer; } -CamelMimeMessage * -e_msg_composer_get_message_print (EMsgComposer *composer, gboolean save_html_object_data) +static void +disable_editor (EMsgComposer *composer) { - EMsgComposer *temp_composer; - CamelMimeMessage *msg; - GString *flags; + GtkhtmlEditor *editor; + GtkAction *action; - msg = build_message (composer, save_html_object_data); - if (msg == NULL) - return NULL; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - temp_composer = e_msg_composer_new_with_message (msg); - camel_object_unref (msg); + editor = GTKHTML_EDITOR (composer); - /* build flags string */ - flags = g_string_sized_new (128); - if (temp_composer->priv->send_html) - g_string_append (flags, "text/html"); - else - g_string_append (flags, "text/plain"); - if (temp_composer->priv->pgp_sign) - g_string_append (flags, ", pgp-sign"); - if (temp_composer->priv->pgp_encrypt) - g_string_append (flags, ", pgp-encrypt"); - if (temp_composer->priv->smime_sign) - g_string_append (flags, ", smime-sign"); - if (temp_composer->priv->smime_encrypt) - g_string_append (flags, ", smime-encrypt"); + gtkhtml_editor_run_command (editor, "editable-off"); - /* override composer flags */ - temp_composer->priv->send_html = TRUE; - temp_composer->priv->pgp_sign = FALSE; - temp_composer->priv->pgp_encrypt = FALSE; - temp_composer->priv->smime_sign = FALSE; - temp_composer->priv->smime_encrypt = FALSE; + action = GTKHTML_EDITOR_ACTION_EDIT_MENU (composer); + gtk_action_set_sensitive (action, FALSE); - msg = build_message (temp_composer, save_html_object_data); - if (msg != NULL) - camel_medium_set_header ( - CAMEL_MEDIUM (msg), - "X-Evolution-Format", flags->str); + action = GTKHTML_EDITOR_ACTION_FORMAT_MENU (composer); + gtk_action_set_sensitive (action, FALSE); - e_msg_composer_delete (temp_composer); - g_string_free (flags, TRUE); + action = GTKHTML_EDITOR_ACTION_INSERT_MENU (composer); + gtk_action_set_sensitive (action, FALSE); - return msg; + gtk_widget_set_sensitive (composer->priv->attachment_bar, FALSE); } -CamelMimeMessage * -e_msg_composer_get_message_draft (EMsgComposer *composer) +/** + * e_msg_composer_new_redirect: + * @message: The message to use as the source + * + * Create a new message composer widget. + * + * Returns: A pointer to the newly created widget + **/ +EMsgComposer * +e_msg_composer_new_redirect (CamelMimeMessage *message, + const gchar *resent_from) { - CamelMimeMessage *msg; - EAccount *account; - gboolean old_flags[4]; - gboolean old_send_html; - GString *flags; - int i; - EMsgComposerPrivate *p = composer->priv; - - /* always save drafts as HTML to preserve formatting */ - old_send_html = p->send_html; - p->send_html = TRUE; - old_flags[0] = p->pgp_sign; - p->pgp_sign = FALSE; - old_flags[1] = p->pgp_encrypt; - p->pgp_encrypt = FALSE; - old_flags[2] = p->smime_sign; - p->smime_sign = FALSE; - old_flags[3] = p->smime_encrypt; - p->smime_encrypt = FALSE; - - msg = build_message (composer, TRUE); - if (msg == NULL) - return NULL; - - p->send_html = old_send_html; - p->pgp_sign = old_flags[0]; - p->pgp_encrypt = old_flags[1]; - p->smime_sign = old_flags[2]; - p->smime_encrypt = old_flags[3]; - - /* Attach account info to the draft. */ - account = e_msg_composer_get_preferred_account (composer); - if (account && account->name) - camel_medium_set_header (CAMEL_MEDIUM (msg), "X-Evolution-Account", account->uid); + EMsgComposer *composer; + EComposerHeaderTable *table; + const gchar *subject; - /* build_message() set this to text/html since we set p->send_html to - TRUE before calling e_msg_composer_get_message() */ - if (!p->send_html) - flags = g_string_new("text/plain"); - else - flags = g_string_new("text/html"); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); - /* This should probably only save the setting if it is - * different from the from-account default? */ - for (i=0;i<4;i++) { - if (old_flags[i]) - g_string_append_printf(flags, ", %s", emc_draft_format_names[i]); - } + composer = e_msg_composer_new_with_message (message); + table = e_msg_composer_get_header_table (composer); - camel_medium_set_header (CAMEL_MEDIUM (msg), "X-Evolution-Format", flags->str); - g_string_free(flags, TRUE); + subject = camel_mime_message_get_subject (message); - return msg; -} + composer->priv->redirect = message; + camel_object_ref (message); + e_composer_header_table_set_account_name (table, resent_from); + e_composer_header_table_set_subject (table, subject); -static void -delete_old_signature (EMsgComposer *composer) -{ - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; + disable_editor (composer); - /* printf ("delete_old_signature\n"); */ - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "block-selection", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "cursor-bod", &ev); - if (GNOME_GtkHTML_Editor_Engine_searchByData (p->eeditor_engine, 1, "ClueFlow", "signature", "1", &ev)) { - /* printf ("found\n"); */ - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "select-paragraph", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "delete", &ev); - /* selection-move-right doesn't succeed means that we are already on the end of document */ - /* if (!rv) - break; */ - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "signature", "0", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "delete-back", &ev); - } - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "unblock-selection", &ev); - CORBA_exception_free (&ev); + return composer; } - /** - * e_msg_composer_show_sig: - * @composer: A message composer widget + * e_msg_composer_send: + * @composer: an #EMsgComposer * - * Set a signature + * Send the message in @composer. **/ void -e_msg_composer_show_sig_file (EMsgComposer *composer) +e_msg_composer_send (EMsgComposer *composer) { - CORBA_Environment ev; - char *html; - EMsgComposerPrivate *p = composer->priv; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - /* printf ("e_msg_composer_show_sig_file\n"); */ - /* printf ("set sig '%s' '%s'\n", sig_file, p->sig_file); */ - if (!p->redirect) { - p->in_signature_insert = TRUE; - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_freeze (p->eeditor_engine, &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "cursor-position-save", &ev); - GNOME_GtkHTML_Editor_Engine_undoBegin (p->eeditor_engine, "Set signature", "Reset signature", &ev); - - delete_old_signature (composer); - html = get_signature_html (composer); - if (html) { - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "insert-paragraph", &ev); - if (!GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "cursor-backward", &ev)) - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "insert-paragraph", &ev); - else - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "cursor-forward", &ev); - /* printf ("insert %s\n", html); */ - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "orig", "0", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "indent-zero", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "style-normal", &ev); - GNOME_GtkHTML_Editor_Engine_insertHTML (p->eeditor_engine, html, &ev); - g_free (html); - } - - GNOME_GtkHTML_Editor_Engine_undoEnd (p->eeditor_engine, &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "cursor-position-restore", &ev); - GNOME_GtkHTML_Editor_Engine_thaw (p->eeditor_engine, &ev); - CORBA_exception_free (&ev); - p->in_signature_insert = FALSE; - } - /* printf ("e_msg_composer_show_sig_file end\n"); */ + g_signal_emit (composer, signals[SEND], 0); } - /** - * e_msg_composer_set_send_html: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "Send HTML" flag set + * e_msg_composer_save_draft: + * @composer: an #EMsgComposer * - * Set the status of the "Send HTML" toggle item. The user can override it. + * Save the message in @composer to the selected account's Drafts folder. **/ void -e_msg_composer_set_send_html (EMsgComposer *composer, - gboolean send_html) +e_msg_composer_save_draft (EMsgComposer *composer) { - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - if (p->send_html && send_html) - return; + editor = GTKHTML_EDITOR (composer); + + g_signal_emit (composer, signals[SAVE_DRAFT], 0, FALSE); + + /* XXX This should be elsewhere. */ + gtkhtml_editor_set_changed (editor, FALSE); + e_composer_autosave_set_saved (composer, FALSE); +} + +static GList * +add_recipients (GList *list, const gchar *recips) +{ + CamelInternetAddress *cia; + const gchar *name, *addr; + gint num, i; + + cia = camel_internet_address_new (); + num = camel_address_decode (CAMEL_ADDRESS (cia), recips); - if (!p->send_html && !send_html) - return; + for (i = 0; i < num; i++) { + if (camel_internet_address_get (cia, i, &name, &addr)) { + EDestination *dest = e_destination_new (); + e_destination_set_name (dest, name); + e_destination_set_email (dest, addr); - p->send_html = send_html; + list = g_list_append (list, dest); + } + } - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "block-redraw", &ev); - CORBA_exception_free (&ev); + return list; +} - bonobo_ui_component_set_prop (p->uic, "/commands/FormatHtml", - "state", p->send_html ? "1" : "0", NULL); +static void +handle_mailto (EMsgComposer *composer, const gchar *mailto) +{ + EMsgComposerPrivate *priv = composer->priv; + EComposerHeaderTable *table; + GList *to = NULL, *cc = NULL, *bcc = NULL; + EDestination **tov, **ccv, **bccv; + gchar *subject = NULL, *body = NULL; + gchar *header, *content, *buf; + gsize nread, nwritten; + const gchar *p; + gint len, clen; + CamelURL *url; - /* let the editor know which mode we are in */ - bonobo_widget_set_property (BONOBO_WIDGET (p->eeditor), - "FormatHTML", TC_CORBA_boolean, - p->send_html, NULL); + table = e_msg_composer_get_header_table (composer); + buf = g_strdup (mailto); - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "unblock-redraw", &ev); - CORBA_exception_free (&ev); -} + /* Parse recipients (everything after ':' until '?' or eos). */ + p = buf + 7; + len = strcspn (p, "?"); + if (len) { + content = g_strndup (p, len); + camel_url_decode (content); + to = add_recipients (to, content); + g_free (content); + } + p += len; + if (*p == '?') { + p++; -/** - * e_msg_composer_get_send_html: - * @composer: A message composer widget - * - * Get the status of the "Send HTML mail" flag. - * - * Return value: The status of the "Send HTML mail" flag. - **/ -gboolean -e_msg_composer_get_send_html (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; + while (*p) { + len = strcspn (p, "=&"); - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + /* If it's malformed, give up. */ + if (p[len] != '=') + break; - return p->send_html; -} + header = (char *) p; + header[len] = '\0'; + p += len + 1; + clen = strcspn (p, "&"); -/** - * e_msg_composer_get_preferred_account: - * @composer: composer - * - * Returns the user-specified account (from field). - */ -EAccount * -e_msg_composer_get_preferred_account (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - EMsgComposerHdrs *hdrs; + content = g_strndup (p, clen); + camel_url_decode (content); - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + if (!g_ascii_strcasecmp (header, "to")) { + to = add_recipients (to, content); + } else if (!g_ascii_strcasecmp (header, "cc")) { + cc = add_recipients (cc, content); + } else if (!g_ascii_strcasecmp (header, "bcc")) { + bcc = add_recipients (bcc, content); + } else if (!g_ascii_strcasecmp (header, "subject")) { + g_free (subject); + if (g_utf8_validate (content, -1, NULL)) { + subject = content; + content = NULL; + } else { + subject = g_locale_to_utf8 (content, clen, &nread, + &nwritten, NULL); + if (subject) { + subject = g_realloc (subject, nwritten + 1); + subject[nwritten] = '\0'; + } + } + } else if (!g_ascii_strcasecmp (header, "body")) { + g_free (body); + if (g_utf8_validate (content, -1, NULL)) { + body = content; + content = NULL; + } else { + body = g_locale_to_utf8 (content, clen, &nread, + &nwritten, NULL); + if (body) { + body = g_realloc (body, nwritten + 1); + body[nwritten] = '\0'; + } + } + } else if (!g_ascii_strcasecmp (header, "attach") || + !g_ascii_strcasecmp (header, "attachment")) { + /* Change file url to absolute path */ + if (!g_ascii_strncasecmp (content, "file:", 5)) { + url = camel_url_new (content, NULL); + e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar), + url->path, + "attachment"); + camel_url_free (url); + } else { + e_attachment_bar_attach (E_ATTACHMENT_BAR (priv->attachment_bar), + content, + "attachment"); + } + gtk_widget_show (priv->attachment_expander); + gtk_widget_show (priv->attachment_scrolled_window); + } else if (!g_ascii_strcasecmp (header, "from")) { + /* Ignore */ + } else if (!g_ascii_strcasecmp (header, "reply-to")) { + /* ignore */ + } else { + /* add an arbitrary header? */ + e_msg_composer_add_header (composer, header, content); + } - hdrs = E_MSG_COMPOSER_HDRS (p->hdrs); + g_free (content); - return e_msg_composer_hdrs_get_from_account (hdrs); -} + p += clen; + if (*p == '&') { + p++; + if (!strcmp (p, "amp;")) + p += 4; + } + } + } + g_free (buf); -/** - * e_msg_composer_set_pgp_sign: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "PGP Sign" flag set - * - * Set the status of the "PGP Sign" toggle item. The user can override it. - **/ -void -e_msg_composer_set_pgp_sign (EMsgComposer *composer, gboolean pgp_sign) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + tov = destination_list_to_vector (to); + ccv = destination_list_to_vector (cc); + bccv = destination_list_to_vector (bcc); - if (p->pgp_sign && pgp_sign) - return; - if (!p->pgp_sign && !pgp_sign) - return; + g_list_free (to); + g_list_free (cc); + g_list_free (bcc); - p->pgp_sign = pgp_sign; - e_msg_composer_set_changed (composer); + e_composer_header_table_set_destinations_to (table, tov); + e_composer_header_table_set_destinations_cc (table, ccv); + e_composer_header_table_set_destinations_bcc (table, bccv); - bonobo_ui_component_set_prop (p->uic, "/commands/SecurityPGPSign", - "state", p->pgp_sign ? "1" : "0", NULL); -} + e_destination_freev (tov); + e_destination_freev (ccv); + e_destination_freev (bccv); + e_composer_header_table_set_subject (table, subject); + g_free (subject); -/** - * e_msg_composer_get_pgp_sign: - * @composer: A message composer widget - * - * Get the status of the "PGP Sign" flag. - * - * Return value: The status of the "PGP Sign" flag. - **/ -gboolean -e_msg_composer_get_pgp_sign (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + if (body) { + gchar *htmlbody; - return p->pgp_sign; + htmlbody = camel_text_to_html (body, CAMEL_MIME_FILTER_TOHTML_PRE, 0); + set_editor_text (composer, htmlbody, FALSE); + g_free (htmlbody); + } } - -/** - * e_msg_composer_set_pgp_encrypt: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "PGP Encrypt" flag set - * - * Set the status of the "PGP Encrypt" toggle item. The user can override it. - **/ -void -e_msg_composer_set_pgp_encrypt (EMsgComposer *composer, gboolean pgp_encrypt) +static void +handle_uri (EMsgComposer *composer, + const gchar *uri, + gboolean html_dnd) { EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if (p->pgp_encrypt && pgp_encrypt) - return; - if (!p->pgp_encrypt && !pgp_encrypt) - return; + GtkhtmlEditor *editor; + gboolean html_content; - p->pgp_encrypt = pgp_encrypt; - e_msg_composer_set_changed (composer); + editor = GTKHTML_EDITOR (composer); + html_content = gtkhtml_editor_get_html_mode (editor); - bonobo_ui_component_set_prop (p->uic, "/commands/SecurityPGPEncrypt", - "state", p->pgp_encrypt ? "1" : "0", NULL); -} + if (!g_ascii_strncasecmp (uri, "mailto:", 7)) { + handle_mailto (composer, uri); + } else { + CamelURL *url = camel_url_new (uri, NULL); + gchar *type; + if (!url) + return; -/** - * e_msg_composer_get_pgp_encrypt: - * @composer: A message composer widget - * - * Get the status of the "PGP Encrypt" flag. - * - * Return value: The status of the "PGP Encrypt" flag. - **/ -gboolean -e_msg_composer_get_pgp_encrypt (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + if (!g_ascii_strcasecmp (url->protocol, "file")) { + type = e_msg_composer_guess_mime_type (uri); + if (!type) + return; - return p->pgp_encrypt; + if (strncmp (type, "image", 5) || !html_dnd || (!html_content && !strncmp (type, "image", 5))) { + e_attachment_bar_attach (E_ATTACHMENT_BAR (p->attachment_bar), + url->path, "attachment"); + } + g_free (type); + } else { + e_attachment_bar_attach_remote_file (E_ATTACHMENT_BAR (p->attachment_bar), + uri, "attachment"); + } + camel_url_free (url); + } } - /** - * e_msg_composer_set_smime_sign: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "S/MIME Sign" flag set + * e_msg_composer_new_from_url: + * @url: a mailto URL * - * Set the status of the "S/MIME Sign" toggle item. The user can override it. + * Create a new message composer widget, and fill in fields as + * defined by the provided URL. **/ -void -e_msg_composer_set_smime_sign (EMsgComposer *composer, gboolean smime_sign) +EMsgComposer * +e_msg_composer_new_from_url (const gchar *url) { - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + EMsgComposer *composer; - if (p->smime_sign && smime_sign) - return; - if (!p->smime_sign && !smime_sign) - return; + g_return_val_if_fail (g_ascii_strncasecmp (url, "mailto:", 7) == 0, NULL); - p->smime_sign = smime_sign; - e_msg_composer_set_changed (composer); + composer = e_msg_composer_new (); + if (!composer) + return NULL; - bonobo_ui_component_set_prop (p->uic, "/commands/SecuritySMimeSign", - "state", p->smime_sign ? "1" : "0", NULL); -} + handle_mailto (composer, url); + return composer; +} /** - * e_msg_composer_get_smime_sign: - * @composer: A message composer widget - * - * Get the status of the "S/MIME Sign" flag. + * e_msg_composer_set_body_text: + * @composer: a composer object + * @text: the HTML text to initialize the editor with * - * Return value: The status of the "S/MIME Sign" flag. + * Loads the given HTML text into the editor. **/ -gboolean -e_msg_composer_get_smime_sign (EMsgComposer *composer) +void +e_msg_composer_set_body_text (EMsgComposer *composer, + const gchar *text, + gssize len) { - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (text != NULL); - return p->smime_sign; + set_editor_text (composer, text, TRUE); } - /** - * e_msg_composer_set_smime_encrypt: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "S/MIME Encrypt" flag set + * e_msg_composer_set_body: + * @composer: a composer object + * @body: the data to initialize the composer with + * @mime_type: the MIME type of data * - * Set the status of the "S/MIME Encrypt" toggle item. The user can override it. + * Loads the given data ginto the composer as the message body. + * This function should only be used by the CORBA composer factory. **/ void -e_msg_composer_set_smime_encrypt (EMsgComposer *composer, gboolean smime_encrypt) +e_msg_composer_set_body (EMsgComposer *composer, + const gchar *body, + const gchar *mime_type) { EMsgComposerPrivate *p = composer->priv; + EComposerHeaderTable *table; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - if (p->smime_encrypt && smime_encrypt) - return; - if (!p->smime_encrypt && !smime_encrypt) - return; + table = e_msg_composer_get_header_table (composer); - p->smime_encrypt = smime_encrypt; - e_msg_composer_set_changed (composer); + set_editor_text (composer, _("(The composer contains a non-text message body, which cannot be edited.)"), FALSE); + gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (composer), FALSE); + disable_editor (composer); - bonobo_ui_component_set_prop (p->uic, "/commands/SecuritySMimeEncrypt", - "state", p->smime_encrypt ? "1" : "0", NULL); -} + g_free (p->mime_body); + p->mime_body = g_strdup (body); + g_free (p->mime_type); + p->mime_type = g_strdup (mime_type); + if (g_ascii_strncasecmp (p->mime_type, "text/calendar", 13) == 0) { + EAccount *account; -/** - * e_msg_composer_get_smime_encrypt: - * @composer: A message composer widget - * - * Get the status of the "S/MIME Encrypt" flag. - * - * Return value: The status of the "S/MIME Encrypt" flag. - **/ -gboolean -e_msg_composer_get_smime_encrypt (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + account = e_composer_header_table_get_account (table); + if (account && account->pgp_no_imip_sign) { + GtkToggleAction *action; - return p->smime_encrypt; + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, FALSE); + } + } } - /** - * e_msg_composer_get_view_from: - * @composer: A message composer widget - * - * Get the status of the "View From header" flag. + * e_msg_composer_add_header: + * @composer: a composer object + * @name: the header name + * @value: the header value * - * Return value: The status of the "View From header" flag. + * Adds a header with @name and @value to the message. This header + * may not be displayed by the composer, but will be included in + * the message it outputs. **/ -gboolean -e_msg_composer_get_view_from (EMsgComposer *composer) +void +e_msg_composer_add_header (EMsgComposer *composer, + const gchar *name, + const gchar *value) { EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - return p->view_from; -} + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (name != NULL); + g_return_if_fail (value != NULL); + g_ptr_array_add (p->extra_hdr_names, g_strdup (name)); + g_ptr_array_add (p->extra_hdr_values, g_strdup (value)); +} /** - * e_msg_composer_set_view_from: - * @composer: A message composer widget - * @state: whether to show or hide the From selector + * e_msg_composer_modify_header : + * @composer : a composer object + * @name: the header name + * @change_value: the header value to put in place of the previous + * value * - * Controls the state of the From selector - */ + * Searches for a header with name=@name ,if found it removes + * that header and adds a new header with the @name and @change_value . + * If not found then it creates a new header with @name and @change_value . + **/ void -e_msg_composer_set_view_from (EMsgComposer *composer, gboolean view_from) +e_msg_composer_modify_header (EMsgComposer *composer, + const gchar *name, + const gchar *change_value) { - EMsgComposerPrivate *p = composer->priv; - GConfClient *gconf; - GError *error = NULL; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (name != NULL); + g_return_if_fail (change_value != NULL); - if ((p->view_from && view_from) || - (!p->view_from && !view_from)) - return; - - p->view_from = view_from; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewFrom", - "state", p->view_from ? "1" : "0", NULL); - - gconf = gconf_client_get_default (); - if (!gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/From", view_from, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - - g_object_unref (gconf); - - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); + e_msg_composer_remove_header (composer, name); + e_msg_composer_add_header (composer, name, change_value); } - /** - * e_msg_composer_get_view_replyto: - * @composer: A message composer widget - * - * Get the status of the "View Reply-To header" flag. + * e_msg_composer_modify_header : + * @composer : a composer object + * @name: the header name * - * Return value: The status of the "View Reply-To header" flag. + * Searches for the header and if found it removes it . **/ -gboolean -e_msg_composer_get_view_replyto (EMsgComposer *composer) +void +e_msg_composer_remove_header (EMsgComposer *composer, + const gchar *name) { - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + EMsgComposerPrivate *p; + gint i; - return p->view_replyto; -} + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (name != NULL); + + p = composer->priv; + for (i = 0; i < p->extra_hdr_names->len; i++) { + if (strcmp (p->extra_hdr_names->pdata[i], name) == 0) { + g_ptr_array_remove_index (p->extra_hdr_names, i); + g_ptr_array_remove_index (p->extra_hdr_values, i); + } + } +} /** - * e_msg_composer_set_view_replyto: - * @composer: A message composer widget - * @state: whether to show or hide the Reply-To selector + * e_msg_composer_attach: + * @composer: a composer object + * @attachment: the CamelMimePart to attach * - * Controls the state of the Reply-To selector - */ + * Attaches @attachment to the message being composed in the composer. + **/ void -e_msg_composer_set_view_replyto (EMsgComposer *composer, gboolean view_replyto) +e_msg_composer_attach (EMsgComposer *composer, CamelMimePart *attachment) { + EAttachmentBar *bar; EMsgComposerPrivate *p = composer->priv; - GConfClient *gconf; - GError *error = NULL; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if ((p->view_replyto && view_replyto) || - (!p->view_replyto && !view_replyto)) - return; - - p->view_replyto = view_replyto; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewReplyTo", - "state", p->view_replyto ? "1" : "0", NULL); - /* we do this /only/ if the fields is in the visible_mask */ - gconf = gconf_client_get_default (); - if (!gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/ReplyTo", view_replyto, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - g_object_unref (gconf); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_PART (attachment)); - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); + bar = E_ATTACHMENT_BAR (p->attachment_bar); + e_attachment_bar_attach_mime_part (bar, attachment); } - /** - * e_msg_composer_get_view_to: - * @composer: A message composer widget + * e_msg_composer_add_inline_image_from_file: + * @composer: a composer object + * @filename: the name of the file containing the image * - * Get the status of the "View To header" flag. + * This reads in the image in @filename and adds it to @composer + * as an inline image, to be wrapped in a multipart/related. * - * Return value: The status of the "View To header" flag. + * Returns: the newly-created CamelMimePart (which must be reffed + * if the caller wants to keep its own reference), or %NULL on error. **/ -gboolean -e_msg_composer_get_view_to (EMsgComposer *composer) +CamelMimePart * +e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, + const gchar *filename) { + gchar *mime_type, *cid, *url, *name, *dec_file_name; + CamelStream *stream; + CamelDataWrapper *wrapper; + CamelMimePart *part; EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - return p->view_to; -} + dec_file_name = g_strdup (filename); + camel_url_decode (dec_file_name); + if (!g_file_test (dec_file_name, G_FILE_TEST_IS_REGULAR)) + return NULL; -/** - * e_msg_composer_set_view_to: - * @composer: A message composer widget - * @state: whether to show or hide the To selector - * - * Controls the state of the To selector - */ -void -e_msg_composer_set_view_to (EMsgComposer *composer, gboolean view_to) -{ - EMsgComposerPrivate *p = composer->priv; - GConfClient *gconf; - GError *error = NULL; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + stream = camel_stream_fs_new_with_name (dec_file_name, O_RDONLY, 0); + if (!stream) + return NULL; - if ((p->view_to && view_to) || - (!p->view_to && !view_to)) - return; + wrapper = camel_data_wrapper_new (); + camel_data_wrapper_construct_from_stream (wrapper, stream); + camel_object_unref (CAMEL_OBJECT (stream)); - p->view_to = view_to; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewTo", - "state", p->view_to ? "1" : "0", NULL); + mime_type = e_msg_composer_guess_mime_type (dec_file_name); + if (mime_type == NULL) + mime_type = g_strdup ("application/octet-stream"); + camel_data_wrapper_set_mime_type (wrapper, mime_type); + g_free (mime_type); - if ((E_MSG_COMPOSER_HDRS(p->hdrs))->visible_mask & E_MSG_COMPOSER_VISIBLE_TO) { - gconf = gconf_client_get_default (); - if (!gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/To", view_to, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - g_object_unref (gconf); - } + part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper); + camel_object_unref (wrapper); - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} + cid = camel_header_msgid_generate (); + camel_mime_part_set_content_id (part, cid); + name = g_path_get_basename (dec_file_name); + camel_mime_part_set_filename (part, name); + g_free (name); + camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64); + url = g_strdup_printf ("file:%s", dec_file_name); + g_hash_table_insert (p->inline_images_by_url, url, part); + url = g_strdup_printf ("cid:%s", cid); + g_hash_table_insert (p->inline_images, url, part); + g_free (cid); -/** - * e_msg_composer_get_view_postto: - * @composer: A message composer widget - * - * Get the status of the "View PostTo header" flag. - * - * Return value: The status of the "View PostTo header" flag. - **/ -gboolean -e_msg_composer_get_view_postto (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + g_free (dec_file_name); - return p->view_postto; + return part; } - /** - * e_msg_composer_set_view_postto: - * @composer: A message composer widget - * @state: whether to show or hide the PostTo selector + * e_msg_composer_add_inline_image_from_mime_part: + * @composer: a composer object + * @part: a CamelMimePart containing image data * - * Controls the state of the PostTo selector - */ + * This adds the mime part @part to @composer as an inline image, to + * be wrapped in a multipart/related. + **/ void -e_msg_composer_set_view_postto (EMsgComposer *composer, gboolean view_postto) +e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer, + CamelMimePart *part) { - GConfClient *gconf; - GError *error = NULL; - + gchar *url; + const gchar *location, *cid; EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if ((p->view_postto && view_postto) || - (!p->view_postto && !view_postto)) - return; - - p->view_postto = view_postto; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewPostTo", - "state", p->view_postto ? "1" : "0", NULL); - - if ((E_MSG_COMPOSER_HDRS(p->hdrs))->visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO) { - gconf = gconf_client_get_default (); - if (!gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/PostTo", view_postto, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - g_object_unref (gconf); + cid = camel_mime_part_get_content_id (part); + if (!cid) { + camel_mime_part_set_content_id (part, NULL); + cid = camel_mime_part_get_content_id (part); } - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} - + url = g_strdup_printf ("cid:%s", cid); + g_hash_table_insert (p->inline_images, url, part); + camel_object_ref (part); + location = camel_mime_part_get_content_location (part); + if (location != NULL) + g_hash_table_insert ( + p->inline_images_by_url, + g_strdup (location), part); +} /** - * e_msg_composer_get_view_cc: + * e_msg_composer_get_message: * @composer: A message composer widget * - * Get the status of the "View CC header" flag. + * Retrieve the message edited by the user as a CamelMimeMessage. The + * CamelMimeMessage object is created on the fly; subsequent calls to this + * function will always create new objects from scratch. * - * Return value: The status of the "View CC header" flag. + * Returns: A pointer to the new CamelMimeMessage object **/ -gboolean -e_msg_composer_get_view_cc (EMsgComposer *composer) +CamelMimeMessage * +e_msg_composer_get_message (EMsgComposer *composer, + gboolean save_html_object_data) { - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + GtkhtmlEditor *editor; + gboolean html_content; - return p->view_cc; -} + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + + if (e_msg_composer_get_remote_download_count (composer) != 0) { + if (!em_utils_prompt_user (GTK_WINDOW (composer), NULL, + "mail-composer:ask-send-message-pending-download", NULL)) { + return NULL; + } + } + editor = GTKHTML_EDITOR (composer); + html_content = gtkhtml_editor_get_html_mode (editor); -/** - * e_msg_composer_set_view_cc: - * @composer: A message composer widget - * @state: whether to show or hide the cc view - * - * Controls the state of the CC display - */ -void -e_msg_composer_set_view_cc (EMsgComposer *composer, gboolean view_cc) + return build_message (composer, html_content, save_html_object_data); +} + +static gchar * +msg_composer_get_message_print_helper (EMsgComposer *composer, + gboolean html_content) { - GConfClient *gconf; - GError *error = NULL; - EMsgComposerPrivate *p = composer->priv; + GtkToggleAction *action; + GString *string; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + string = g_string_sized_new (128); - if ((p->view_cc && view_cc) || - (!p->view_cc && !view_cc)) - return; + if (html_content) + g_string_append (string, "text/html"); + else + g_string_append (string, "text/plain"); - p->view_cc = view_cc; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewCC", - "state", p->view_cc ? "1" : "0", NULL); + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + if (gtk_toggle_action_get_active (action)) + g_string_append (string, ", pgp-sign"); + gtk_toggle_action_set_active (action, FALSE); - if ((E_MSG_COMPOSER_HDRS (p->hdrs))->visible_mask & E_MSG_COMPOSER_VISIBLE_CC) { - gconf = gconf_client_get_default (); - if (!gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/Cc", view_cc, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - g_object_unref (gconf); - } + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + if (gtk_toggle_action_get_active (action)) + g_string_append (string, ", pgp-encrypt"); + gtk_toggle_action_set_active (action, FALSE); - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + if (gtk_toggle_action_get_active (action)) + g_string_append (string, ", smime-sign"); + gtk_toggle_action_set_active (action, FALSE); + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + if (gtk_toggle_action_get_active (action)) + g_string_append (string, ", smime-encrypt"); + gtk_toggle_action_set_active (action, FALSE); + return g_string_free (string, FALSE); +} -/** - * e_msg_composer_get_view_bcc: - * @composer: A message composer widget - * - * Get the status of the "View BCC header" flag. - * - * Return value: The status of the "View BCC header" flag. - **/ -gboolean -e_msg_composer_get_view_bcc (EMsgComposer *composer) +CamelMimeMessage * +e_msg_composer_get_message_print (EMsgComposer *composer, + gboolean save_html_object_data) { - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + EMsgComposer *temp_composer; + CamelMimeMessage *msg; + gboolean html_content; + gchar *flags; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + editor = GTKHTML_EDITOR (composer); + html_content = gtkhtml_editor_get_html_mode (editor); - return p->view_bcc; -} + msg = build_message (composer, html_content, save_html_object_data); + if (msg == NULL) + return NULL; + temp_composer = e_msg_composer_new_with_message (msg); + camel_object_unref (msg); -/** - * e_msg_composer_set_view_bcc: - * @composer: A message composer widget - * @state: whether to show or hide the bcc view - * - * Controls the state of the BCC display - */ -void -e_msg_composer_set_view_bcc (EMsgComposer *composer, gboolean view_bcc) + /* Override composer flags. */ + flags = msg_composer_get_message_print_helper ( + temp_composer, html_content); + + msg = build_message (temp_composer, TRUE, save_html_object_data); + if (msg != NULL) + camel_medium_set_header ( + CAMEL_MEDIUM (msg), "X-Evolution-Format", flags); + + gtk_widget_destroy (GTK_WIDGET (temp_composer)); + g_free (flags); + + return msg; +} + +CamelMimeMessage * +e_msg_composer_get_message_draft (EMsgComposer *composer) { - GConfClient *gconf; - GError *error = NULL; - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + EComposerHeaderTable *table; + GtkToggleAction *action; + CamelMimeMessage *msg; + EAccount *account; + gboolean html_content; + gboolean pgp_encrypt; + gboolean pgp_sign; + gboolean smime_encrypt; + gboolean smime_sign; + GString *flags; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + editor = GTKHTML_EDITOR (composer); + table = e_msg_composer_get_header_table (composer); + html_content = gtkhtml_editor_get_html_mode (editor); - if ((p->view_bcc && view_bcc) || - (!p->view_bcc && !view_bcc)) - return; + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + pgp_sign = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); - p->view_bcc = view_bcc; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewBCC", - "state", p->view_bcc ? "1" : "0", NULL); + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + pgp_encrypt = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); - if ((E_MSG_COMPOSER_HDRS (p->hdrs))->visible_mask & E_MSG_COMPOSER_VISIBLE_BCC) { - gconf = gconf_client_get_default (); - if (!gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/Bcc", view_bcc, &error)) { - g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); - g_error_free(error); - } - g_object_unref (gconf); - } + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + smime_sign = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + smime_encrypt = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); + msg = build_message (composer, TRUE, TRUE); + if (msg == NULL) + return NULL; + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, pgp_sign); -/** - * e_msg_composer_get_request_receipt - * @composer: A message composer widget - * - * Get the status of the "Request receipt" flag. - * - * Return value: The status of the "Request receipt" flag. - **/ -gboolean -e_msg_composer_get_request_receipt (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + gtk_toggle_action_set_active (action, pgp_encrypt); - return p->request_receipt; -} + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + gtk_toggle_action_set_active (action, smime_sign); + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + gtk_toggle_action_set_active (action, smime_encrypt); -/** - * e_msg_composer_set_request_receipt: - * @composer: A message composer widget - * @state: whether to request or not a receipt - * - * If set, a message delivery notification request will be sent to the recipient - */ -void -e_msg_composer_set_request_receipt (EMsgComposer *composer, gboolean request_receipt) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + if (msg == NULL) + return NULL; - if ((p->request_receipt && request_receipt) || - (!p->request_receipt && !request_receipt)) - return; + /* Attach account info to the draft. */ + account = e_composer_header_table_get_account (table); + if (account && account->name) + camel_medium_set_header ( + CAMEL_MEDIUM (msg), + "X-Evolution-Account", account->uid); - p->request_receipt = request_receipt; - bonobo_ui_component_set_prop (p->uic, "/commands/RequestReceipt", - "state", p->request_receipt ? "1" : "0", NULL); -} + flags = g_string_new (html_content ? "text/html" : "text/plain"); + /* This should probably only save the setting if it is + * different from the from-account default? */ + if (pgp_sign) + g_string_append (flags, ", pgp-sign"); + if (pgp_encrypt) + g_string_append (flags, ", pgp-encrypt"); + if (smime_sign) + g_string_append (flags, ", smime-sign"); + if (smime_encrypt) + g_string_append (flags, ", smime-encrypt"); -/** - * e_msg_composer_get_priority - * @composer: A message composer widget - * - * Get the status of the "Priority" flag. - * - * Return value: The status of the "Priority" flag. - **/ -gboolean -e_msg_composer_get_priority (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + camel_medium_set_header ( + CAMEL_MEDIUM (msg), "X-Evolution-Format", flags->str); + g_string_free (flags, TRUE); - return p->set_priority; + return msg; } - /** - * e_msg_composer_set_priority: + * e_msg_composer_show_sig: * @composer: A message composer widget - * @state: whether to set priority or not * - * If set, a message is sent with a high priority - */ + * Set a signature + **/ void -e_msg_composer_set_priority (EMsgComposer *composer, gboolean set_priority) +e_msg_composer_show_sig_file (EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + GtkHTML *html; + gchar *html_text; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - if ((p->set_priority && set_priority) || - (!p->set_priority && !set_priority)) + editor = GTKHTML_EDITOR (composer); + html = gtkhtml_editor_get_html (editor); + + if (composer->priv->redirect) return; - p->set_priority= set_priority; - bonobo_ui_component_set_prop (p->uic, "/commands/SetPriority", - "state", p->set_priority ? "1" : "0", NULL); -} + composer->priv->in_signature_insert = TRUE; -EDestination ** -e_msg_composer_get_recipients (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + gtkhtml_editor_freeze (editor); + gtkhtml_editor_run_command (editor, "cursor-position-save"); + gtkhtml_editor_undo_begin (editor, "Set signature", "Reset signature"); - return p->hdrs ? e_msg_composer_hdrs_get_recipients (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; -} + /* Delete the old signature. */ + gtkhtml_editor_run_command (editor, "block-selection"); + gtkhtml_editor_run_command (editor, "cursor-bod"); + if (gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1")) { + gtkhtml_editor_run_command (editor, "select-paragraph"); + gtkhtml_editor_run_command (editor, "delete"); + gtkhtml_editor_set_paragraph_data (editor, "signature", "0"); + gtkhtml_editor_run_command (editor, "delete-back"); + } + gtkhtml_editor_run_command (editor, "unblock-selection"); -EDestination ** -e_msg_composer_get_to (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + html_text = get_signature_html (composer); + if (html_text) { + gtkhtml_editor_run_command (editor, "insert-paragraph"); + if (!gtkhtml_editor_run_command (editor, "cursor-backward")) + gtkhtml_editor_run_command (editor, "insert-paragraph"); + else + gtkhtml_editor_run_command (editor, "cursor-forward"); + gtkhtml_editor_set_paragraph_data (editor, "orig", "0"); + gtkhtml_editor_run_command (editor, "indent-zero"); + gtkhtml_editor_run_command (editor, "style-normal"); + gtkhtml_editor_insert_html (editor, html_text); + g_free (html_text); + } - return p->hdrs ? e_msg_composer_hdrs_get_to (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; + gtkhtml_editor_undo_end (editor); + gtkhtml_editor_run_command (editor, "cursor-position-restore"); + gtkhtml_editor_thaw (editor); + + composer->priv->in_signature_insert = FALSE; } -EDestination ** -e_msg_composer_get_cc (EMsgComposer *composer) +CamelInternetAddress * +e_msg_composer_get_from (EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; + CamelInternetAddress *address; + EComposerHeaderTable *table; + EAccount *account; + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - return p->hdrs ? e_msg_composer_hdrs_get_cc (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; -} + table = e_msg_composer_get_header_table (composer); -EDestination ** -e_msg_composer_get_bcc (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + account = e_composer_header_table_get_account (table); + if (account == NULL) + return NULL; + + address = camel_internet_address_new (); + camel_internet_address_add ( + address, account->id->name, account->id->address); - return p->hdrs ? e_msg_composer_hdrs_get_bcc (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; + return address; } -const char * -e_msg_composer_get_subject (EMsgComposer *composer) +CamelInternetAddress * +e_msg_composer_get_reply_to (EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; + CamelInternetAddress *address; + EComposerHeaderTable *table; + const gchar *reply_to; + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - return p->hdrs ? e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; -} + table = e_msg_composer_get_header_table (composer); + + reply_to = e_composer_header_table_get_reply_to (table); + if (reply_to == NULL || *reply_to == '\0') + return NULL; + + address = camel_internet_address_new (); + if (camel_address_unformat (CAMEL_ADDRESS (address), reply_to) == -1) { + camel_object_unref (CAMEL_OBJECT (address)); + return NULL; + } + return address; +} /** * e_msg_composer_guess_mime_type: - * @file_name: filename + * @filename: filename * - * Returns the guessed mime type of the file given by #file_name. + * Returns the guessed mime type of the file given by @filename. **/ -char * -e_msg_composer_guess_mime_type (const char *file_name) +gchar * +e_msg_composer_guess_mime_type (const gchar *filename) { GnomeVFSFileInfo *info; GnomeVFSResult result; - char *type = NULL; + gchar *type = NULL; + + g_return_val_if_fail (filename != NULL, NULL); info = gnome_vfs_file_info_new (); - result = gnome_vfs_get_file_info (file_name, info, - GNOME_VFS_FILE_INFO_GET_MIME_TYPE | - GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE | - GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + result = gnome_vfs_get_file_info ( + filename, info, + GNOME_VFS_FILE_INFO_GET_MIME_TYPE | + GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE | + GNOME_VFS_FILE_INFO_FOLLOW_LINKS); if (result == GNOME_VFS_OK) type = g_strdup (gnome_vfs_file_info_get_mime_type (info)); @@ -6203,89 +4534,6 @@ e_msg_composer_guess_mime_type (const char *file_name) return type; } - -/** - * e_msg_composer_set_changed: - * @composer: An EMsgComposer object. - * - * Mark the composer as changed, so before the composer gets destroyed - * the user will be prompted about unsaved changes. - **/ -void -e_msg_composer_set_changed (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - p->has_changed = TRUE; -} - - -/** - * e_msg_composer_unset_changed: - * @composer: An EMsgComposer object. - * - * Mark the composer as unchanged, so no prompt about unsaved changes - * will appear before destroying the composer. - **/ -void -e_msg_composer_unset_changed (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - p->has_changed = FALSE; -} - -gboolean -e_msg_composer_is_dirty (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - gboolean rv; - - CORBA_exception_init (&ev); - rv = p->has_changed - || (GNOME_GtkHTML_Editor_Engine_hasUndo (p->eeditor_engine, &ev) && - !GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "is-saved", &ev)); - CORBA_exception_free (&ev); - - return rv; -} - -/** - * e_msg_composer_set_autosaved: - * @composer: An EMsgComposer object. - * - * Mark the composer as autosaved, so before the composer gets destroyed - * the user will be prompted about unsaved changes. - **/ -void -e_msg_composer_set_autosaved (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - p->autosaved = TRUE; -} - - -/** - * e_msg_composer_unset_autosaved: - * @composer: An EMsgComposer object. - * - * Mark the composer as unautosaved, so no prompt about unsaved changes - * will appear before destroying the composer. - **/ -void -e_msg_composer_unset_autosaved (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - p->autosaved = FALSE; -} - /** * e_msg_composer_get_raw_message_text: * @@ -6294,334 +4542,176 @@ e_msg_composer_unset_autosaved (EMsgComposer *composer) GByteArray * e_msg_composer_get_raw_message_text (EMsgComposer *composer) { - GByteArray *data = NULL; - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - - data = get_text (p->persist_stream_interface, "text/plain"); - if (data) - return data; - - return NULL; -} - -EAttachmentBar* -e_msg_composer_get_attachment_bar (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + GByteArray *array; + gchar *text; + gsize length; - return (EAttachmentBar*) p->attachment_bar; -} + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); -gboolean -e_msg_composer_is_autosaved (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + array = g_byte_array_new (); + editor = GTKHTML_EDITOR (composer); + text = gtkhtml_editor_get_text_plain (editor, &length); + g_byte_array_append (array, (guint8 *) text, (guint) length); + g_free (text); - return p->autosaved; + return array; } -void -e_msg_composer_set_enable_autosave (EMsgComposer *composer, gboolean enabled) +EAttachmentBar * +e_msg_composer_get_attachment_bar (EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - p->enable_autosave = enabled; + return E_ATTACHMENT_BAR (composer->priv->attachment_bar); } void -e_msg_composer_drop_editor_undo (EMsgComposer *composer) +e_msg_composer_set_enable_autosave (EMsgComposer *composer, + gboolean enabled) { - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_dropUndo (p->eeditor_engine, &ev); - CORBA_exception_free (&ev); + e_composer_autosave_set_enabled (composer, enabled); } - gboolean e_msg_composer_request_close_all (void) { - GSList *p, *pnext; + GSList *iter, *next; - for (p = all_composers; p != NULL; p = pnext) { - pnext = p->next; - do_exit (E_MSG_COMPOSER (p->data)); + for (iter = all_composers; iter != NULL; iter = next) { + EMsgComposer *composer = iter->data; + next = iter->next; + gtk_action_activate (ACTION (CLOSE)); } - if (all_composers == NULL) - return TRUE; - else - return FALSE; -} - -void -e_msg_composer_check_autosave(GtkWindow *parent) -{ - if (am == NULL) - am = autosave_manager_new(); - - if (am->ask) { - am->ask = FALSE; - autosave_manager_query_load_orphans (am, parent); - am->ask = TRUE; - } + return (all_composers == NULL); } -void -e_msg_composer_show_attachments_ui (EMsgComposer *composer) +EMsgComposer * +e_msg_composer_load_from_file (const gchar *filename) { - EMsgComposerPrivate *p = composer->priv; + CamelStream *stream; + CamelMimeMessage *msg; + EMsgComposer *composer; - if (e_attachment_bar_get_num_attachments(E_ATTACHMENT_BAR(p->attachment_bar))) - show_attachments (composer, TRUE); -} + g_return_val_if_fail (filename != NULL, NULL); -void -e_msg_composer_set_alternative (EMsgComposer *composer, gboolean alt) -{ - EMsgComposerPrivate *p = composer->priv; + stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0); + if (stream == NULL) + return NULL; - p->is_alternative = alt; - p->send_html = !alt; -} + msg = camel_mime_message_new (); + camel_data_wrapper_construct_from_stream ( + CAMEL_DATA_WRAPPER (msg), stream); + camel_object_unref (stream); -void -e_msg_composer_reply_indent (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; + composer = e_msg_composer_new_with_message (msg); + if (composer != NULL) { + g_signal_connect ( + composer, "send", + G_CALLBACK (em_utils_composer_send_cb), NULL); - CORBA_exception_init (&ev); + g_signal_connect ( + composer, "save-draft", + G_CALLBACK (em_utils_composer_save_draft_cb), NULL); - if (!GNOME_GtkHTML_Editor_Engine_isParagraphEmpty (p->eeditor_engine, &ev)) { - if (GNOME_GtkHTML_Editor_Engine_isPreviousParagraphEmpty (p->eeditor_engine, &ev)) - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "cursor-backward", &ev); - else { - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "text-default-color", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "insert-paragraph", &ev); - return; - } + gtk_widget_show (GTK_WIDGET (composer)); } - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "style-normal", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "indent-zero", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "text-default-color", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - - CORBA_exception_free (&ev); - + return composer; } void -e_msg_composer_insert_paragraph_before (EMsgComposer *composer) +e_msg_composer_check_autosave (GtkWindow *parent) { - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - if (!p->in_signature_insert) { - CORBA_char *orig, *signature; - gboolean changed = FALSE; - /* FIXME check for insert-paragraph command */ + GList *orphans = NULL; + gint response; + GError *error = NULL; - orig = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "orig", &ev); - if (ev._major == CORBA_NO_EXCEPTION) { - if (orig && *orig == '1') { - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "text-default-color", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - changed = TRUE; - } - CORBA_free (orig); - } - if (!changed) { - signature = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "signature", &ev); - if (ev._major == CORBA_NO_EXCEPTION) { - if (signature && *signature == '1') { - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "text-default-color", - &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - } - CORBA_free (signature); - } + /* Look for orphaned autosave files. */ + orphans = e_composer_autosave_find_orphans (&error); + if (orphans == NULL) { + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); } + return; } - CORBA_exception_free (&ev); -} - -static void -clear_signature (GNOME_GtkHTML_Editor_Engine e, CORBA_Environment * ev) -{ - if (GNOME_GtkHTML_Editor_Engine_isParagraphEmpty (e, ev)) - GNOME_GtkHTML_Editor_Engine_setParagraphData (e, "signature", "0", ev); - else if (GNOME_GtkHTML_Editor_Engine_isPreviousParagraphEmpty (e, ev) - && GNOME_GtkHTML_Editor_Engine_runCommand (e, "cursor-backward", ev)) { - GNOME_GtkHTML_Editor_Engine_setParagraphData (e, "signature", "0", ev); - GNOME_GtkHTML_Editor_Engine_runCommand (e, "cursor-forward", ev); - } - GNOME_GtkHTML_Editor_Engine_runCommand (e, "text-default-color", ev); - GNOME_GtkHTML_Editor_Engine_runCommand (e, "italic-off", ev); -} - -void -e_msg_composer_insert_paragraph_after (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - if (!p->in_signature_insert) { - CORBA_char *orig, *signature; - /* FIXME check for insert-paragraph command */ - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - - orig = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "orig", &ev); - if (ev._major == CORBA_NO_EXCEPTION) { - if (orig && *orig == '1') - e_msg_composer_reply_indent (composer); - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "orig", "0", &ev); - CORBA_free (orig); - } - signature = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "signature", &ev); - if (ev._major == CORBA_NO_EXCEPTION) { - if (signature && *signature == '1') - clear_signature (p->eeditor_engine, &ev); - CORBA_free (signature); - } - } - - CORBA_exception_free (&ev); -} - -void -e_msg_composer_delete (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - if (GNOME_GtkHTML_Editor_Engine_isParagraphEmpty (p->eeditor_engine, &ev)) { - CORBA_char *orig; - CORBA_char *signature; + /* Ask if the user wants to recover the orphaned files. */ + response = e_error_run ( + parent, "mail-composer:recover-autosave", NULL); - orig = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "orig", &ev); - if (ev._major == CORBA_NO_EXCEPTION) { - if (orig && *orig == '1') { - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "orig", "0", &ev); + /* Based on the user's response, recover or delete them. */ + while (orphans != NULL) { + const gchar *filename = orphans->data; + EMsgComposer *composer; - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "indent-zero", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "style-normal", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "text-default-color", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "italic-off", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "insert-paragraph", &ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "delete-back", &ev); - } - CORBA_free (orig); - } - signature = GNOME_GtkHTML_Editor_Engine_getParagraphData (p->eeditor_engine, "signature", &ev); - if (ev._major == CORBA_NO_EXCEPTION) { - if (signature && *signature == '1') - GNOME_GtkHTML_Editor_Engine_setParagraphData (p->eeditor_engine, "signature", "0", &ev); - CORBA_free (signature); + if (response == GTK_RESPONSE_YES) { + /* FIXME: composer is never used */ + composer = autosave_load_draft (filename); + } else { + g_unlink (filename); } - } - CORBA_exception_free (&ev); -} - - -gchar * -e_msg_composer_resolve_image_url (EMsgComposer *composer, gchar *url) -{ - EMsgComposerPrivate *p = composer->priv; - CamelMimePart *part; - const char *cid; - part = g_hash_table_lookup (p->inline_images_by_url, url); - if (!part && !strncmp (url, "file:", 5)) { - part = e_msg_composer_add_inline_image_from_file (composer, url + 5); - } - if (!part && !strncmp (url, "cid:", 4)) { - part = g_hash_table_lookup (p->inline_images, url); + g_free (orphans->data); + orphans = g_list_delete_link (orphans, orphans); } - if (!part) - return NULL; - - p->current_images = g_list_prepend (p->current_images, part); - - cid = camel_mime_part_get_content_id (part); - if (!cid) - return NULL; - - return g_strconcat ("cid:", cid, NULL); } -CamelMimePart* -e_msg_composer_url_requested (EMsgComposer *composer, gchar *url) +void +e_msg_composer_set_alternative (EMsgComposer *composer, + gboolean alt) { - EMsgComposerPrivate *p = composer->priv; - CamelMimePart *part = NULL; + GtkhtmlEditor *editor; - part = g_hash_table_lookup (p->inline_images_by_url, url); - if (!part) - part = g_hash_table_lookup (p->inline_images, url); - if (!part) - return NULL; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - return part; + editor = GTKHTML_EDITOR (composer); + + composer->priv->is_alternative = alt; + gtkhtml_editor_set_html_mode (editor, !alt); } void -e_msg_composer_link_clicked (EMsgComposer *composer, const gchar *url) +e_msg_composer_reply_indent (EMsgComposer *composer) { - g_return_if_fail (composer != NULL); + GtkhtmlEditor *editor; - if (url && *url && - g_ascii_strncasecmp (url, "mailto:", 7) && - g_ascii_strncasecmp (url, "thismessage:", 12) && - g_ascii_strncasecmp (url, "cid:", 4)) { - GError *err = NULL; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - gnome_url_show (url, &err); + editor = GTKHTML_EDITOR (composer); - if (err) { - g_warning ("gnome_url_show: %s", err->message); - g_error_free (err); + if (!gtkhtml_editor_is_paragraph_empty (editor)) { + if (gtkhtml_editor_is_previous_paragraph_empty (editor)) + gtkhtml_editor_run_command (editor, "cursor-backward"); + else { + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); + gtkhtml_editor_run_command (editor, "insert-paragraph"); + return; } } -} -EMsgComposerHdrs* -e_msg_composer_get_hdrs (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - return (EMsgComposerHdrs*)p->hdrs; + gtkhtml_editor_run_command (editor, "style-normal"); + gtkhtml_editor_run_command (editor, "indent-zero"); + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); } -void -e_msg_composer_set_saved (EMsgComposer *composer) +EComposerHeaderTable * +e_msg_composer_get_header_table (EMsgComposer *composer) { - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "saved", &ev); - CORBA_exception_free (&ev); + return E_COMPOSER_HEADER_TABLE (composer->priv->header_table); } void -e_msg_composer_set_send_options (EMsgComposer *composer, gboolean send_enable) +e_msg_composer_set_send_options (EMsgComposer *composer, + gboolean send_enable) { - EMsgComposerPrivate *priv; - priv = composer->priv; + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - priv->send_invoked = send_enable; + composer->priv->send_invoked = send_enable; } -- cgit v1.2.3