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/ChangeLog | 46 + composer/Composer.idl | 3 - composer/Evolution-Composer.idl | 141 - composer/Makefile.am | 54 +- composer/e-composer-actions.c | 692 +++ composer/e-composer-actions.h | 71 + composer/e-composer-autosave.c | 422 ++ composer/e-composer-autosave.h | 44 + composer/e-composer-common.h | 19 + composer/e-composer-from-header.c | 74 +- composer/e-composer-from-header.h | 24 +- composer/e-composer-header-table.c | 1104 +++++ composer/e-composer-header-table.h | 162 + composer/e-composer-header.c | 76 +- composer/e-composer-header.h | 22 + composer/e-composer-name-header.c | 21 +- composer/e-composer-name-header.h | 19 + composer/e-composer-post-header.c | 19 + composer/e-composer-post-header.h | 21 +- composer/e-composer-private.c | 290 ++ composer/e-composer-private.h | 145 + composer/e-composer-text-header.c | 19 + composer/e-composer-text-header.h | 19 + composer/e-msg-composer-hdrs.c | 967 ---- composer/e-msg-composer-hdrs.h | 170 - composer/e-msg-composer-select-file.c | 203 - composer/e-msg-composer-select-file.h | 36 - composer/e-msg-composer.c | 7926 +++++++++++++-------------------- composer/e-msg-composer.h | 309 +- composer/evolution-composer.c | 395 -- composer/evolution-composer.h | 75 - composer/evolution-composer.ui | 61 + composer/gconf-bridge.c | 1249 ++++++ composer/gconf-bridge.h | 117 + composer/listener.c | 156 - composer/listener.h | 51 - 36 files changed, 7858 insertions(+), 7364 deletions(-) delete mode 100644 composer/Composer.idl delete mode 100644 composer/Evolution-Composer.idl create mode 100644 composer/e-composer-actions.c create mode 100644 composer/e-composer-actions.h create mode 100644 composer/e-composer-autosave.c create mode 100644 composer/e-composer-autosave.h create mode 100644 composer/e-composer-header-table.c create mode 100644 composer/e-composer-header-table.h create mode 100644 composer/e-composer-private.c create mode 100644 composer/e-composer-private.h delete mode 100644 composer/e-msg-composer-hdrs.c delete mode 100644 composer/e-msg-composer-hdrs.h delete mode 100644 composer/e-msg-composer-select-file.c delete mode 100644 composer/e-msg-composer-select-file.h delete mode 100644 composer/evolution-composer.c delete mode 100644 composer/evolution-composer.h create mode 100644 composer/evolution-composer.ui create mode 100644 composer/gconf-bridge.c create mode 100644 composer/gconf-bridge.h delete mode 100644 composer/listener.c delete mode 100644 composer/listener.h (limited to 'composer') diff --git a/composer/ChangeLog b/composer/ChangeLog index c01c8e10b5..db4dc8b316 100644 --- a/composer/ChangeLog +++ b/composer/ChangeLog @@ -1,3 +1,49 @@ +2008-04-02 Matthew Barnes + + ** Adapt to GtkHTML's new editor widget. + + * Makefile.am: + Remove CORBA rules for the old composer. + + * e-msg-composer.c: + * e-msg-composer.h: + EMsgComposer is now a subclass of GtkhtmlEditor. + Extensive refactoring and cleanup, too much to list in detail. + + * e-composer-header.c: + * e-composer-header.h: + Add "sensitive" property along with get/set functions. + + * e-composer-from-header.c: + * e-composer-from-header.h: + Propagate "refreshed" signal from EAccountComboBox. + Add function e_composer_from_header_get_account_list(). + + * e-composer-private.c: + * e-composer-private.h: + New files manage composer's private data. + Allows other composer files to manipulate private data. + + * e-msg-composer-hdrs.c: + * e-msg-composer-hdrs.h: + Remove these files; replaced by EComposerHeaderTable widget. + + * evolution-composer.c: + * evolution-composer.h: + Remove these files; composer is now a subclass of GtkhtmlEditor. + + * e-msg-composer-select-file.c: + * e-msg-composer-select-file.h: + Remove these files; logic moved to e-msg-composer.c. + + * listener.c: + * listener.h: + Remove these files; event handlers moved to e-msg-composer.c. + + * Composer.idl: + * Evolution-Composer.idl: + Remove these files; composer is no longer a Bonobo object. + 2008-03-11 Matthew Barnes ** Fixes part of bug #513951 diff --git a/composer/Composer.idl b/composer/Composer.idl deleted file mode 100644 index 0f2e47e517..0000000000 --- a/composer/Composer.idl +++ /dev/null @@ -1,3 +0,0 @@ -/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -#include diff --git a/composer/Evolution-Composer.idl b/composer/Evolution-Composer.idl deleted file mode 100644 index 5dc62a3317..0000000000 --- a/composer/Evolution-Composer.idl +++ /dev/null @@ -1,141 +0,0 @@ -/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Evolution-Composer.idl: Mail composer interfaces for Evolution - * - * Author: - * Dan Winship - * - * (C) 2000 Ximian, Inc. - */ - -#include - -module GNOME { -module Evolution { - - interface Composer : Bonobo::Unknown { - struct Recipient { - string name; /* UTF-8 */ - string address; - }; - typedef sequence RecipientList; - - typedef sequence AttachmentData; - - enum MultipartType { - MIXED, - ALTERNATIVE - }; - - /** - * setHeaders: - * @from: the "From" account or address - * @to: the "To" recipients - * @cc: the "CC" recipients - * @bcc: the "Bcc" recipients - * @subject: the subject of the message - * - * Sets the composer headers. Any of @to, @cc, and - * @bcc may be an empty list, and @subject may be an - * empty string. If @from is empty or invalid, the - * default account will be used. Otherwise is - * specifies an account name or email address to send - * from. - **/ - void setHeaders (in string from, in RecipientList to, - in RecipientList cc, in RecipientList bcc, - in string subject); - - /** - * setMultipartType: - * @type: a multipart subtype - * - * Sets the kind of multipart message that is being - * created. - * - * If @type is MIXED (the default), setBody() - * will create the body, and attachMIME() and - * attachData() will create attachments. - * - * If @type is ALTERNATIVE, setBody() will create - * text/plain alternative, and each following - * attachMIME() or attachData() call will create - * another alternative. - * - * Other values of @type are not currently supported, - * although "related" probably should be. - **/ - void setMultipartType (in MultipartType type); - - /** - * setBody: - * @body: the body - * @mime_type: the MIME type of @body - * - * Sets the body of the composer to @body. If - * @mime_type is something other than "text/plain" or - * "text/html", the composer will not be editable - * and it will not attempt to assign a non-UTF8 - * character set to the data. However, @mime_type may - * include parameters in that case. - **/ - void setBody (in string body, in string mime_type); - - /** - * attachMIME: - * @data: the attachment data - * - * This adds an attachment to the composer. @data - * should be a fully-formed MIME body part. - **/ - exception CouldNotParse {}; - void attachMIME (in string data) - raises (CouldNotParse); - - /** - * attachData: - * @content_type: the Content-Type header - * @filename: the suggested filename, or "" - * @description: a description of the data, or "" - * @show_inline: whether the attachment should be - * displayed inline or not. - * @data: the raw attachment data - * - * This adds @data as an attachment, using the provided - * information to generate MIME headers. @content_type - * may contain just a MIME content type, or it may - * contain a complete Content-Type header. @filename - * is a filename for the Content-Disposition header - * @description (if not "") provides the - * Content-Description, and @show_inline determines if the - * Content-Disposition is "inline" or "attachment". - * - * If you need to specify headers or values other than - * what this function can do, you will need to generate - * all of the MIME headers yourself and use - * add_attachment (). - **/ - void attachData (in string content_type, - in string filename, - in string description, - in boolean show_inline, - in AttachmentData data); - - /** - * show: - * - * Shows the composer and lets the user edit things - * and send the message. - **/ - void show (); - - - /** - * send: - * - * Send the message without showing the user the composer - **/ - void send (); - }; -}; -}; diff --git a/composer/Makefile.am b/composer/Makefile.am index 1edbe0dec7..562687a1d4 100644 --- a/composer/Makefile.am +++ b/composer/Makefile.am @@ -1,40 +1,9 @@ -## CORBA stuff - -IDLS = \ - Evolution-Composer.idl \ - Composer.idl - -IDL_GENERATED = \ - Composer.h \ - Composer-common.c \ - Composer-skels.c \ - Composer-stubs.c - -HTML_EDITOR_GENERATED = \ - Editor.h \ - Editor-common.c \ - Editor-skels.c \ - Editor-stubs.c - -$(IDL_GENERATED): $(IDLS) - $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl $(IDL_INCLUDES) \ - $(srcdir)/Composer.idl - -Editor-commmon.c: $(GTKHTML_DATADIR)/Editor.idl - -$(HTML_EDITOR_GENERATED): $(GTKHTML_DATADIR)/Editor.idl - $(ORBIT_IDL) -I $(srcdir) $(IDL_INCLUDES) -I $(GTKHTML_DATADIR)/gtkhtml $(GTKHTML_DATADIR)/Editor.idl - -## - error_DATA = mail-composer.error errordir = $(privdatadir)/errors # provides error rule @EVO_PLUGIN_RULE@ -idl_DATA = $(IDLS) - noinst_LTLIBRARIES = libcomposer.la INCLUDES = \ @@ -58,31 +27,36 @@ INCLUDES = \ libcomposer_la_SOURCES = \ $(IDL_GENERATED) \ $(HTML_EDITOR_GENERATED) \ + e-composer-actions.c \ + e-composer-actions.h \ + e-composer-autosave.c \ + e-composer-autosave.h \ e-composer-common.h \ e-composer-header.c \ e-composer-header.h \ + e-composer-header-table.c \ + e-composer-header-table.h \ e-composer-from-header.c \ e-composer-from-header.h \ e-composer-name-header.c \ e-composer-name-header.h \ e-composer-post-header.c \ e-composer-post-header.h \ + e-composer-private.c \ + e-composer-private.h \ e-composer-text-header.c \ e-composer-text-header.h \ - e-msg-composer-hdrs.c \ - e-msg-composer-hdrs.h \ - e-msg-composer-select-file.c \ - e-msg-composer-select-file.h \ e-msg-composer.c \ e-msg-composer.h \ - evolution-composer.c \ - evolution-composer.h \ - listener.c \ - listener.h + gconf-bridge.c \ + gconf-bridge.h + +uidir = $(evolutionuidir) +ui_DATA = evolution-composer.ui EXTRA_DIST = \ + $(ui_DATA) \ mail-composer.error.xml \ - $(IDLS) \ ChangeLog.pre-1-4 BUILT_SOURCES = $(IDL_GENERATED) $(HTML_EDITOR_GENERATED) $(error_DATA) diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c new file mode 100644 index 0000000000..0bb49cfcf3 --- /dev/null +++ b/composer/e-composer-actions.c @@ -0,0 +1,692 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "e-composer-actions.h" +#include "e-composer-private.h" + +#include +#include +#include +#include +#include + +#include "misc/e-charset-picker.h" + +static void +action_attach_cb (GtkAction *action, + EMsgComposer *composer) +{ + EAttachmentBar *bar; + GtkWidget *dialog; + GtkWidget *option; + GSList *uris, *iter; + gboolean active; + gint response; + + bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar); + + dialog = gtk_file_chooser_dialog_new ( + _("Insert Attachment"), + GTK_WINDOW (composer), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("A_ttach"), GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_file_chooser_set_local_only ( + GTK_FILE_CHOOSER (dialog), FALSE); + gtk_file_chooser_set_select_multiple ( + GTK_FILE_CHOOSER (dialog), TRUE); + gtk_window_set_icon_name ( + GTK_WINDOW (dialog), "mail-message-new"); + + option = gtk_check_button_new_with_mnemonic ( + _("_Suggest automatic display of attachment")); + gtk_widget_show (option); + gtk_file_chooser_set_extra_widget ( + GTK_FILE_CHOOSER (dialog), option); + + response = gtkhtml_editor_file_chooser_dialog_run ( + GTKHTML_EDITOR (composer), dialog); + + if (response != GTK_RESPONSE_OK) + goto exit; + + uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog)); + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option)); + + for (iter = uris; iter != NULL; iter = iter->next) { + const gchar *disposition; + CamelURL *url; + + url = camel_url_new (iter->data, NULL); + if (url == NULL) + continue; + + disposition = active ? "inline" : "attachment"; + if (!g_ascii_strcasecmp (url->protocol, "file")) + e_attachment_bar_attach (bar, url->path, disposition); + else + e_attachment_bar_attach (bar, iter->data, disposition); + + camel_url_free (url); + } + + g_slist_foreach (uris, (GFunc) g_free, NULL); + g_slist_free (uris); + +exit: + gtk_widget_destroy (dialog); +} + +static void +action_charset_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMsgComposer *composer) +{ + const gchar *charset; + + if (action != current) + return; + + charset = gtk_action_get_name (GTK_ACTION (current)); + + g_free (composer->priv->charset); + composer->priv->charset = g_strdup (charset); +} + +static void +action_close_cb (GtkAction *action, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + EComposerHeaderTable *table; + const gchar *subject; + gint response; + + editor = GTKHTML_EDITOR (composer); + + if (!gtkhtml_editor_get_changed (editor) && + !e_composer_autosave_get_saved (composer)) { + + gtk_widget_destroy (GTK_WIDGET (composer)); + return; + } + + gdk_window_raise (GTK_WIDGET (composer)->window); + + table = e_msg_composer_get_header_table (composer); + subject = e_composer_header_table_get_subject (table); + + if (subject == NULL || *subject == '\0') + subject = _("Untitled Message"); + + response = e_error_run ( + GTK_WINDOW (composer), + "mail-composer:exit-unsaved", + subject, NULL); + + switch (response) { + case GTK_RESPONSE_YES: + gtk_action_activate (ACTION (SAVE_DRAFT)); + break; + + case GTK_RESPONSE_NO: + gtk_widget_destroy (GTK_WIDGET (composer)); + break; + + case GTK_RESPONSE_CANCEL: + break; + } +} + +static void +action_pgp_encrypt_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_set_changed (editor, TRUE); +} + +static void +action_pgp_sign_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_set_changed (editor, TRUE); +} + +static void +action_print_cb (GtkAction *action, + EMsgComposer *composer) +{ + GtkPrintOperationAction print_action; + CamelMimeMessage *message; + EMFormatHTMLPrint *efhp; + + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + message = e_msg_composer_get_message (composer, 1); + + efhp = em_format_html_print_new (NULL, print_action); + em_format_html_print_raw_message (efhp, message); + g_object_unref (efhp); +} + +static void +action_print_preview_cb (GtkAction *action, + EMsgComposer *composer) +{ + GtkPrintOperationAction print_action; + CamelMimeMessage *message; + EMFormatHTMLPrint *efhp; + + print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW; + message = e_msg_composer_get_message_print (composer, 1); + + efhp = em_format_html_print_new (NULL, print_action); + em_format_html_print_raw_message (efhp, message); + g_object_unref (efhp); +} + +static void +action_save_cb (GtkAction *action, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor = GTKHTML_EDITOR (composer); + const gchar *filename; + gint fd; + GError *error = NULL; + + filename = gtkhtml_editor_get_filename (editor); + if (filename == NULL) { + gtk_action_activate (ACTION (SAVE_AS)); + return; + } + + /* Check if the file already exists and we can create it. */ + fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, 0777); + if (fd < 0) { + gint errno_saved = errno; + + if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { + gint response; + + response = e_error_run ( + GTK_WINDOW (composer), + E_ERROR_ASK_FILE_EXISTS_OVERWRITE, + filename, NULL); + if (response != GTK_RESPONSE_OK) + return; + } else { + e_error_run ( + GTK_WINDOW (composer), + E_ERROR_NO_SAVE_FILE, filename, + g_strerror (errno_saved)); + return; + } + } else + close (fd); + + if (!gtkhtml_editor_save (editor, filename, TRUE, &error)) { + e_error_run ( + GTK_WINDOW (composer), + E_ERROR_NO_SAVE_FILE, + filename, error->message); + g_error_free (error); + return; + } + + gtkhtml_editor_run_command (GTKHTML_EDITOR (composer), "saved"); + e_composer_autosave_set_saved (composer, FALSE); +} + +static void +action_save_as_cb (GtkAction *action, + EMsgComposer *composer) +{ + GtkWidget *dialog; + gchar *filename; + gint response; + + dialog = gtk_file_chooser_dialog_new ( + _("Save as..."), GTK_WINDOW (composer), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_file_chooser_set_local_only ( + GTK_FILE_CHOOSER (dialog), FALSE); + gtk_window_set_icon_name ( + GTK_WINDOW (dialog), "mail-message-new"); + + response = gtkhtml_editor_file_chooser_dialog_run ( + GTKHTML_EDITOR (composer), dialog); + + if (response != GTK_RESPONSE_OK) + goto exit; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + gtkhtml_editor_set_filename (GTKHTML_EDITOR (composer), filename); + g_free (filename); + + gtk_action_activate (ACTION (SAVE)); + +exit: + gtk_widget_destroy (dialog); +} + +static void +action_save_draft_cb (GtkAction *action, + EMsgComposer *composer) +{ + e_msg_composer_save_draft (composer); +} + +static void +action_send_cb (GtkAction *action, + EMsgComposer *composer) +{ + e_msg_composer_send (composer); +} + +static void +action_send_options_cb (GtkAction *action, + EMsgComposer *composer) +{ + EMEvent *event = em_event_peek (); + EMEventTargetComposer *target; + + target = em_event_target_new_composer ( + event, composer, EM_EVENT_COMPOSER_SEND_OPTION); + e_msg_composer_set_send_options (composer, FALSE); + + e_event_emit ( + (EEvent *) event, + "composer.selectsendoption", + (EEventTarget *) target); + + if (!composer->priv->send_invoked) + e_error_run ( + GTK_WINDOW (composer), + "mail-component:send-options-support", NULL); +} + +static void +action_smime_encrypt_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_set_changed (editor, TRUE); +} + +static void +action_smime_sign_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_set_changed (editor, TRUE); +} + +static void +action_view_bcc_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_BCC, active); +} + +static void +action_view_cc_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_CC, active); +} + +static void +action_view_from_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_FROM, active); +} + +static void +action_view_post_to_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_POST_TO, active); +} + +static void +action_view_reply_to_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_REPLY_TO, active); +} + +static void +action_view_subject_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_SUBJECT, active); +} + +static void +action_view_to_cb (GtkToggleAction *action, + EMsgComposer *composer) +{ + EComposerHeaderTable *table; + gboolean active; + + table = e_msg_composer_get_header_table (composer); + active = gtk_toggle_action_get_active (action); + + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_TO, active); +} + +static GtkActionEntry entries[] = { + + { "attach", + "mail-attachment", + N_("_Attachment..."), + "m", + N_("Attach a file"), + G_CALLBACK (action_attach_cb) }, + + { "close", + GTK_STOCK_CLOSE, + N_("_Close"), + "w", + N_("Close the current file"), + G_CALLBACK (action_close_cb) }, + + { "print", + GTK_STOCK_PRINT, + N_("_Print..."), + "p", + NULL, + G_CALLBACK (action_print_cb) }, + + { "print-preview", + GTK_STOCK_PRINT_PREVIEW, + N_("Print Pre_view"), + "p", + NULL, + G_CALLBACK (action_print_preview_cb) }, + + { "save", + GTK_STOCK_SAVE, + N_("_Save"), + "s", + N_("Save the current file"), + G_CALLBACK (action_save_cb) }, + + { "save-as", + GTK_STOCK_SAVE_AS, + N_("Save _As..."), + NULL, + N_("Save the current file with a different name"), + G_CALLBACK (action_save_as_cb) }, + + { "save-draft", + GTK_STOCK_SAVE, + N_("Save _Draft"), + "s", + N_("Save as draft"), + G_CALLBACK (action_save_draft_cb) }, + + { "send", + "mail-send", + N_("S_end"), + "Return", + N_("Send this message"), + G_CALLBACK (action_send_cb) }, + + { "send-options", + NULL, + N_("_Send Options"), + NULL, + N_("Insert Send options"), + G_CALLBACK (action_send_options_cb) }, + + /* Menus */ + + { "charset-menu", + NULL, + N_("Ch_aracter Encoding"), + NULL, + NULL, + NULL }, + + { "security-menu", + NULL, + N_("_Security"), + NULL, + NULL, + NULL } +}; + +static GtkToggleActionEntry toggle_entries[] = { + + { "pgp-encrypt", + NULL, + N_("PGP _Encrypt"), + NULL, + N_("Encrypt this message with PGP"), + G_CALLBACK (action_pgp_encrypt_cb), + FALSE }, + + { "pgp-sign", + NULL, + N_("PGP _Sign"), + NULL, + N_("Sign this message with your PGP key"), + G_CALLBACK (action_pgp_sign_cb), + FALSE }, + + { "prioritize-message", + NULL, + N_("_Prioritize Message"), + NULL, + N_("Set the message priority to high"), + NULL, /* no callback */ + FALSE }, + + { "request-read-receipt", + NULL, + N_("R_equest Read Receipt"), + NULL, + N_("Get delivery notification when your message is read"), + NULL, /* no callback */ + FALSE }, + + { "smime-encrypt", + NULL, + N_("S/MIME En_crypt"), + NULL, + N_("Encrypt this message with your S/MIME Encryption Certificate"), + G_CALLBACK (action_smime_encrypt_cb), + FALSE }, + + { "smime-sign", + NULL, + N_("S/MIME Sig_n"), + NULL, + N_("Sign this message with your S/MIME Signature Certificate"), + G_CALLBACK (action_smime_sign_cb), + FALSE }, + + { "view-bcc", + NULL, + N_("_Bcc Field"), + NULL, + N_("Toggles whether the BCC field is displayed"), + G_CALLBACK (action_view_bcc_cb), + FALSE }, + + { "view-cc", + NULL, + N_("_Cc Field"), + NULL, + N_("Toggles whether the CC field is displayed"), + G_CALLBACK (action_view_cc_cb), + FALSE }, + + { "view-from", + NULL, + N_("_From Field"), + NULL, + N_("Toggles whether the From chooser is displayed"), + G_CALLBACK (action_view_from_cb), + FALSE }, + + { "view-post-to", + NULL, + N_("_Post-To Field"), + NULL, + N_("Toggles whether the Post-To field is displayed"), + G_CALLBACK (action_view_post_to_cb), + FALSE }, + + { "view-reply-to", + NULL, + N_("_Reply-To Field"), + NULL, + N_("Toggles whether the Reply-To field is displayed"), + G_CALLBACK (action_view_reply_to_cb), + FALSE }, + + { "view-subject", + NULL, + N_("_Subject Field"), + NULL, + N_("Toggles whether the Subject field is displayed"), + G_CALLBACK (action_view_subject_cb), + FALSE }, + + { "view-to", + NULL, + N_("_To Field"), + NULL, + N_("Toggles whether the To field is displayed"), + G_CALLBACK (action_view_to_cb), + FALSE } +}; + +void +e_composer_actions_init (EMsgComposer *composer) +{ + GtkActionGroup *action_group; + GtkUIManager *manager; + gboolean visible; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer)); + + /* Composer Actions */ + action_group = composer->priv->composer_actions; + gtk_action_group_set_translation_domain ( + action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions ( + action_group, entries, + G_N_ELEMENTS (entries), composer); + gtk_action_group_add_toggle_actions ( + action_group, toggle_entries, + G_N_ELEMENTS (toggle_entries), composer); + gtk_ui_manager_insert_action_group (manager, action_group, 0); + + /* Character Set Actions */ + action_group = composer->priv->charset_actions; + gtk_action_group_set_translation_domain ( + action_group, GETTEXT_PACKAGE); + e_charset_add_radio_actions ( + action_group, composer->priv->charset, + G_CALLBACK (action_charset_cb), composer); + gtk_ui_manager_insert_action_group (manager, action_group, 0); + + /* Fine Tuning */ + + g_object_set ( + G_OBJECT (ACTION (ATTACH)), + "short-label", _("Attach"), NULL); + +#if defined (HAVE_NSS) && defined (SMIME_SUPPORTED) + visible = TRUE; +#else + visible = FALSE; +#endif + + gtk_action_set_visible (ACTION (SMIME_ENCRYPT), visible); + gtk_action_set_visible (ACTION (SMIME_SIGN), visible); +} diff --git a/composer/e-composer-actions.h b/composer/e-composer-actions.h new file mode 100644 index 0000000000..16058da9ec --- /dev/null +++ b/composer/e-composer-actions.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_COMPOSER_ACTIONS_H +#define E_COMPOSER_ACTIONS_H + +#define E_COMPOSER_ACTION(composer, name) \ + (gtkhtml_editor_get_action (GTKHTML_EDITOR (composer), (name))) + +#define E_COMPOSER_ACTION_ATTACH(composer) \ + E_COMPOSER_ACTION ((composer), "attach") +#define E_COMPOSER_ACTION_CLOSE(composer) \ + E_COMPOSER_ACTION ((composer), "close") +#define E_COMPOSER_ACTION_PGP_ENCRYPT(composer) \ + E_COMPOSER_ACTION ((composer), "pgp-encrypt") +#define E_COMPOSER_ACTION_PGP_SIGN(composer) \ + E_COMPOSER_ACTION ((composer), "pgp-sign") +#define E_COMPOSER_ACTION_PRINT(composer) \ + E_COMPOSER_ACTION ((composer), "print") +#define E_COMPOSER_ACTION_PRINT_PREVIEW(composer) \ + E_COMPOSER_ACTION ((composer), "print-preview") +#define E_COMPOSER_ACTION_PRIORITIZE_MESSAGE(composer) \ + E_COMPOSER_ACTION ((composer), "prioritize-message") +#define E_COMPOSER_ACTION_REQUEST_READ_RECEIPT(composer) \ + E_COMPOSER_ACTION ((composer), "request-read-receipt") +#define E_COMPOSER_ACTION_SAVE(composer) \ + E_COMPOSER_ACTION ((composer), "save") +#define E_COMPOSER_ACTION_SAVE_AS(composer) \ + E_COMPOSER_ACTION ((composer), "save-as") +#define E_COMPOSER_ACTION_SAVE_DRAFT(composer) \ + E_COMPOSER_ACTION ((composer), "save-draft") +#define E_COMPOSER_ACTION_SECURITY_MENU(composer) \ + E_COMPOSER_ACTION ((composer), "security-menu") +#define E_COMPOSER_ACTION_SEND(composer) \ + E_COMPOSER_ACTION ((composer), "send") +#define E_COMPOSER_ACTION_SMIME_ENCRYPT(composer) \ + E_COMPOSER_ACTION ((composer), "smime-encrypt") +#define E_COMPOSER_ACTION_SMIME_SIGN(composer) \ + E_COMPOSER_ACTION ((composer), "smime-sign") +#define E_COMPOSER_ACTION_VIEW_BCC(composer) \ + E_COMPOSER_ACTION ((composer), "view-bcc") +#define E_COMPOSER_ACTION_VIEW_CC(composer) \ + E_COMPOSER_ACTION ((composer), "view-cc") +#define E_COMPOSER_ACTION_VIEW_FROM(composer) \ + E_COMPOSER_ACTION ((composer), "view-from") +#define E_COMPOSER_ACTION_VIEW_POST_TO(composer) \ + E_COMPOSER_ACTION ((composer), "view-post-to") +#define E_COMPOSER_ACTION_VIEW_REPLY_TO(composer) \ + E_COMPOSER_ACTION ((composer), "view-reply-to") +#define E_COMPOSER_ACTION_VIEW_SUBJECT(composer) \ + E_COMPOSER_ACTION ((composer), "view-subject") +#define E_COMPOSER_ACTION_VIEW_TO(composer) \ + E_COMPOSER_ACTION ((composer), "view-to") + +#endif /* E_COMPOSER_ACTIONS_H */ diff --git a/composer/e-composer-autosave.c b/composer/e-composer-autosave.c new file mode 100644 index 0000000000..eda3a033e2 --- /dev/null +++ b/composer/e-composer-autosave.c @@ -0,0 +1,422 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "e-composer-autosave.h" + +#include +#include +#include +#include + +#include +#include +#include + +#define AUTOSAVE_PREFIX ".evolution-composer.autosave" +#define AUTOSAVE_SEED AUTOSAVE_PREFIX "-XXXXXX" +#define AUTOSAVE_INTERVAL 60000 /* 60 seconds */ + +typedef struct _AutosaveState AutosaveState; + +struct _AutosaveState { + gchar *filename; + gboolean enabled; + gboolean saved; + gint fd; +}; + +static GList *autosave_registry; +static guint autosave_source_id; + +static EMsgComposer * +composer_autosave_registry_lookup (const gchar *basename) +{ + GList *iter; + + /* Find the composer with the given autosave filename. */ + for (iter = autosave_registry; iter != NULL; iter = iter->next) { + EMsgComposer *composer = iter->data; + AutosaveState *state; + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + if (state == NULL || state->filename == NULL) + continue; + + if (g_str_has_suffix (state->filename, basename)) + return composer; + } + + return NULL; +} + +static AutosaveState * +composer_autosave_state_new (void) +{ + AutosaveState *state; + + state = g_slice_new (AutosaveState); + state->filename = NULL; + state->enabled = TRUE; + state->fd = -1; + + return state; +} + +static void +composer_autosave_state_free (AutosaveState *state) +{ + if (state->fd >= 0) + close (state->fd); + + g_free (state->filename); + g_slice_free (AutosaveState, state); +} + +static gboolean +composer_autosave_state_open (AutosaveState *state, + GError **error) +{ + if (state->filename != NULL) + return TRUE; + + state->filename = g_build_filename ( + e_get_user_data_dir (), AUTOSAVE_SEED, NULL); + + errno = 0; + if ((state->fd = g_mkstemp (state->filename)) >= 0) + return TRUE; + + g_set_error ( + error, G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s: %s", state->filename, g_strerror (errno)); + + g_free (state->filename); + state->filename = NULL; + + return FALSE; +} + +static void +composer_autosave_foreach (EMsgComposer *composer) +{ + /* Make sure the composer is still alive. */ + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + if (e_composer_autosave_get_enabled (composer)) + e_composer_autosave_snapshot (composer); +} + +static gint +composer_autosave_timeout (void) +{ + g_list_foreach ( + autosave_registry, (GFunc) + composer_autosave_foreach, NULL); + + return TRUE; +} + +static void +composer_autosave_notify (gpointer unused, + GObject *where_the_object_was) +{ + /* Remove the dead composer from the registry. */ + autosave_registry = g_list_remove ( + autosave_registry, where_the_object_was); + + /* Cancel timeouts if the registry is now empty. */ + if (autosave_registry == NULL && autosave_source_id != 0) { + g_source_remove (autosave_source_id); + autosave_source_id = 0; + } +} + +GList * +e_composer_autosave_find_orphans (GError **error) +{ + GDir *dir; + const gchar *dirname; + const gchar *basename; + GList *orphans = NULL; + + dirname = e_get_user_data_dir (); + dir = g_dir_open (dirname, 0, error); + if (dir == NULL) + return NULL; + + /* Scan the user directory for autosave files. */ + while ((basename = g_dir_read_name (dir)) != NULL) { + const gchar *errmsg; + gchar *filename; + struct stat st; + + /* Is this an autosave file? */ + if (!g_str_has_prefix (basename, AUTOSAVE_PREFIX)) + continue; + + /* Is this an orphaned autosave file? */ + if (composer_autosave_registry_lookup (basename) != NULL) + continue; + + filename = g_build_filename (dirname, basename, NULL); + + /* Try to examine the autosave file. Failure here + * is non-fatal; just emit a warning and move on. */ + errno = 0; + if (g_stat (filename, &st) < 0) { + errmsg = g_strerror (errno); + g_warning ("%s: %s", filename, errmsg); + g_free (filename); + continue; + } + + /* If the file is empty, delete it. Failure here + * is non-fatal; just emit a warning and move on. */ + if (st.st_size == 0) { + errno = 0; + if (g_unlink (filename) < 0) { + errmsg = g_strerror (errno); + g_warning ("%s: %s", filename, errmsg); + } + g_free (filename); + continue; + } + + orphans = g_list_prepend (orphans, filename); + } + + g_dir_close (dir); + + return g_list_reverse (orphans); +} + +void +e_composer_autosave_register (EMsgComposer *composer) +{ + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + g_object_set_data_full ( + G_OBJECT (composer), "autosave", + composer_autosave_state_new (), + (GDestroyNotify) composer_autosave_state_free); + + autosave_registry = g_list_prepend (autosave_registry, composer); + + g_object_weak_ref ( + G_OBJECT (composer), (GWeakNotify) + composer_autosave_notify, NULL); + + if (autosave_source_id == 0) + autosave_source_id = g_timeout_add ( + AUTOSAVE_INTERVAL, (GSourceFunc) + composer_autosave_timeout, NULL); +} + +void +e_composer_autosave_unregister (EMsgComposer *composer) +{ + AutosaveState *state; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + if (state == NULL || state->filename == NULL) + return; + + if (e_composer_autosave_snapshot (composer)) { + close (state->fd); + g_unlink (state->filename); + } else + close (state->fd); + + g_object_set_data (G_OBJECT (composer), "autosave", NULL); +} + +gboolean +e_composer_autosave_snapshot (EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + CamelMimeMessage *message; + AutosaveState *state; + CamelStream *stream; + gint camelfd; + const gchar *errmsg; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + + editor = GTKHTML_EDITOR (composer); + + /* If the contents are unchanged, exit early. */ + if (!gtkhtml_editor_get_changed (editor)) + return TRUE; + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_val_if_fail (state != NULL, FALSE); + + /* Open the autosave file on-demand. */ + if (!composer_autosave_state_open (state, NULL)) { + errmsg = _("Could not open autosave file"); + goto fail; + } + + /* Extract a MIME message from the composer. */ + message = e_msg_composer_get_message_draft (composer); + if (message == NULL) { + errmsg = _("Unable to retrieve message from editor"); + goto fail; + } + + /* Move to the beginning of the autosave file. */ + if (lseek (state->fd, (off_t) 0, SEEK_SET) < 0) { + camel_object_unref (message); + errmsg = g_strerror (errno); + goto fail; + } + + /* Destroy the contents of the autosave file. */ + if (ftruncate (state->fd, (off_t) 0) < 0) { + camel_object_unref (message); + errmsg = g_strerror (errno); + goto fail; + } + + /* Duplicate the file descriptor for Camel. */ + if ((camelfd = dup (state->fd)) < 0) { + camel_object_unref (message); + errmsg = g_strerror (errno); + goto fail; + } + + /* Open a CamelStream to the autosave file. */ + stream = camel_stream_fs_new_with_fd (camelfd); + + /* Write the message to the CamelStream. */ + if (camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream) < 0) { + camel_object_unref (message); + camel_object_unref (stream); + errmsg = g_strerror (errno); + goto fail; + } + + /* Close the CamelStream. */ + if (camel_stream_close (CAMEL_STREAM (stream)) < 0) { + camel_object_unref (message); + camel_object_unref (stream); + errmsg = g_strerror (errno); + goto fail; + } + + /* Snapshot was successful; set various flags. */ + gtkhtml_editor_set_changed (editor, FALSE); + e_composer_autosave_set_saved (composer, TRUE); + + camel_object_unref (message); + camel_object_unref (stream); + + return TRUE; + +fail: + e_error_run ( + GTK_WINDOW (composer), "mail-composer:no-autosave", + (state->filename != NULL) ? state->filename : "", + errmsg, NULL); + + return FALSE; +} + +gint +e_composer_autosave_get_fd (EMsgComposer *composer) +{ + AutosaveState *state; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), -1); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_val_if_fail (state != NULL, -1); + + return state->fd; +} + +const gchar * +e_composer_autosave_get_filename (EMsgComposer *composer) +{ + AutosaveState *state; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_val_if_fail (state != NULL, NULL); + + return state->filename; +} + +gboolean +e_composer_autosave_get_enabled (EMsgComposer *composer) +{ + AutosaveState *state; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_val_if_fail (state != NULL, FALSE); + + return state->enabled; +} + +void +e_composer_autosave_set_enabled (EMsgComposer *composer, + gboolean enabled) +{ + AutosaveState *state; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_if_fail (state != NULL); + + state->enabled = enabled; +} + +gboolean +e_composer_autosave_get_saved (EMsgComposer *composer) +{ + AutosaveState *state; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_val_if_fail (state != NULL, FALSE); + + return state->saved; +} + +void +e_composer_autosave_set_saved (EMsgComposer *composer, + gboolean saved) +{ + AutosaveState *state; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + state = g_object_get_data (G_OBJECT (composer), "autosave"); + g_return_if_fail (state != NULL); + + state->saved = saved; +} diff --git a/composer/e-composer-autosave.h b/composer/e-composer-autosave.h new file mode 100644 index 0000000000..b2db7b7dac --- /dev/null +++ b/composer/e-composer-autosave.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_COMPOSER_AUTOSAVE_H +#define E_COMPOSER_AUTOSAVE_H + +#include "e-composer-common.h" +#include "e-msg-composer.h" + +G_BEGIN_DECLS + +GList * e_composer_autosave_find_orphans (GError **error); + +void e_composer_autosave_register (EMsgComposer *composer); +void e_composer_autosave_unregister (EMsgComposer *composer); +gboolean e_composer_autosave_snapshot (EMsgComposer *composer); +gint e_composer_autosave_get_fd (EMsgComposer *composer); +const gchar * e_composer_autosave_get_filename (EMsgComposer *composer); +gboolean e_composer_autosave_get_enabled (EMsgComposer *composer); +void e_composer_autosave_set_enabled (EMsgComposer *composer, + gboolean enabled); +gboolean e_composer_autosave_get_saved (EMsgComposer *composer); +void e_composer_autosave_set_saved (EMsgComposer *composer, + gboolean saved); + +G_END_DECLS + +#endif /* E_COMPOSER_AUTOSAVE_H */ diff --git a/composer/e-composer-common.h b/composer/e-composer-common.h index f03b7ca344..ac16e0ccce 100644 --- a/composer/e-composer-common.h +++ b/composer/e-composer-common.h @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #ifndef E_COMPOSER_COMMON #define E_COMPOSER_COMMON diff --git a/composer/e-composer-from-header.c b/composer/e-composer-from-header.c index 55f525326e..21a961d746 100644 --- a/composer/e-composer-from-header.c +++ b/composer/e-composer-from-header.c @@ -1,10 +1,35 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #include "e-composer-from-header.h" /* Convenience macro */ #define E_COMPOSER_FROM_HEADER_GET_COMBO_BOX(header) \ (E_ACCOUNT_COMBO_BOX (E_COMPOSER_HEADER (header)->input_widget)) +enum { + REFRESHED, + LAST_SIGNAL +}; + static gpointer parent_class; +static guint signal_ids[LAST_SIGNAL]; static void composer_from_header_changed_cb (EAccountComboBox *combo_box, @@ -13,10 +38,25 @@ composer_from_header_changed_cb (EAccountComboBox *combo_box, g_signal_emit_by_name (header, "changed"); } +static void +composer_from_header_refreshed_cb (EAccountComboBox *combo_box, + EComposerFromHeader *header) +{ + g_signal_emit (header, signal_ids[REFRESHED], 0); +} + static void composer_from_header_class_init (EComposerFromHeaderClass *class) { parent_class = g_type_class_peek_parent (class); + + signal_ids[REFRESHED] = g_signal_new ( + "refreshed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -28,8 +68,10 @@ composer_from_header_init (EComposerFromHeader *header) g_signal_connect ( widget, "changed", G_CALLBACK (composer_from_header_changed_cb), header); + g_signal_connect ( + widget, "refreshed", + G_CALLBACK (composer_from_header_refreshed_cb), header); E_COMPOSER_HEADER (header)->input_widget = widget; - gtk_widget_show (widget); } GType @@ -67,6 +109,17 @@ e_composer_from_header_new (const gchar *label) "button", FALSE, NULL); } +EAccountList * +e_composer_from_header_get_account_list (EComposerFromHeader *header) +{ + EAccountComboBox *combo_box; + + g_return_val_if_fail (E_IS_COMPOSER_FROM_HEADER (header), NULL); + + combo_box = E_COMPOSER_FROM_HEADER_GET_COMBO_BOX (header); + return e_account_combo_box_get_account_list (combo_box); +} + void e_composer_from_header_set_account_list (EComposerFromHeader *header, EAccountList *account_list) @@ -124,22 +177,3 @@ e_composer_from_header_set_active_name (EComposerFromHeader *header, combo_box = E_COMPOSER_FROM_HEADER_GET_COMBO_BOX (header); return e_account_combo_box_set_active_name (combo_box, account_name); } - -CamelInternetAddress * -e_composer_from_header_get_active_address (EComposerFromHeader *header) -{ - CamelInternetAddress *address; - EAccount *account; - - g_return_val_if_fail (E_IS_COMPOSER_FROM_HEADER (header), NULL); - - account = e_composer_from_header_get_active (header); - if (account == NULL) - return NULL; - - address = camel_internet_address_new (); - camel_internet_address_add ( - address, account->id->name, account->id->address); - - return address; -} diff --git a/composer/e-composer-from-header.h b/composer/e-composer-from-header.h index cc5aa78235..11e1229522 100644 --- a/composer/e-composer-from-header.h +++ b/composer/e-composer-from-header.h @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #ifndef E_COMPOSER_FROM_HEADER_H #define E_COMPOSER_FROM_HEADER_H @@ -5,7 +24,6 @@ #include #include -#include #include "e-account-combo-box.h" #include "e-composer-header.h" @@ -44,6 +62,8 @@ struct _EComposerFromHeaderClass { GType e_composer_from_header_get_type (void); EComposerHeader * e_composer_from_header_new (const gchar *label); +EAccountList * e_composer_from_header_get_account_list + (EComposerFromHeader *header); void e_composer_from_header_set_account_list (EComposerFromHeader *header, EAccountList *account_list); @@ -57,8 +77,6 @@ const gchar * e_composer_from_header_get_active_name gboolean e_composer_from_header_set_active_name (EComposerFromHeader *header, const gchar *account_name); -CamelInternetAddress * e_composer_from_header_get_active_address - (EComposerFromHeader *header); G_END_DECLS diff --git a/composer/e-composer-header-table.c b/composer/e-composer-header-table.c new file mode 100644 index 0000000000..5fdff8e1ee --- /dev/null +++ b/composer/e-composer-header-table.c @@ -0,0 +1,1104 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "e-composer-header-table.h" + +#include +#include +#include + +#include "e-signature-combo-box.h" + +#include "e-composer-from-header.h" +#include "e-composer-name-header.h" +#include "e-composer-post-header.h" +#include "e-composer-text-header.h" + +#define E_COMPOSER_HEADER_TABLE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_COMPOSER_HEADER_TABLE, EComposerHeaderTablePrivate)) + +#define E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER(table) \ + (E_COMPOSER_FROM_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_FROM))) + +#define E_COMPOSER_HEADER_TABLE_GET_REPLY_TO_HEADER(table) \ + (E_COMPOSER_TEXT_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_REPLY_TO))) + +#define E_COMPOSER_HEADER_TABLE_GET_TO_HEADER(table) \ + (E_COMPOSER_NAME_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_TO))) + +#define E_COMPOSER_HEADER_TABLE_GET_CC_HEADER(table) \ + (E_COMPOSER_NAME_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_CC))) + +#define E_COMPOSER_HEADER_TABLE_GET_BCC_HEADER(table) \ + (E_COMPOSER_NAME_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_BCC))) + +#define E_COMPOSER_HEADER_TABLE_GET_POST_TO_HEADER(table) \ + (E_COMPOSER_POST_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_POST_TO))) + +#define E_COMPOSER_HEADER_TABLE_GET_SUBJECT_HEADER(table) \ + (E_COMPOSER_TEXT_HEADER (e_composer_header_table_get_header \ + (E_COMPOSER_HEADER_TABLE (table), E_COMPOSER_HEADER_SUBJECT))) + +#define HEADER_TOOLTIP_TO \ + _("Enter the recipients of the message") +#define HEADER_TOOLTIP_CC \ + _("Enter the addresses that will receive a " \ + "carbon copy of the message") +#define HEADER_TOOLTIP_BCC \ + _("Enter the addresses that will receive a " \ + "carbon copy of the message without appearing " \ + "in the recipient list of the message") + +enum { + PROP_0, + PROP_ACCOUNT, + PROP_ACCOUNT_LIST, + PROP_ACCOUNT_NAME, + PROP_DESTINATIONS_BCC, + PROP_DESTINATIONS_CC, + PROP_DESTINATIONS_TO, + PROP_POST_TO, + PROP_REPLY_TO, + PROP_SIGNATURE, + PROP_SIGNATURE_LIST, + PROP_SUBJECT +}; + +struct _EComposerHeaderTablePrivate { + EComposerHeader *headers[E_COMPOSER_NUM_HEADERS]; + GtkWidget *signature_label; + GtkWidget *signature_combo_box; + ENameSelector *name_selector; +}; + +static gpointer parent_class; + +static void +g_value_set_destinations (GValue *value, + EDestination **destinations) +{ + GValueArray *value_array; + GValue element; + gint ii; + + memset (&element, 0, sizeof (GValue)); + g_value_init (&element, E_TYPE_DESTINATION); + + /* Preallocate some reasonable number. */ + value_array = g_value_array_new (64); + + for (ii = 0; destinations[ii] != NULL; ii++) { + g_value_set_object (&element, destinations[ii]); + g_value_array_append (value_array, &element); + } + + g_value_take_boxed (value, value_array); +} + +static EDestination ** +g_value_dup_destinations (const GValue *value) +{ + EDestination **destinations; + GValueArray *value_array; + GValue *element; + gint ii; + + value_array = g_value_get_boxed (value); + destinations = g_new0 (EDestination *, value_array->n_values + 1); + + for (ii = 0; ii < value_array->n_values; ii++) { + element = g_value_array_get_nth (value_array, ii); + destinations[ii] = g_value_dup_object (element); + } + + return destinations; +} + +static void +g_value_set_string_list (GValue *value, + GList *list) +{ + GValueArray *value_array; + GValue element; + + memset (&element, 0, sizeof (GValue)); + g_value_init (&element, G_TYPE_STRING); + + value_array = g_value_array_new (g_list_length (list)); + + while (list != NULL) { + g_value_set_string (&element, list->data); + g_value_array_append (value_array, &element); + } + + g_value_take_boxed (value, value_array); +} + +static GList * +g_value_dup_string_list (const GValue *value) +{ + GValueArray *value_array; + GList *list = NULL; + GValue *element; + gint ii; + + value_array = g_value_get_boxed (value); + + for (ii = 0; ii < value_array->n_values; ii++) { + element = g_value_array_get_nth (value_array, ii); + list = g_list_prepend (list, g_value_dup_string (element)); + } + + return g_list_reverse (list); +} + +static void +composer_header_table_notify_header (EComposerHeader *header, + const gchar *property_name) +{ + GtkWidget *parent; + + parent = gtk_widget_get_parent (header->input_widget); + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (parent)); + g_object_notify (G_OBJECT (parent), property_name); +} + +static void +composer_header_table_notify_widget (GtkWidget *widget, + const gchar *property_name) +{ + GtkWidget *parent; + + parent = gtk_widget_get_parent (widget); + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (parent)); + g_object_notify (G_OBJECT (parent), property_name); +} + +static void +composer_header_table_bind_header (const gchar *property_name, + const gchar *signal_name, + EComposerHeader *header) +{ + /* Propagate the signal as "notify::property_name". */ + + g_signal_connect ( + header, signal_name, + G_CALLBACK (composer_header_table_notify_header), + (gpointer) property_name); +} + +static void +composer_header_table_bind_widget (const gchar *property_name, + const gchar *signal_name, + GtkWidget *widget) +{ + /* Propagate the signal as "notify::property_name". */ + + g_signal_connect ( + widget, signal_name, + G_CALLBACK (composer_header_table_notify_widget), + (gpointer) property_name); +} + +static void +composer_header_table_from_changed_cb (EComposerHeaderTable *table) +{ + EAccount *account; + EComposerPostHeader *post_header; + EComposerTextHeader *text_header; + const gchar *reply_to; + + /* Keep "Post-To" and "Reply-To" synchronized with "From" */ + + account = e_composer_header_table_get_account (table); + + post_header = E_COMPOSER_HEADER_TABLE_GET_POST_TO_HEADER (table); + e_composer_post_header_set_account (post_header, account); + + reply_to = (account != NULL) ? account->id->reply_to : NULL; + text_header = E_COMPOSER_HEADER_TABLE_GET_REPLY_TO_HEADER (table); + e_composer_text_header_set_text (text_header, reply_to); +} + +static GObject * +composer_header_table_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *object; + EComposerHeaderTablePrivate *priv; + guint rows, ii; + + /* Chain up to parent's constructor() method. */ + object = G_OBJECT_CLASS (parent_class)->constructor ( + type, n_construct_properties, construct_properties); + + priv = E_COMPOSER_HEADER_TABLE_GET_PRIVATE (object); + + rows = G_N_ELEMENTS (priv->headers); + gtk_table_resize (GTK_TABLE (object), rows, 4); + gtk_table_set_row_spacings (GTK_TABLE (object), 0); + gtk_table_set_col_spacings (GTK_TABLE (object), 6); + + /* Use "ypadding" instead of "row-spacing" because some rows may + * be invisible and we don't want spacing around them. */ + + for (ii = 0; ii < rows; ii++) { + gtk_table_attach ( + GTK_TABLE (object), priv->headers[ii]->title_widget, + 0, 1, ii, ii + 1, GTK_FILL, GTK_FILL, 0, 3); + gtk_table_attach ( + GTK_TABLE (object), priv->headers[ii]->input_widget, + 1, 4, ii, ii + 1, GTK_FILL | GTK_EXPAND, 0, 0, 3); + } + + ii = E_COMPOSER_HEADER_FROM; + + /* Leave room in the "From" row for signature stuff. */ + gtk_container_child_set ( + GTK_CONTAINER (object), + priv->headers[ii]->input_widget, + "right-attach", 2, NULL); + + /* Now add the signature stuff. */ + gtk_table_attach ( + GTK_TABLE (object), priv->signature_label, + 2, 3, ii, ii + 1, 0, 0, 0, 3); + gtk_table_attach ( + GTK_TABLE (object), priv->signature_combo_box, + 3, 4, ii, ii + 1, 0, 0, 0, 3); + + return object; +} + +static void +composer_header_table_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EDestination **destinations; + GList *list; + + switch (property_id) { + case PROP_ACCOUNT: + e_composer_header_table_set_account ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_object (value)); + return; + + case PROP_ACCOUNT_LIST: + e_composer_header_table_set_account_list ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_object (value)); + return; + + case PROP_ACCOUNT_NAME: + e_composer_header_table_set_account_name ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_string (value)); + return; + + case PROP_DESTINATIONS_BCC: + destinations = g_value_dup_destinations (value); + e_composer_header_table_set_destinations_bcc ( + E_COMPOSER_HEADER_TABLE (object), + destinations); + e_destination_freev (destinations); + return; + + case PROP_DESTINATIONS_CC: + destinations = g_value_dup_destinations (value); + e_composer_header_table_set_destinations_cc ( + E_COMPOSER_HEADER_TABLE (object), + destinations); + e_destination_freev (destinations); + return; + + case PROP_DESTINATIONS_TO: + destinations = g_value_dup_destinations (value); + e_composer_header_table_set_destinations_to ( + E_COMPOSER_HEADER_TABLE (object), + destinations); + e_destination_freev (destinations); + return; + + case PROP_POST_TO: + list = g_value_dup_string_list (value); + e_composer_header_table_set_post_to_list ( + E_COMPOSER_HEADER_TABLE (object), list); + g_list_foreach (list, (GFunc) g_free, NULL); + g_list_free (list); + return; + + case PROP_REPLY_TO: + e_composer_header_table_set_reply_to ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_string (value)); + return; + + case PROP_SIGNATURE: + e_composer_header_table_set_signature ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_object (value)); + return; + + case PROP_SIGNATURE_LIST: + e_composer_header_table_set_signature_list ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_object (value)); + return; + + case PROP_SUBJECT: + e_composer_header_table_set_subject ( + E_COMPOSER_HEADER_TABLE (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +composer_header_table_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EDestination **destinations; + GList *list; + + switch (property_id) { + case PROP_ACCOUNT: + g_value_set_object ( + value, + e_composer_header_table_get_account ( + E_COMPOSER_HEADER_TABLE (object))); + return; + + case PROP_ACCOUNT_LIST: + g_value_set_object ( + value, + e_composer_header_table_get_account_list ( + E_COMPOSER_HEADER_TABLE (object))); + return; + + case PROP_ACCOUNT_NAME: + g_value_set_string ( + value, + e_composer_header_table_get_account_name ( + E_COMPOSER_HEADER_TABLE (object))); + return; + + case PROP_DESTINATIONS_BCC: + destinations = + e_composer_header_table_get_destinations_bcc ( + E_COMPOSER_HEADER_TABLE (object)); + g_value_set_destinations (value, destinations); + e_destination_freev (destinations); + return; + + case PROP_DESTINATIONS_CC: + destinations = + e_composer_header_table_get_destinations_cc ( + E_COMPOSER_HEADER_TABLE (object)); + g_value_set_destinations (value, destinations); + e_destination_freev (destinations); + return; + + case PROP_DESTINATIONS_TO: + destinations = + e_composer_header_table_get_destinations_to ( + E_COMPOSER_HEADER_TABLE (object)); + g_value_set_destinations (value, destinations); + e_destination_freev (destinations); + return; + + case PROP_POST_TO: + list = e_composer_header_table_get_post_to ( + E_COMPOSER_HEADER_TABLE (object)); + g_value_set_string_list (value, list); + g_list_foreach (list, (GFunc) g_free, NULL); + g_list_free (list); + return; + + case PROP_REPLY_TO: + g_value_set_string ( + value, + e_composer_header_table_get_reply_to ( + E_COMPOSER_HEADER_TABLE (object))); + return; + + case PROP_SIGNATURE: + g_value_set_object ( + value, + e_composer_header_table_get_signature ( + E_COMPOSER_HEADER_TABLE (object))); + return; + + case PROP_SIGNATURE_LIST: + g_value_set_object ( + value, + e_composer_header_table_get_signature_list ( + E_COMPOSER_HEADER_TABLE (object))); + return; + + case PROP_SUBJECT: + g_value_set_string ( + value, + e_composer_header_table_get_subject ( + E_COMPOSER_HEADER_TABLE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +composer_header_table_dispose (GObject *object) +{ + EComposerHeaderTablePrivate *priv; + gint ii; + + priv = E_COMPOSER_HEADER_TABLE_GET_PRIVATE (object); + + for (ii = 0; ii < G_N_ELEMENTS (priv->headers); ii++) { + if (priv->headers[ii] != NULL) { + g_object_unref (priv->headers[ii]); + priv->headers[ii] = NULL; + } + } + + if (priv->signature_combo_box != NULL) { + g_object_unref (priv->signature_combo_box); + priv->signature_combo_box = NULL; + } + + if (priv->name_selector != NULL) { + g_object_unref (priv->name_selector); + priv->name_selector = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +composer_header_table_class_init (EComposerHeaderTableClass *class) +{ + GObjectClass *object_class; + GParamSpec *element_spec; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EComposerHeaderTablePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructor = composer_header_table_constructor; + object_class->set_property = composer_header_table_set_property; + object_class->get_property = composer_header_table_get_property; + object_class->dispose = composer_header_table_dispose; + + g_object_class_install_property ( + object_class, + PROP_ACCOUNT, + g_param_spec_object ( + "account", + NULL, + NULL, + E_TYPE_ACCOUNT, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_ACCOUNT_LIST, + g_param_spec_object ( + "account-list", + NULL, + NULL, + E_TYPE_ACCOUNT_LIST, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_ACCOUNT_NAME, + g_param_spec_string ( + "account-name", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + + /* floating reference */ + element_spec = g_param_spec_object ( + "value-array-element", + NULL, + NULL, + E_TYPE_DESTINATION, + G_PARAM_READWRITE); + + g_object_class_install_property ( + object_class, + PROP_DESTINATIONS_BCC, + g_param_spec_value_array ( + "destinations-bcc", + NULL, + NULL, + element_spec, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_DESTINATIONS_CC, + g_param_spec_value_array ( + "destinations-cc", + NULL, + NULL, + element_spec, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_DESTINATIONS_TO, + g_param_spec_value_array ( + "destinations-to", + NULL, + NULL, + element_spec, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_REPLY_TO, + g_param_spec_string ( + "reply-to", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SIGNATURE, + g_param_spec_object ( + "signature", + NULL, + NULL, + E_TYPE_SIGNATURE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SIGNATURE_LIST, + g_param_spec_object ( + "signature-list", + NULL, + NULL, + E_TYPE_SIGNATURE_LIST, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SUBJECT, + g_param_spec_string ( + "subject", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); +} + +static void +composer_header_table_init (EComposerHeaderTable *table) +{ + EComposerHeader *header; + ENameSelector *name_selector; + GtkWidget *widget; + + table->priv = E_COMPOSER_HEADER_TABLE_GET_PRIVATE (table); + + name_selector = e_name_selector_new (); + table->priv->name_selector = name_selector; + + header = e_composer_from_header_new (_("Fr_om:")); + composer_header_table_bind_header ("account", "changed", header); + composer_header_table_bind_header ("account-list", "refreshed", header); + composer_header_table_bind_header ("account-name", "changed", header); + g_signal_connect_swapped ( + header, "changed", G_CALLBACK ( + composer_header_table_from_changed_cb), table); + table->priv->headers[E_COMPOSER_HEADER_FROM] = header; + + header = e_composer_text_header_new_label (_("_Reply-To:")); + composer_header_table_bind_header ("reply-to", "changed", header); + table->priv->headers[E_COMPOSER_HEADER_REPLY_TO] = header; + + header = e_composer_name_header_new (_("_To:"), name_selector); + e_composer_header_set_input_tooltip (header, HEADER_TOOLTIP_TO); + composer_header_table_bind_header ("destinations-to", "changed", header); + table->priv->headers[E_COMPOSER_HEADER_TO] = header; + + header = e_composer_name_header_new (_("_Cc:"), name_selector); + e_composer_header_set_input_tooltip (header, HEADER_TOOLTIP_CC); + composer_header_table_bind_header ("destinations-cc", "changed", header); + table->priv->headers[E_COMPOSER_HEADER_CC] = header; + + header = e_composer_name_header_new (_("_Bcc:"), name_selector); + e_composer_header_set_input_tooltip (header, HEADER_TOOLTIP_BCC); + composer_header_table_bind_header ("destinations-bcc", "changed", header); + table->priv->headers[E_COMPOSER_HEADER_BCC] = header; + + header = e_composer_post_header_new (_("_Post To:")); + composer_header_table_bind_header ("post-to", "changed", header); + table->priv->headers[E_COMPOSER_HEADER_POST_TO] = header; + + header = e_composer_text_header_new_label (_("S_ubject:")); + composer_header_table_bind_header ("subject", "changed", header); + table->priv->headers[E_COMPOSER_HEADER_SUBJECT] = header; + + widget = e_signature_combo_box_new (); + composer_header_table_bind_widget ("signature", "changed", widget); + composer_header_table_bind_widget ("signature-list", "refreshed", widget); + table->priv->signature_combo_box = g_object_ref_sink (widget); + + widget = gtk_label_new_with_mnemonic (_("Si_gnature:")); + gtk_label_set_mnemonic_widget ( + GTK_LABEL (widget), table->priv->signature_combo_box); + table->priv->signature_label = g_object_ref_sink (widget); +} + +GType +e_composer_header_table_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EComposerHeaderTableClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) composer_header_table_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EComposerHeaderTable), + 0, /* n_preallocs */ + (GInstanceInitFunc) composer_header_table_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + GTK_TYPE_TABLE, "EComposerHeaderTable", &type_info, 0); + } + + return type; +} + +GtkWidget * +e_composer_header_table_new (void) +{ + return g_object_new (E_TYPE_COMPOSER_HEADER_TABLE, NULL); +} + +EComposerHeader * +e_composer_header_table_get_header (EComposerHeaderTable *table, + EComposerHeaderType type) +{ + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + g_return_val_if_fail (type < E_COMPOSER_NUM_HEADERS, NULL); + g_return_val_if_fail (type >= 0, NULL); + + return table->priv->headers[type]; +} + +gboolean +e_composer_header_table_get_header_visible (EComposerHeaderTable *table, + EComposerHeaderType type) +{ + EComposerHeader *header; + + header = e_composer_header_table_get_header (table, type); + return e_composer_header_get_visible (header); +} + +void +e_composer_header_table_set_header_visible (EComposerHeaderTable *table, + EComposerHeaderType type, + gboolean visible) +{ + EComposerHeader *header; + + header = e_composer_header_table_get_header (table, type); + e_composer_header_set_visible (header, visible); + + /* Signature widgets track the "From" header. */ + if (type == E_COMPOSER_HEADER_FROM) { + if (visible) { + gtk_widget_show (table->priv->signature_label); + gtk_widget_show (table->priv->signature_combo_box); + } else { + gtk_widget_hide (table->priv->signature_label); + gtk_widget_hide (table->priv->signature_combo_box); + } + } +} + +gboolean +e_composer_header_table_get_header_sensitive (EComposerHeaderTable *table, + EComposerHeaderType type) +{ + EComposerHeader *header; + + header = e_composer_header_table_get_header (table, type); + return e_composer_header_get_sensitive (header); +} + +void +e_composer_header_table_set_header_sensitive (EComposerHeaderTable *table, + EComposerHeaderType type, + gboolean sensitive) +{ + EComposerHeader *header; + + header = e_composer_header_table_get_header (table, type); + e_composer_header_set_sensitive (header, sensitive); +} + +EAccount * +e_composer_header_table_get_account (EComposerHeaderTable *table) +{ + EComposerFromHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER (table); + return e_composer_from_header_get_active (header); +} + +gboolean +e_composer_header_table_set_account (EComposerHeaderTable *table, + EAccount *account) +{ + EComposerFromHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), FALSE); + + header = E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER (table); + return e_composer_from_header_set_active (header, account); +} + +EAccountList * +e_composer_header_table_get_account_list (EComposerHeaderTable *table) +{ + EComposerFromHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER (table); + return e_composer_from_header_get_account_list (header); +} + +void +e_composer_header_table_set_account_list (EComposerHeaderTable *table, + EAccountList *account_list) +{ + EComposerFromHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER (table); + e_composer_from_header_set_account_list (header, account_list); +} + +const gchar * +e_composer_header_table_get_account_name (EComposerHeaderTable *table) +{ + EComposerFromHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER (table); + return e_composer_from_header_get_active_name (header); +} + +gboolean +e_composer_header_table_set_account_name (EComposerHeaderTable *table, + const gchar *account_name) +{ + EComposerFromHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), FALSE); + + header = E_COMPOSER_HEADER_TABLE_GET_FROM_HEADER (table); + return e_composer_from_header_set_active_name (header, account_name); +} + +EDestination ** +e_composer_header_table_get_destinations (EComposerHeaderTable *table) +{ + EDestination **destinations; + EDestination **to, **cc, **bcc; + gint total, n_to, n_cc, n_bcc; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + to = e_composer_header_table_get_destinations_to (table); + for (n_to = 0; to != NULL && to[n_to] != NULL; n_to++); + + cc = e_composer_header_table_get_destinations_cc (table); + for (n_cc = 0; cc != NULL && cc[n_cc] != NULL; n_cc++); + + bcc = e_composer_header_table_get_destinations_bcc (table); + for (n_bcc = 0; bcc != NULL && bcc[n_bcc] != NULL; n_bcc++); + + total = n_to + n_cc + n_bcc; + destinations = g_new0 (EDestination *, total + 1); + + while (n_bcc > 0 && total > 0) + destinations[--total] = g_object_ref (bcc[--n_bcc]); + + while (n_cc > 0 && total > 0) + destinations[--total] = g_object_ref (cc[--n_cc]); + + while (n_to > 0 && total > 0) + destinations[--total] = g_object_ref (to[--n_to]); + + /* Counters should all be zero now. */ + g_assert (total == 0 && n_to == 0 && n_cc == 0 && n_bcc == 0); + + e_destination_freev (to); + e_destination_freev (cc); + e_destination_freev (bcc); + + return destinations; +} + +EDestination ** +e_composer_header_table_get_destinations_bcc (EComposerHeaderTable *table) +{ + EComposerNameHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_BCC_HEADER (table); + return e_composer_name_header_get_destinations (header); +} + +void +e_composer_header_table_set_destinations_bcc (EComposerHeaderTable *table, + EDestination **destinations) +{ + EComposerNameHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_BCC_HEADER (table); + e_composer_name_header_set_destinations (header, destinations); + + if (destinations != NULL && *destinations != NULL) + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_BCC, TRUE); +} + +EDestination ** +e_composer_header_table_get_destinations_cc (EComposerHeaderTable *table) +{ + EComposerNameHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_CC_HEADER (table); + return e_composer_name_header_get_destinations (header); +} + +void +e_composer_header_table_set_destinations_cc (EComposerHeaderTable *table, + EDestination **destinations) +{ + EComposerNameHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_CC_HEADER (table); + e_composer_name_header_set_destinations (header, destinations); + + if (destinations != NULL && *destinations != NULL) + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_CC, TRUE); +} + +EDestination ** +e_composer_header_table_get_destinations_to (EComposerHeaderTable *table) +{ + EComposerNameHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_TO_HEADER (table); + return e_composer_name_header_get_destinations (header); +} + +void +e_composer_header_table_set_destinations_to (EComposerHeaderTable *table, + EDestination **destinations) +{ + EComposerNameHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_TO_HEADER (table); + e_composer_name_header_set_destinations (header, destinations); +} + +GList * +e_composer_header_table_get_post_to (EComposerHeaderTable *table) +{ + EComposerPostHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_POST_TO_HEADER (table); + return e_composer_post_header_get_folders (header); +} + +void +e_composer_header_table_set_post_to_base (EComposerHeaderTable *table, + const gchar *base_url, + const gchar *folders) +{ + EComposerPostHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_POST_TO_HEADER (table); + e_composer_post_header_set_folders_base (header, base_url, folders); +} + +void +e_composer_header_table_set_post_to_list (EComposerHeaderTable *table, + GList *folders) +{ + EComposerPostHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_POST_TO_HEADER (table); + e_composer_post_header_set_folders (header, folders); +} + +const gchar * +e_composer_header_table_get_reply_to (EComposerHeaderTable *table) +{ + EComposerTextHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_REPLY_TO_HEADER (table); + return e_composer_text_header_get_text (header); +} + +void +e_composer_header_table_set_reply_to (EComposerHeaderTable *table, + const gchar *reply_to) +{ + EComposerTextHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_REPLY_TO_HEADER (table); + e_composer_text_header_set_text (header, reply_to); + + if (reply_to != NULL && *reply_to != '\0') + e_composer_header_table_set_header_visible ( + table, E_COMPOSER_HEADER_REPLY_TO, TRUE); +} + +ESignature * +e_composer_header_table_get_signature (EComposerHeaderTable *table) +{ + ESignatureComboBox *combo_box; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + combo_box = E_SIGNATURE_COMBO_BOX (table->priv->signature_combo_box); + return e_signature_combo_box_get_active (combo_box); +} + +gboolean +e_composer_header_table_set_signature (EComposerHeaderTable *table, + ESignature *signature) +{ + ESignatureComboBox *combo_box; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), FALSE); + + combo_box = E_SIGNATURE_COMBO_BOX (table->priv->signature_combo_box); + return e_signature_combo_box_set_active (combo_box, signature); +} + +ESignatureList * +e_composer_header_table_get_signature_list (EComposerHeaderTable *table) +{ + ESignatureComboBox *combo_box; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + combo_box = E_SIGNATURE_COMBO_BOX (table->priv->signature_combo_box); + return e_signature_combo_box_get_signature_list (combo_box); +} + +void +e_composer_header_table_set_signature_list (EComposerHeaderTable *table, + ESignatureList *signature_list) +{ + ESignatureComboBox *combo_box; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + combo_box = E_SIGNATURE_COMBO_BOX (table->priv->signature_combo_box); + e_signature_combo_box_set_signature_list (combo_box, signature_list); +} + +const gchar * +e_composer_header_table_get_subject (EComposerHeaderTable *table) +{ + EComposerTextHeader *header; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER_TABLE (table), NULL); + + header = E_COMPOSER_HEADER_TABLE_GET_SUBJECT_HEADER (table); + return e_composer_text_header_get_text (header); +} + +void +e_composer_header_table_set_subject (EComposerHeaderTable *table, + const gchar *subject) +{ + EComposerTextHeader *header; + + g_return_if_fail (E_IS_COMPOSER_HEADER_TABLE (table)); + + header = E_COMPOSER_HEADER_TABLE_GET_SUBJECT_HEADER (table); + e_composer_text_header_set_text (header, subject); +} diff --git a/composer/e-composer-header-table.h b/composer/e-composer-header-table.h new file mode 100644 index 0000000000..2de115563c --- /dev/null +++ b/composer/e-composer-header-table.h @@ -0,0 +1,162 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_COMPOSER_HEADER_TABLE_H +#define E_COMPOSER_HEADER_TABLE_H + +#include "e-composer-common.h" + +#include +#include +#include +#include +#include + +#include "e-composer-header.h" + +/* Standard GObject macros */ +#define E_TYPE_COMPOSER_HEADER_TABLE \ + (e_composer_header_table_get_type ()) +#define E_COMPOSER_HEADER_TABLE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_COMPOSER_HEADER_TABLE, EComposerHeaderTable)) +#define E_COMPOSER_HEADER_TABLE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_COMPOSER_HEADER_TABLE, EComposerHeaderTableClass)) +#define E_IS_COMPOSER_HEADER_TABLE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_COMPOSER_HEADER_TABLE)) +#define E_IS_COMPOSER_HEADER_TABLE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_COMPOSER_HEADER_TABLE)) +#define E_COMPOSER_HEADER_TABLE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_COMPOSER_HEADER_TABLE, EComposerHeaderTableClass)) + +G_BEGIN_DECLS + +typedef struct _EComposerHeaderTable EComposerHeaderTable; +typedef struct _EComposerHeaderTableClass EComposerHeaderTableClass; +typedef struct _EComposerHeaderTablePrivate EComposerHeaderTablePrivate; + +/* Headers, listed in the order they should appear in the table. */ +typedef enum { + E_COMPOSER_HEADER_FROM, + E_COMPOSER_HEADER_REPLY_TO, + E_COMPOSER_HEADER_TO, + E_COMPOSER_HEADER_CC, + E_COMPOSER_HEADER_BCC, + E_COMPOSER_HEADER_POST_TO, + E_COMPOSER_HEADER_SUBJECT, + E_COMPOSER_NUM_HEADERS +} EComposerHeaderType; + +struct _EComposerHeaderTable { + GtkTable parent; + EComposerHeaderTablePrivate *priv; +}; + +struct _EComposerHeaderTableClass { + GtkTableClass parent_class; +}; + +GType e_composer_header_table_get_type (void); +GtkWidget * e_composer_header_table_new (void); +EComposerHeader * e_composer_header_table_get_header + (EComposerHeaderTable *table, + EComposerHeaderType type); +gboolean e_composer_header_table_get_header_sensitive + (EComposerHeaderTable *table, + EComposerHeaderType type); +void e_composer_header_table_set_header_sensitive + (EComposerHeaderTable *table, + EComposerHeaderType type, + gboolean sensitive); +gboolean e_composer_header_table_get_header_visible + (EComposerHeaderTable *table, + EComposerHeaderType type); +void e_composer_header_table_set_header_visible + (EComposerHeaderTable *table, + EComposerHeaderType type, + gboolean visible); +EAccount * e_composer_header_table_get_account + (EComposerHeaderTable *table); +gboolean e_composer_header_table_set_account + (EComposerHeaderTable *table, + EAccount *account); +EAccountList * e_composer_header_table_get_account_list + (EComposerHeaderTable *table); +void e_composer_header_table_set_account_list + (EComposerHeaderTable *table, + EAccountList *account_list); +const gchar * e_composer_header_table_get_account_name + (EComposerHeaderTable *table); +gboolean e_composer_header_table_set_account_name + (EComposerHeaderTable *table, + const gchar *account_name); +EDestination ** e_composer_header_table_get_destinations + (EComposerHeaderTable *table); +EDestination ** e_composer_header_table_get_destinations_bcc + (EComposerHeaderTable *table); +void e_composer_header_table_set_destinations_bcc + (EComposerHeaderTable *table, + EDestination **destinations); +EDestination ** e_composer_header_table_get_destinations_cc + (EComposerHeaderTable *table); +void e_composer_header_table_set_destinations_cc + (EComposerHeaderTable *table, + EDestination **destinations); +EDestination ** e_composer_header_table_get_destinations_to + (EComposerHeaderTable *table); +void e_composer_header_table_set_destinations_to + (EComposerHeaderTable *table, + EDestination **destinations); +GList * e_composer_header_table_get_post_to + (EComposerHeaderTable *table); +void e_composer_header_table_set_post_to_base + (EComposerHeaderTable *table, + const gchar *base_url, + const gchar *post_to); +void e_composer_header_table_set_post_to_list + (EComposerHeaderTable *table, + GList *folder_list); +const gchar * e_composer_header_table_get_reply_to + (EComposerHeaderTable *table); +void e_composer_header_table_set_reply_to + (EComposerHeaderTable *table, + const gchar *reply_to); +ESignature * e_composer_header_table_get_signature + (EComposerHeaderTable *table); +gboolean e_composer_header_table_set_signature + (EComposerHeaderTable *table, + ESignature *signature); +ESignatureList *e_composer_header_table_get_signature_list + (EComposerHeaderTable *table); +void e_composer_header_table_set_signature_list + (EComposerHeaderTable *table, + ESignatureList *signature_list); +const gchar * e_composer_header_table_get_subject + (EComposerHeaderTable *table); +void e_composer_header_table_set_subject + (EComposerHeaderTable *table, + const gchar *subject); + +G_END_DECLS + +#endif /* E_COMPOSER_HEADER_TABLE_H */ diff --git a/composer/e-composer-header.c b/composer/e-composer-header.c index a7c10997cc..d8e94e2b01 100644 --- a/composer/e-composer-header.c +++ b/composer/e-composer-header.c @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #include "e-composer-header.h" #define E_COMPOSER_HEADER_GET_PRIVATE(obj) \ @@ -8,6 +27,7 @@ enum { PROP_0, PROP_BUTTON, PROP_LABEL, + PROP_SENSITIVE, PROP_VISIBLE }; @@ -57,12 +77,8 @@ composer_header_constructor (GType type, header); } else { widget = gtk_label_new_with_mnemonic (header->priv->label); - - /* The subclass may not have initialized 'input_widget' yet, - * in which case the subclass will have to do this. */ - if (header->input_widget != NULL) - gtk_label_set_mnemonic_widget ( - GTK_LABEL (widget), header->input_widget); + gtk_label_set_mnemonic_widget ( + GTK_LABEL (widget), header->input_widget); } header->title_widget = g_object_ref_sink (widget); @@ -91,6 +107,12 @@ composer_header_set_property (GObject *object, priv->label = g_value_dup_string (value); return; + case PROP_SENSITIVE: + e_composer_header_set_sensitive ( + E_COMPOSER_HEADER (object), + g_value_get_boolean (value)); + return; + case PROP_VISIBLE: e_composer_header_set_visible ( E_COMPOSER_HEADER (object), @@ -122,6 +144,12 @@ composer_header_get_property (GObject *object, E_COMPOSER_HEADER (object))); return; + case PROP_SENSITIVE: + g_value_set_boolean ( + value, e_composer_header_get_sensitive ( + E_COMPOSER_HEADER (object))); + return; + case PROP_VISIBLE: g_value_set_boolean ( value, e_composer_header_get_visible ( @@ -187,6 +215,16 @@ composer_header_class_init (EComposerHeaderClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property ( + object_class, + PROP_SENSITIVE, + g_param_spec_boolean ( + "sensitive", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property ( object_class, PROP_VISIBLE, @@ -260,6 +298,32 @@ e_composer_header_get_label (EComposerHeader *header) return label; } +gboolean +e_composer_header_get_sensitive (EComposerHeader *header) +{ + gboolean sensitive; + + g_return_val_if_fail (E_IS_COMPOSER_HEADER (header), FALSE); + + sensitive = GTK_WIDGET_SENSITIVE (header->title_widget); + if (GTK_WIDGET_SENSITIVE (header->input_widget) != sensitive) + g_warning ("%s: Sensitivity is out of sync", G_STRFUNC); + + return sensitive; +} + +void +e_composer_header_set_sensitive (EComposerHeader *header, + gboolean sensitive) +{ + g_return_if_fail (E_IS_COMPOSER_HEADER (header)); + + gtk_widget_set_sensitive (header->title_widget, sensitive); + gtk_widget_set_sensitive (header->input_widget, sensitive); + + g_object_notify (G_OBJECT (header), "sensitive"); +} + gboolean e_composer_header_get_visible (EComposerHeader *header) { diff --git a/composer/e-composer-header.h b/composer/e-composer-header.h index c98b3419e4..38a381f33e 100644 --- a/composer/e-composer-header.h +++ b/composer/e-composer-header.h @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #ifndef E_COMPOSER_HEADER_H #define E_COMPOSER_HEADER_H @@ -41,6 +60,9 @@ struct _EComposerHeaderClass { GType e_composer_header_get_type (void); gchar * e_composer_header_get_label (EComposerHeader *header); +gboolean e_composer_header_get_sensitive (EComposerHeader *header); +void e_composer_header_set_sensitive (EComposerHeader *header, + gboolean sensitive); gboolean e_composer_header_get_visible (EComposerHeader *header); void e_composer_header_set_visible (EComposerHeader *header, gboolean visible); diff --git a/composer/e-composer-name-header.c b/composer/e-composer-name-header.c index d9af95c500..f06314993a 100644 --- a/composer/e-composer-name-header.c +++ b/composer/e-composer-name-header.c @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #include "e-composer-name-header.h" #include @@ -271,7 +290,7 @@ e_composer_name_header_get_destinations (EComposerNameHeader *header) g_list_free (list); - return destinations; + return destinations; /* free with e_destination_freev() */ } void diff --git a/composer/e-composer-name-header.h b/composer/e-composer-name-header.h index 1027c14d3c..03c6c03918 100644 --- a/composer/e-composer-name-header.h +++ b/composer/e-composer-name-header.h @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #ifndef E_COMPOSER_NAME_HEADER_H #define E_COMPOSER_NAME_HEADER_H diff --git a/composer/e-composer-post-header.c b/composer/e-composer-post-header.c index ea16eeb684..42e58dcc88 100644 --- a/composer/e-composer-post-header.c +++ b/composer/e-composer-post-header.c @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #include "e-composer-post-header.h" #include diff --git a/composer/e-composer-post-header.h b/composer/e-composer-post-header.h index 2825b6c5dd..cd1d965054 100644 --- a/composer/e-composer-post-header.h +++ b/composer/e-composer-post-header.h @@ -1,8 +1,25 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #ifndef E_COMPOSER_POST_HEADER_H #define E_COMPOSER_POST_HEADER_H -#include "e-composer-common.h" - #include #include "e-composer-text-header.h" diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c new file mode 100644 index 0000000000..f5e3e08c68 --- /dev/null +++ b/composer/e-composer-private.c @@ -0,0 +1,290 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "e-composer-private.h" + +static void +composer_setup_charset_menu (EMsgComposer *composer) +{ + GtkUIManager *manager; + const gchar *path; + GList *list; + guint merge_id; + + manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (composer)); + list = gtk_action_group_list_actions (composer->priv->charset_actions); + path = "/main-menu/edit-menu/pre-spell-check/charset-menu"; + merge_id = gtk_ui_manager_new_merge_id (manager); + + while (list != NULL) { + GtkAction *action = list->data; + + gtk_ui_manager_add_ui ( + manager, merge_id, path, + gtk_action_get_name (action), + gtk_action_get_name (action), + GTK_UI_MANAGER_AUTO, FALSE); + + list = g_list_delete_link (list, list); + } +} + +void +e_composer_private_init (EMsgComposer *composer) +{ + EMsgComposerPrivate *priv = composer->priv; + + GtkhtmlEditor *editor; + GtkUIManager *manager; + GtkWidget *widget; + GtkWidget *expander; + GtkWidget *container; + gchar *filename; + GError *error = NULL; + + editor = GTKHTML_EDITOR (composer); + manager = gtkhtml_editor_get_ui_manager (editor); + + priv->charset_actions = gtk_action_group_new ("charset"); + priv->composer_actions = gtk_action_group_new ("composer"); + + priv->extra_hdr_names = g_ptr_array_new (); + priv->extra_hdr_values = g_ptr_array_new (); + + priv->gconf_bridge_binding_ids = g_array_new ( + FALSE, FALSE, sizeof (guint)); + + priv->inline_images = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + + priv->inline_images_by_url = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) camel_object_unref); + + priv->charset = e_composer_get_default_charset (); + + e_composer_actions_init (composer); + + filename = e_composer_find_data_file ("evolution-composer.ui"); + gtk_ui_manager_add_ui_from_file (manager, filename, &error); + composer_setup_charset_menu (composer); + gtk_ui_manager_ensure_update (manager); + g_free (filename); + + if (error != NULL) { + /* Henceforth, bad things start happening. */ + g_critical ("%s", error->message); + g_clear_error (&error); + } + + /* Construct the header table. */ + + widget = e_composer_header_table_new (); + gtk_container_set_border_width (GTK_CONTAINER (widget), 6); + gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 2); + priv->header_table = g_object_ref (widget); + gtk_widget_show (widget); + + /* Construct attachment widgets. + * XXX Move this stuff into a new custom widget. */ + + widget = gtk_expander_new (NULL); + gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (widget), 6); + gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0); + priv->attachment_expander = g_object_ref (widget); + gtk_widget_show (widget); + expander = widget; + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (expander), widget); + priv->attachment_scrolled_window = g_object_ref (widget); + gtk_widget_show (widget); + container = widget; + + widget = e_attachment_bar_new (NULL); + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->attachment_bar = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_hbox_new (FALSE, 0); + gtk_expander_set_label_widget (GTK_EXPANDER (expander), widget); + gtk_widget_show (widget); + container = widget; + + widget = gtk_label_new_with_mnemonic (_("Show _Attachment Bar")); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 6); + priv->attachment_expander_label = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_image_new_from_icon_name ( + "mail-attachment", GTK_ICON_SIZE_MENU); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); + gtk_widget_set_size_request (widget, 100, -1); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->attachment_expander_icon = g_object_ref (widget); + gtk_widget_hide (widget); + + widget = gtk_label_new (NULL); + gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 6); + priv->attachment_expander_num = g_object_ref (widget); + gtk_widget_show (widget); +} + +void +e_composer_private_dispose (EMsgComposer *composer) +{ + GConfBridge *bridge; + GArray *array; + guint binding_id; + + bridge = gconf_bridge_get (); + array = composer->priv->gconf_bridge_binding_ids; + + while (array->len > 0) { + binding_id = g_array_index (array, guint, 0); + gconf_bridge_unbind (bridge, binding_id); + g_array_remove_index_fast (array, 0); + } + + if (composer->priv->header_table != NULL) { + g_object_unref (composer->priv->header_table); + composer->priv->header_table = NULL; + } + + if (composer->priv->charset_actions != NULL) { + g_object_unref (composer->priv->charset_actions); + composer->priv->charset_actions = NULL; + } + + if (composer->priv->composer_actions != NULL) { + g_object_unref (composer->priv->composer_actions); + composer->priv->composer_actions = NULL; + } + + g_hash_table_remove_all (composer->priv->inline_images); + g_hash_table_remove_all (composer->priv->inline_images_by_url); + + if (composer->priv->redirect != NULL) { + camel_object_unref (composer->priv->redirect); + composer->priv->redirect = NULL; + } +} + +void +e_composer_private_finalize (EMsgComposer *composer) +{ + GPtrArray *array; + + array = composer->priv->extra_hdr_names; + g_ptr_array_foreach (array, (GFunc) g_free, NULL); + g_ptr_array_free (array, TRUE); + + array = composer->priv->extra_hdr_values; + g_ptr_array_foreach (array, (GFunc) g_free, NULL); + g_ptr_array_free (array, TRUE); + + g_free (composer->priv->charset); + g_free (composer->priv->mime_type); + g_free (composer->priv->mime_body); + + g_hash_table_destroy (composer->priv->inline_images); + g_hash_table_destroy (composer->priv->inline_images_by_url); +} + +gchar * +e_composer_find_data_file (const gchar *basename) +{ + gchar *filename; + + g_return_val_if_fail (basename != NULL, NULL); + + /* Support running directly from the source tree. */ + filename = g_build_filename (".", basename, NULL); + if (g_file_test (filename, G_FILE_TEST_EXISTS)) + return filename; + g_free (filename); + + /* XXX This is kinda broken. */ + filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL); + if (g_file_test (filename, G_FILE_TEST_EXISTS)) + return filename; + g_free (filename); + + g_critical ("Could not locate '%s'", basename); + + return NULL; +} + +gchar * +e_composer_get_default_charset (void) +{ + GConfClient *client; + gchar *charset; + GError *error = NULL; + + client = gconf_client_get_default (); + + charset = gconf_client_get_string ( + client, COMPOSER_GCONF_CHARSET_KEY, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_clear_error (&error); + } + + /* See what charset the mailer is using. + * XXX We should not have to know where this lives in GConf. + * Need a mail_config_get_default_charset() that does this. */ + if (!charset || charset[0] == '\0') { + g_free (charset); + charset = gconf_client_get_string ( + client, MAIL_GCONF_CHARSET_KEY, NULL); + if (charset != NULL && *charset == '\0') { + g_free (charset); + charset = NULL; + } else if (error != NULL) { + g_warning ("%s", error->message); + g_clear_error (&error); + } + } + + g_object_unref (client); + + if (charset == NULL) + charset = g_strdup (e_iconv_locale_charset ()); + + if (charset == NULL) + charset = g_strdup ("us-ascii"); + + return charset; +} + diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h new file mode 100644 index 0000000000..c0557a4a26 --- /dev/null +++ b/composer/e-composer-private.h @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_COMPOSER_PRIVATE_H +#define E_COMPOSER_PRIVATE_H + +#include "e-msg-composer.h" + +#include +#include +#include "gconf-bridge.h" + +#include + +#include "e-attachment-bar.h" +#include "e-composer-actions.h" +#include "e-composer-autosave.h" +#include "e-composer-header-table.h" + +#define E_MSG_COMPOSER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MSG_COMPOSER, EMsgComposerPrivate)) + +/* Mail configuration keys */ +#define MAIL_GCONF_PREFIX \ + "/apps/evolution/mail" +#define MAIL_GCONF_CHARSET_KEY \ + MAIL_GCONF_PREFIX "/format/charset" + +/* Composer configuration keys */ +#define COMPOSER_GCONF_PREFIX \ + MAIL_GCONF_PREFIX "/composer" +#define COMPOSER_GCONF_CHARSET_KEY \ + COMPOSER_GCONF_PREFIX "/charset" +#define COMPOSER_GCONF_CURRENT_FOLDER_KEY \ + COMPOSER_GCONF_PREFIX "/current_folder" +#define COMPOSER_GCONF_INLINE_SPELLING_KEY \ + COMPOSER_GCONF_PREFIX "/inline_spelling" +#define COMPOSER_GCONF_MAGIC_LINKS_KEY \ + COMPOSER_GCONF_PREFIX "/magic_links" +#define COMPOSER_GCONF_MAGIC_SMILEYS_KEY \ + COMPOSER_GCONF_PREFIX "/magic_smileys" +#define COMPOSER_GCONF_REQUEST_RECEIPT_KEY \ + COMPOSER_GCONF_PREFIX "/request_receipt" +#define COMPOSER_GCONF_TOP_SIGNATURE_KEY \ + COMPOSER_GCONF_PREFIX "/top_signature" +#define COMPOSER_GCONF_SEND_HTML_KEY \ + COMPOSER_GCONF_PREFIX "/send_html" +#define COMPOSER_GCONF_VIEW_BCC_KEY \ + COMPOSER_GCONF_PREFIX "/view/Bcc" +#define COMPOSER_GCONF_VIEW_CC_KEY \ + COMPOSER_GCONF_PREFIX "/view/Cc" +#define COMPOSER_GCONF_VIEW_FROM_KEY \ + COMPOSER_GCONF_PREFIX "/view/From" +#define COMPOSER_GCONF_VIEW_POST_TO_KEY \ + COMPOSER_GCONF_PREFIX "/view/PostTo" +#define COMPOSER_GCONF_VIEW_REPLY_TO_KEY \ + COMPOSER_GCONF_PREFIX "/view/ReplyTo" +#define COMPOSER_GCONF_WINDOW_PREFIX \ + COMPOSER_GCONF_PREFIX "/window" + +/* Shorthand, requires a variable named "composer". */ +#define ACTION(name) (E_COMPOSER_ACTION_##name (composer)) + +/* For use in dispose() methods. */ +#define DISPOSE(obj) \ + G_STMT_START { \ + if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \ + } G_STMT_END + +G_BEGIN_DECLS + +struct _EMsgComposerPrivate { + + /*** UI Management ***/ + + GtkWidget *html_editor; + GtkWidget *header_table; + GtkActionGroup *charset_actions; + GtkActionGroup *composer_actions; + + GPtrArray *extra_hdr_names, *extra_hdr_values; + GArray *gconf_bridge_binding_ids; + + GtkWidget *focused_entry; + + 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; + + GHashTable *inline_images; + GHashTable *inline_images_by_url; + GList *current_images; + + gchar *mime_type, *mime_body, *charset; + + guint32 attachment_bar_visible : 1; + guint32 is_alternative : 1; + guint32 autosaved : 1; + + guint32 mode_post : 1; + + guint32 in_signature_insert : 1; + + CamelMimeMessage *redirect; + + guint notify_id; + + gboolean send_invoked; +}; + +void e_composer_private_init (EMsgComposer *composer); +void e_composer_private_dispose (EMsgComposer *composer); +void e_composer_private_finalize (EMsgComposer *composer); + +/* Private Utilities */ + +void e_composer_actions_init (EMsgComposer *composer); +gchar * e_composer_find_data_file (const gchar *basename); +gchar * e_composer_get_default_charset (void); + +G_END_DECLS + +#endif /* E_COMPOSER_PRIVATE_H */ diff --git a/composer/e-composer-text-header.c b/composer/e-composer-text-header.c index 6d3626218f..e5a6fc24dd 100644 --- a/composer/e-composer-text-header.c +++ b/composer/e-composer-text-header.c @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #include "e-composer-text-header.h" /* Convenience macro */ diff --git a/composer/e-composer-text-header.h b/composer/e-composer-text-header.h index 9d4bb2fa3e..4f5ebbd82b 100644 --- a/composer/e-composer-text-header.h +++ b/composer/e-composer-text-header.h @@ -1,3 +1,22 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + #ifndef E_COMPOSER_TEXT_HEADER_H #define E_COMPOSER_TEXT_HEADER_H diff --git a/composer/e-msg-composer-hdrs.c b/composer/e-msg-composer-hdrs.c deleted file mode 100644 index 471cc40dc2..0000000000 --- a/composer/e-msg-composer-hdrs.c +++ /dev/null @@ -1,967 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* msg-composer-hdrs.c - * - * Copyright (C) 1999 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * published by the Free Software Foundation; either version 2 of the - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Author: Ettore Perazzoli - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -#include "Composer.h" - -#include -#include -#include -#include "e-msg-composer-hdrs.h" -#include "mail/mail-config.h" -#include "mail/mail-session.h" -#include "e-account-combo-box.h" -#include "e-signature-combo-box.h" - -#include "e-composer-header.h" -#include "e-composer-from-header.h" -#include "e-composer-name-header.h" -#include "e-composer-post-header.h" -#include "e-composer-text-header.h" - -#define E_MSG_COMPOSER_HDRS_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MSG_COMPOSER_HDRS, EMsgComposerHdrsPrivate)) - -/* Headers, listed in the order that they should appear in the table. */ -enum { - HEADER_FROM, - HEADER_REPLY_TO, - HEADER_TO, - HEADER_CC, - HEADER_BCC, - HEADER_POST_TO, - HEADER_SUBJECT, - NUM_HEADERS -}; - -enum { - SUBJECT_CHANGED, - HDRS_CHANGED, - FROM_CHANGED, - SIGNATURE_CHANGED, - LAST_SIGNAL -}; - -struct _EMsgComposerHdrsPrivate { - ENameSelector *name_selector; - - /* ui component */ - BonoboUIComponent *uic; - - EComposerHeader *headers[NUM_HEADERS]; - GtkWidget *signature_combo_box; -}; - -static gpointer parent_class; -static guint signal_ids[LAST_SIGNAL]; - -static void -from_changed (EComposerFromHeader *from_header, EMsgComposerHdrs *hdrs) -{ - EComposerHeader *header; - EAccount *account; - - account = e_composer_from_header_get_active (from_header); - - header = hdrs->priv->headers[HEADER_POST_TO]; - e_composer_post_header_set_account ( - E_COMPOSER_POST_HEADER (header), account); - - /* we do this rather than calling e_msg_composer_hdrs_set_reply_to() - because we don't want to change the visibility of the header */ - header = hdrs->priv->headers[HEADER_REPLY_TO]; - e_composer_text_header_set_text ( - E_COMPOSER_TEXT_HEADER (header), account->id->reply_to); - - g_signal_emit (hdrs, signal_ids[FROM_CHANGED], 0); -} - -static void -signature_changed (EMsgComposerHdrs *hdrs) -{ - g_signal_emit (hdrs, signal_ids[SIGNATURE_CHANGED], 0); -} - -static void -headers_set_visibility (EMsgComposerHdrs *h, int visible_flags) -{ - EMsgComposerHdrsPrivate *p = h->priv; - - /* To is always visible if we're not doing Post-To */ - if (!(h->visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO)) - visible_flags |= E_MSG_COMPOSER_VISIBLE_TO; - else - visible_flags |= E_MSG_COMPOSER_VISIBLE_POSTTO; - - e_composer_header_set_visible ( - p->headers[HEADER_FROM], - visible_flags & E_MSG_COMPOSER_VISIBLE_FROM); - e_composer_header_set_visible ( - p->headers[HEADER_REPLY_TO], - visible_flags & E_MSG_COMPOSER_VISIBLE_REPLYTO); - e_composer_header_set_visible ( - p->headers[HEADER_TO], - visible_flags & E_MSG_COMPOSER_VISIBLE_TO); - e_composer_header_set_visible ( - p->headers[HEADER_CC], - visible_flags & E_MSG_COMPOSER_VISIBLE_CC); - e_composer_header_set_visible ( - p->headers[HEADER_BCC], - visible_flags & E_MSG_COMPOSER_VISIBLE_BCC); - e_composer_header_set_visible ( - p->headers[HEADER_POST_TO], - visible_flags & E_MSG_COMPOSER_VISIBLE_POSTTO); - e_composer_header_set_visible ( - p->headers[HEADER_SUBJECT], - visible_flags & E_MSG_COMPOSER_VISIBLE_SUBJECT); -} - -static void -headers_set_sensitivity (EMsgComposerHdrs *h) -{ - /* these ones are always on */ - bonobo_ui_component_set_prop ( - h->priv->uic, "/commands/ViewTo", "sensitive", - h->visible_mask & E_MSG_COMPOSER_VISIBLE_TO ? "0" : "1", NULL); - - bonobo_ui_component_set_prop ( - h->priv->uic, "/commands/ViewPostTo", "sensitive", - h->visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO ? "0" : "1", NULL); -} - -void -e_msg_composer_hdrs_set_visible_mask (EMsgComposerHdrs *hdrs, int visible_mask) -{ - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - hdrs->visible_mask = visible_mask; - headers_set_sensitivity (hdrs); -} - -void -e_msg_composer_hdrs_set_visible (EMsgComposerHdrs *hdrs, int visible_flags) -{ - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - headers_set_visibility (hdrs, visible_flags); - gtk_widget_queue_resize (GTK_WIDGET (hdrs)); -} - -static GObject * -msg_composer_hdrs_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties) -{ - GObject *object; - EMsgComposerHdrsPrivate *priv; - GtkWidget *widget; - guint rows, ii; - - /* Chain up to parent's constructor() method. */ - object = G_OBJECT_CLASS (parent_class)->constructor ( - type, n_construct_properties, construct_properties); - - priv = E_MSG_COMPOSER_HDRS_GET_PRIVATE (object); - - rows = G_N_ELEMENTS (priv->headers); - gtk_table_resize (GTK_TABLE (object), rows, 4); - gtk_table_set_row_spacings (GTK_TABLE (object), 0); - gtk_table_set_col_spacings (GTK_TABLE (object), 6); - - /* Use "ypadding" instead of "row-spacing" because some rows may - * be invisible and we don't want spacing around them. */ - - for (ii = 0; ii < rows; ii++) { - gtk_table_attach ( - GTK_TABLE (object), priv->headers[ii]->title_widget, - 0, 1, ii, ii + 1, GTK_FILL, GTK_FILL, 0, 3); - gtk_table_attach ( - GTK_TABLE (object), priv->headers[ii]->input_widget, - 1, 4, ii, ii + 1, GTK_FILL | GTK_EXPAND, 0, 0, 3); - } - - /* Leave room in the "From" row for signature stuff. */ - gtk_container_child_set ( - GTK_CONTAINER (object), - priv->headers[HEADER_FROM]->input_widget, - "right-attach", 2, NULL); - - /* Now add the signature stuff. */ - widget = gtk_label_new_with_mnemonic (_("Si_gnature:")); - gtk_table_attach ( - GTK_TABLE (object), widget, - 2, 3, HEADER_FROM, HEADER_FROM + 1, 0, 0, 0, 3); - gtk_table_attach ( - GTK_TABLE (object), priv->signature_combo_box, - 3, 4, HEADER_FROM, HEADER_FROM + 1, 0, 0, 0, 3); - gtk_label_set_mnemonic_widget ( - GTK_LABEL (widget), priv->signature_combo_box); - gtk_widget_show (widget); - - return object; -} - -static void -msg_composer_hdrs_dispose (GObject *object) -{ - EMsgComposerHdrsPrivate *priv; - gint ii; - - priv = E_MSG_COMPOSER_HDRS_GET_PRIVATE (object); - - if (priv->name_selector != NULL) { - g_object_unref (priv->name_selector); - priv->name_selector = NULL; - } - - for (ii = 0; ii < G_N_ELEMENTS (priv->headers); ii++) { - if (priv->headers[ii] != NULL) { - g_object_unref (priv->headers[ii]); - priv->headers[ii] = NULL; - } - } - - if (priv->signature_combo_box != NULL) { - g_object_unref (priv->signature_combo_box); - priv->signature_combo_box = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -msg_composer_hdrs_class_init (EMsgComposerHdrsClass *class) -{ - GObjectClass *object_class; - - parent_class = g_type_class_peek_parent (class); - g_type_class_add_private (class, sizeof (EMsgComposerHdrsPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->constructor = msg_composer_hdrs_constructor; - object_class->dispose = msg_composer_hdrs_dispose; - - signal_ids[SUBJECT_CHANGED] = - g_signal_new ("subject_changed", - E_TYPE_MSG_COMPOSER_HDRS, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(EMsgComposerHdrsClass, subject_changed), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, G_TYPE_STRING); - - signal_ids[HDRS_CHANGED] = - g_signal_new ("hdrs_changed", - E_TYPE_MSG_COMPOSER_HDRS, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(EMsgComposerHdrsClass, hdrs_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signal_ids[FROM_CHANGED] = - g_signal_new ("from_changed", - E_TYPE_MSG_COMPOSER_HDRS, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(EMsgComposerHdrsClass, from_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signal_ids[SIGNATURE_CHANGED] = - g_signal_new ("signature_changed", - E_TYPE_MSG_COMPOSER_HDRS, - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -msg_composer_hdrs_init (EMsgComposerHdrs *hdrs) -{ - EComposerHeader *header; - ENameSelector *name_selector; - GtkWidget *widget; - - hdrs->priv = E_MSG_COMPOSER_HDRS_GET_PRIVATE (hdrs); - - name_selector = e_name_selector_new (); - - hdrs->priv->name_selector = name_selector; - - header = e_composer_from_header_new (_("Fr_om:")); - g_signal_connect ( - header, "changed", - G_CALLBACK (from_changed), hdrs); - hdrs->priv->headers[HEADER_FROM] = header; - - header = e_composer_text_header_new_label (_("_Reply-To:")); - hdrs->priv->headers[HEADER_REPLY_TO] = header; - - header = e_composer_name_header_new (_("_To:"), name_selector); - e_composer_header_set_input_tooltip ( - header, _("Enter the recipients of the message")); - hdrs->priv->headers[HEADER_TO] = header; - - header = e_composer_name_header_new (_("_Cc:"), name_selector); - e_composer_header_set_input_tooltip ( - header, _("Enter the addresses that will receive a " - "carbon copy of the message")); - hdrs->priv->headers[HEADER_CC] = header; - - header = e_composer_name_header_new (_("_Bcc:"), name_selector); - e_composer_header_set_input_tooltip ( - header, _("Enter the addresses that will receive a " - "carbon copy of the message without appearing in the " - "recipient list of the message")); - hdrs->priv->headers[HEADER_BCC] = header; - - header = e_composer_post_header_new (_("_Post To:")); - hdrs->priv->headers[HEADER_POST_TO] = header; - - header = e_composer_text_header_new_label (_("S_ubject:")); - hdrs->priv->headers[HEADER_SUBJECT] = header; - - /* Do this after all the headers are initialized. */ - e_composer_from_header_set_account_list ( - E_COMPOSER_FROM_HEADER (hdrs->priv->headers[HEADER_FROM]), - mail_config_get_accounts ()); - - widget = e_signature_combo_box_new (); - e_signature_combo_box_set_signature_list ( - E_SIGNATURE_COMBO_BOX (widget), - mail_config_get_signatures ()); - g_signal_connect_swapped ( - widget, "changed", - G_CALLBACK (signature_changed), hdrs); - g_signal_connect_swapped ( - widget, "refreshed", - G_CALLBACK (signature_changed), hdrs); - hdrs->priv->signature_combo_box = g_object_ref_sink (widget); - gtk_widget_show (widget); -} - -GType -e_msg_composer_hdrs_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMsgComposerHdrsClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) msg_composer_hdrs_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMsgComposerHdrs), - 0, /* n_preallocs */ - (GInstanceInitFunc) msg_composer_hdrs_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - GTK_TYPE_TABLE, "EMsgComposerHdrs", &type_info, 0); - } - - return type; -} - -GtkWidget * -e_msg_composer_hdrs_new (BonoboUIComponent *uic, int visible_mask, int visible_flags) -{ - EMsgComposerHdrs *new; - EMsgComposerHdrsPrivate *priv; - - new = g_object_new (E_TYPE_MSG_COMPOSER_HDRS, NULL); - priv = new->priv; - priv->uic = uic; - - g_object_ref_sink (new); - - new->visible_mask = visible_mask; - - headers_set_sensitivity (new); - headers_set_visibility (new, visible_flags); - - return GTK_WIDGET (new); -} - -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 char *text_addr, *header; - gboolean seen_hidden_list = FALSE; - int i; - - to_addr = camel_internet_address_new (); - cc_addr = camel_internet_address_new (); - bcc_addr = camel_internet_address_new (); - - if (to_destv) { - for (i = 0; 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); - } - } - } - - if (cc_destv) { - for (i = 0; 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); - } - } - } - - if (bcc_destv) { - for (i = 0; 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 -e_msg_composer_hdrs_to_message_internal (EMsgComposerHdrs *hdrs, - CamelMimeMessage *msg, - gboolean redirect) -{ - EDestination **to_destv, **cc_destv, **bcc_destv; - CamelInternetAddress *addr; - const char *subject; - gboolean visible; - char *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg)); - - subject = e_msg_composer_hdrs_get_subject (hdrs); - camel_mime_message_set_subject (msg, subject); - - addr = e_msg_composer_hdrs_get_from (hdrs); - if (redirect) { - header = camel_address_encode (CAMEL_ADDRESS (addr)); - camel_medium_set_header (CAMEL_MEDIUM (msg), "Resent-From", header); - g_free (header); - } else { - camel_mime_message_set_from (msg, addr); - } - camel_object_unref (addr); - - addr = e_msg_composer_hdrs_get_reply_to (hdrs); - if (addr) { - camel_mime_message_set_reply_to (msg, addr); - camel_object_unref (addr); - } - - visible = - e_composer_header_get_visible ( - hdrs->priv->headers[HEADER_TO]) || - e_composer_header_get_visible ( - hdrs->priv->headers[HEADER_CC]) || - e_composer_header_get_visible ( - hdrs->priv->headers[HEADER_BCC]); - - if (visible) { - to_destv = e_msg_composer_hdrs_get_to (hdrs); - cc_destv = e_msg_composer_hdrs_get_cc (hdrs); - bcc_destv = e_msg_composer_hdrs_get_bcc (hdrs); - - /* Attach destinations to the message. */ - - set_recipients_from_destv (msg, to_destv, cc_destv, bcc_destv, redirect); - - e_destination_freev (to_destv); - e_destination_freev (cc_destv); - e_destination_freev (bcc_destv); - } - - visible = e_composer_header_get_visible ( - hdrs->priv->headers[HEADER_POST_TO]); - - if (visible) { - GList *post, *l; - - camel_medium_remove_header((CamelMedium *)msg, "X-Evolution-PostTo"); - post = e_msg_composer_hdrs_get_post_to(hdrs); - for (l=post;l;l=g_list_next(l)) { - camel_medium_add_header((CamelMedium *)msg, "X-Evolution-PostTo", l->data); - g_free(l->data); - } - g_list_free(post); - } -} - - -void -e_msg_composer_hdrs_to_message (EMsgComposerHdrs *hdrs, - CamelMimeMessage *msg) -{ - e_msg_composer_hdrs_to_message_internal (hdrs, msg, FALSE); -} - - -void -e_msg_composer_hdrs_to_redirect (EMsgComposerHdrs *hdrs, - CamelMimeMessage *msg) -{ - e_msg_composer_hdrs_to_message_internal (hdrs, msg, TRUE); -} - -EAccount * -e_msg_composer_hdrs_get_from_account (EMsgComposerHdrs *hdrs) -{ - EComposerFromHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = E_COMPOSER_FROM_HEADER (hdrs->priv->headers[HEADER_FROM]); - return e_composer_from_header_get_active (header); -} - -gboolean -e_msg_composer_hdrs_set_from_account (EMsgComposerHdrs *hdrs, - const gchar *account_name) -{ - EComposerFromHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), FALSE); - - header = E_COMPOSER_FROM_HEADER (hdrs->priv->headers[HEADER_FROM]); - return e_composer_from_header_set_active_name (header, account_name); -} - -ESignature * -e_msg_composer_hdrs_get_signature (EMsgComposerHdrs *hdrs) -{ - ESignatureComboBox *combo_box; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - combo_box = E_SIGNATURE_COMBO_BOX (hdrs->priv->signature_combo_box); - return e_signature_combo_box_get_active (combo_box); -} - -gboolean -e_msg_composer_hdrs_set_signature (EMsgComposerHdrs *hdrs, - ESignature *signature) -{ - ESignatureComboBox *combo_box; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), FALSE); - - combo_box = E_SIGNATURE_COMBO_BOX (hdrs->priv->signature_combo_box); - return e_signature_combo_box_set_active (combo_box, signature); -} - -void -e_msg_composer_hdrs_set_reply_to (EMsgComposerHdrs *hdrs, - const gchar *text) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - header = hdrs->priv->headers[HEADER_REPLY_TO]; - - e_composer_text_header_set_text ( - E_COMPOSER_TEXT_HEADER (header), text); - - if (*text != '\0') - e_composer_header_set_visible (header, TRUE); -} - -void -e_msg_composer_hdrs_set_to (EMsgComposerHdrs *hdrs, - EDestination **to_destv) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - header = hdrs->priv->headers[HEADER_TO]; - - e_composer_name_header_set_destinations ( - E_COMPOSER_NAME_HEADER (header), to_destv); -} - -void -e_msg_composer_hdrs_set_cc (EMsgComposerHdrs *hdrs, - EDestination **cc_destv) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - header = hdrs->priv->headers[HEADER_CC]; - - e_composer_name_header_set_destinations ( - E_COMPOSER_NAME_HEADER (header), cc_destv); - - if (cc_destv != NULL && *cc_destv != NULL) - e_composer_header_set_visible (header, TRUE); -} - -void -e_msg_composer_hdrs_set_bcc (EMsgComposerHdrs *hdrs, - EDestination **bcc_destv) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - header = hdrs->priv->headers[HEADER_BCC]; - - e_composer_name_header_set_destinations ( - E_COMPOSER_NAME_HEADER (header), bcc_destv); - - if (bcc_destv != NULL && *bcc_destv != NULL) - e_composer_header_set_visible (header, TRUE); -} - -void -e_msg_composer_hdrs_set_post_to (EMsgComposerHdrs *hdrs, - const char *post_to) -{ - GList *list; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - g_return_if_fail (post_to != NULL); - - list = g_list_append (NULL, g_strdup (post_to)); - - e_msg_composer_hdrs_set_post_to_list (hdrs, list); - - g_free (list->data); - g_list_free (list); -} - -void -e_msg_composer_hdrs_set_post_to_list (EMsgComposerHdrs *hdrs, GList *urls) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - header = hdrs->priv->headers[HEADER_POST_TO]; - - e_composer_post_header_set_folders ( - E_COMPOSER_POST_HEADER (header), urls); -} - -void -e_msg_composer_hdrs_set_post_to_base (EMsgComposerHdrs *hdrs, - const gchar *base, - const gchar *post_to) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - - header = hdrs->priv->headers[HEADER_POST_TO]; - - e_composer_post_header_set_folders_base ( - E_COMPOSER_POST_HEADER (header), base, post_to); -} - -void -e_msg_composer_hdrs_set_subject (EMsgComposerHdrs *hdrs, - const gchar *subject) -{ - EComposerHeader *header; - - g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs)); - g_return_if_fail (subject != NULL); - - header = hdrs->priv->headers[HEADER_SUBJECT]; - - e_composer_text_header_set_text ( - E_COMPOSER_TEXT_HEADER (header), subject); -} - - -CamelInternetAddress * -e_msg_composer_hdrs_get_from (EMsgComposerHdrs *hdrs) -{ - EComposerHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = hdrs->priv->headers[HEADER_FROM]; - - return e_composer_from_header_get_active_address ( - E_COMPOSER_FROM_HEADER (header)); -} - -CamelInternetAddress * -e_msg_composer_hdrs_get_reply_to (EMsgComposerHdrs *hdrs) -{ - CamelInternetAddress *addr; - EComposerHeader *header; - const gchar *text; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = hdrs->priv->headers[HEADER_REPLY_TO]; - - text = e_composer_text_header_get_text ( - E_COMPOSER_TEXT_HEADER (header)); - - if (text == NULL || *text == '\0') - return NULL; - - addr = camel_internet_address_new (); - if (camel_address_unformat (CAMEL_ADDRESS (addr), text) == -1) { - camel_object_unref (CAMEL_OBJECT (addr)); - return NULL; - } - - return addr; -} - -EDestination ** -e_msg_composer_hdrs_get_to (EMsgComposerHdrs *hdrs) -{ - EComposerNameHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = E_COMPOSER_NAME_HEADER (hdrs->priv->headers[HEADER_TO]); - return e_composer_name_header_get_destinations (header); -} - -EDestination ** -e_msg_composer_hdrs_get_cc (EMsgComposerHdrs *hdrs) -{ - EComposerNameHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = E_COMPOSER_NAME_HEADER (hdrs->priv->headers[HEADER_CC]); - return e_composer_name_header_get_destinations (header); -} - -EDestination ** -e_msg_composer_hdrs_get_bcc (EMsgComposerHdrs *hdrs) -{ - EComposerNameHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = E_COMPOSER_NAME_HEADER (hdrs->priv->headers[HEADER_BCC]); - return e_composer_name_header_get_destinations (header); -} - -EDestination ** -e_msg_composer_hdrs_get_recipients (EMsgComposerHdrs *hdrs) -{ - EDestination **to_destv; - EDestination **cc_destv; - EDestination **bcc_destv; - EDestination **recip_destv; - int i, j, n; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - to_destv = e_msg_composer_hdrs_get_to (hdrs); - cc_destv = e_msg_composer_hdrs_get_cc (hdrs); - bcc_destv = e_msg_composer_hdrs_get_bcc (hdrs); - - n = 0; - - for (i = 0; to_destv && to_destv[i] != NULL; i++, n++); - for (i = 0; cc_destv && cc_destv[i] != NULL; i++, n++); - for (i = 0; bcc_destv && bcc_destv[i] != NULL; i++, n++); - - if (n == 0) - return NULL; - - recip_destv = g_new (EDestination *, n + 1); - - j = 0; - - for (i = 0; to_destv && to_destv[i] != NULL; i++, j++) - recip_destv[j] = to_destv[i]; - for (i = 0; cc_destv && cc_destv[i] != NULL; i++, j++) - recip_destv[j] = cc_destv[i]; - for (i = 0; bcc_destv && bcc_destv[i] != NULL; i++, j++) - recip_destv[j] = bcc_destv[i]; - - if (j != n) { - g_warning ("j!=n \n"); - } - recip_destv[j] = NULL; - - g_free (to_destv); - g_free (cc_destv); - g_free (bcc_destv); - - return recip_destv; -} - - -GList * -e_msg_composer_hdrs_get_post_to (EMsgComposerHdrs *hdrs) -{ - EComposerHeader *header; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - header = hdrs->priv->headers[HEADER_POST_TO]; - - return e_composer_post_header_get_folders ( - E_COMPOSER_POST_HEADER (header)); -} - - -const gchar * -e_msg_composer_hdrs_get_subject (EMsgComposerHdrs *hdrs) -{ - GtkWidget *widget; - - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - widget = e_msg_composer_hdrs_get_subject_entry (hdrs); - - return gtk_entry_get_text (GTK_ENTRY (widget)); -} - - -GtkWidget * -e_msg_composer_hdrs_get_reply_to_entry (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_REPLY_TO]->input_widget; -} - -GtkWidget * -e_msg_composer_hdrs_get_to_entry (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_TO]->input_widget; -} - -GtkWidget * -e_msg_composer_hdrs_get_cc_entry (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_CC]->input_widget; -} - -GtkWidget * -e_msg_composer_hdrs_get_bcc_entry (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_BCC]->input_widget; -} - -GtkWidget * -e_msg_composer_hdrs_get_post_to_label (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_POST_TO]->input_widget; -} - -GtkWidget * -e_msg_composer_hdrs_get_subject_entry (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_SUBJECT]->input_widget; -} - -GtkWidget * -e_msg_composer_hdrs_get_from_hbox (EMsgComposerHdrs *hdrs) -{ - g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL); - - return hdrs->priv->headers[HEADER_FROM]->input_widget; -} diff --git a/composer/e-msg-composer-hdrs.h b/composer/e-msg-composer-hdrs.h deleted file mode 100644 index 41a4299cc6..0000000000 --- a/composer/e-msg-composer-hdrs.h +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* msg-composer-hdrs.h - * - * Copyright (C) 1999 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * published by the Free Software Foundation; either version 2 of the - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Author: Ettore Perazzoli - */ - -#ifndef ___E_MSG_COMPOSER_HDRS_H__ -#define ___E_MSG_COMPOSER_HDRS_H__ - -#include - -#include - -#include -#include -#include -#include - -#define E_TYPE_MSG_COMPOSER_HDRS \ - (e_msg_composer_hdrs_get_type ()) -#define E_MSG_COMPOSER_HDRS(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_MSG_COMPOSER_HDRS, EMsgComposerHdrs)) -#define E_MSG_COMPOSER_HDRS_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_MSG_COMPOSER_HDRS, EMsgComposerHdrsClass)) -#define E_IS_MSG_COMPOSER_HDRS(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_MSG_COMPOSER_HDRS)) -#define E_IS_MSG_COMPOSER_HDRS_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_MSG_COMPOSER_HDRS)) -#define E_MSG_COMPOSER_HDRS_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_MSG_COMPOSER_HDRS, EMsgComposerHdrsClass)) - -G_BEGIN_DECLS - -typedef struct _EMsgComposerHdrs EMsgComposerHdrs; -typedef struct _EMsgComposerHdrsClass EMsgComposerHdrsClass; -typedef struct _EMsgComposerHdrsPrivate EMsgComposerHdrsPrivate; - -struct _EMsgComposerHdrs { - GtkTable parent; - - EMsgComposerHdrsPrivate *priv; - - guint32 visible_mask; -}; - -struct _EMsgComposerHdrsClass { - GtkTableClass parent_class; - - void (* subject_changed) (EMsgComposerHdrs *hdrs, gchar *subject); - - void (* hdrs_changed) (EMsgComposerHdrs *hdrs); - - void (* from_changed) (EMsgComposerHdrs *hdrs); -}; - -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; - -#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) - -GType e_msg_composer_hdrs_get_type (void); -GtkWidget *e_msg_composer_hdrs_new (BonoboUIComponent *uic, int visible_mask, int visible_flags); - -void e_msg_composer_hdrs_to_message (EMsgComposerHdrs *hdrs, - CamelMimeMessage *msg); - -void e_msg_composer_hdrs_to_redirect (EMsgComposerHdrs *hdrs, - CamelMimeMessage *msg); - - -EAccount * e_msg_composer_hdrs_get_from_account (EMsgComposerHdrs *hdrs); -gboolean e_msg_composer_hdrs_set_from_account (EMsgComposerHdrs *hdrs, - const char *account_name); -ESignature *e_msg_composer_hdrs_get_signature (EMsgComposerHdrs *hdrs); -gboolean e_msg_composer_hdrs_set_signature (EMsgComposerHdrs *hdrs, - ESignature *signature); -void e_msg_composer_hdrs_set_reply_to (EMsgComposerHdrs *hdrs, - const char *reply_to); -void e_msg_composer_hdrs_set_to (EMsgComposerHdrs *hdrs, - EDestination **to_destv); -void e_msg_composer_hdrs_set_cc (EMsgComposerHdrs *hdrs, - EDestination **cc_destv); -void e_msg_composer_hdrs_set_bcc (EMsgComposerHdrs *hdrs, - EDestination **bcc_destv); -void e_msg_composer_hdrs_set_post_to (EMsgComposerHdrs *hdrs, - const char *post_to); -void e_msg_composer_hdrs_set_post_to_list (EMsgComposerHdrs *hdrs, - GList *urls); -void e_msg_composer_hdrs_set_post_to_base (EMsgComposerHdrs *hdrs, - const gchar *base, - const gchar *post_to); -void e_msg_composer_hdrs_set_subject (EMsgComposerHdrs *hdrs, - const char *subject); - -CamelInternetAddress *e_msg_composer_hdrs_get_from (EMsgComposerHdrs *hdrs); -CamelInternetAddress *e_msg_composer_hdrs_get_reply_to (EMsgComposerHdrs *hdrs); - -EDestination **e_msg_composer_hdrs_get_to (EMsgComposerHdrs *hdrs); -EDestination **e_msg_composer_hdrs_get_cc (EMsgComposerHdrs *hdrs); -EDestination **e_msg_composer_hdrs_get_bcc (EMsgComposerHdrs *hdrs); -EDestination **e_msg_composer_hdrs_get_recipients (EMsgComposerHdrs *hdrs); -const char *e_msg_composer_hdrs_get_subject (EMsgComposerHdrs *hdrs); - -/* list of gchar* uris; this data is to be freed by the caller */ -GList *e_msg_composer_hdrs_get_post_to (EMsgComposerHdrs *hdrs); - -GtkWidget *e_msg_composer_hdrs_get_from_hbox (EMsgComposerHdrs *hdrs); -GtkWidget *e_msg_composer_hdrs_get_reply_to_entry (EMsgComposerHdrs *hdrs); -GtkWidget *e_msg_composer_hdrs_get_to_entry (EMsgComposerHdrs *hdrs); -GtkWidget *e_msg_composer_hdrs_get_cc_entry (EMsgComposerHdrs *hdrs); -GtkWidget *e_msg_composer_hdrs_get_bcc_entry (EMsgComposerHdrs *hdrs); -GtkWidget *e_msg_composer_hdrs_get_post_to_label (EMsgComposerHdrs *hdrs); -GtkWidget *e_msg_composer_hdrs_get_subject_entry (EMsgComposerHdrs *hdrs); - -void e_msg_composer_hdrs_set_visible_mask (EMsgComposerHdrs *hdrs, - int visible_mask); -void e_msg_composer_hdrs_set_visible (EMsgComposerHdrs *hdrs, - int visible_flags); - -G_END_DECLS - -#endif /* __E_MSG_COMPOSER_HDRS_H__ */ diff --git a/composer/e-msg-composer-select-file.c b/composer/e-msg-composer-select-file.c deleted file mode 100644 index f0e5b767c9..0000000000 --- a/composer/e-msg-composer-select-file.c +++ /dev/null @@ -1,203 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* - * Authors: Ettore Perazzoli - * Jeffrey Stedfast - * Michael Zucchi - * - * Copyright 2002 Ximian, Inc. (www.ximian.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "e-msg-composer-select-file.h" -#include -#include "e-msg-composer.h" -#include "e-attachment-bar.h" - -enum { - SELECTOR_MODE_MULTI = (1 << 0), - SELECTOR_MODE_SAVE = (1 << 1), - SELECTOR_SHOW_INLINE = 1<<2 -}; - -/* this is a mess */ - -static GtkWidget* -get_selector(struct _EMsgComposer *composer, const char *title, guint32 flags) -{ - GtkWidget *selection; - GtkWidget *showinline = NULL; - GList *icon_list; - const char *path; - - path = e_msg_composer_get_attach_path (composer); - - if (flags & SELECTOR_MODE_SAVE) - selection = gtk_file_chooser_dialog_new (title, - NULL, - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_OK, - NULL); - else - selection = gtk_file_chooser_dialog_new (title, - NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - _("A_ttach"), GTK_RESPONSE_OK, - NULL); - - gtk_dialog_set_default_response (GTK_DIALOG (selection), GTK_RESPONSE_OK); - gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (selection), FALSE); - - if ((flags & SELECTOR_MODE_SAVE) == 0) - gtk_file_chooser_set_select_multiple ((GtkFileChooser *) selection, (flags & SELECTOR_MODE_MULTI)); - - /* restore last path used */ - if (!path) - gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (selection), g_get_home_dir ()); - else - gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (selection), path); - - if (flags & SELECTOR_SHOW_INLINE) { - showinline = gtk_check_button_new_with_mnemonic (_("_Suggest automatic display of attachment")); - gtk_widget_show (showinline); - gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (selection), showinline); - g_object_set_data((GObject *)selection, "show-inline", showinline); - } - - gtk_window_set_transient_for ((GtkWindow *) selection, (GtkWindow *) composer); - gtk_window_set_wmclass ((GtkWindow *) selection, "fileselection", "Evolution:composer"); - gtk_window_set_modal ((GtkWindow *) selection, FALSE); - - icon_list = e_icon_factory_get_icon_list ("mail-message-new"); - if (icon_list) { - gtk_window_set_icon_list (GTK_WINDOW (selection), icon_list); - g_list_foreach (icon_list, (GFunc) g_object_unref, NULL); - g_list_free (icon_list); - } - - return selection; -} - -static void -select_file_response(GtkWidget *selector, guint response, struct _EMsgComposer *composer) -{ - if (response == GTK_RESPONSE_OK) { - const char *name; - char *path; - EMsgComposerSelectFileFunc func = g_object_get_data((GObject *)selector, "callback"); - - name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (selector)); - path = g_path_get_dirname (gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (selector))); - e_msg_composer_set_attach_path (composer, path); - g_free (path); - - func(composer, name); - } - - gtk_widget_destroy(selector); -} - -/** - * e_msg_composer_select_file: - * @composer: a composer - * @w: widget pointer, so same dialog is not re-shown - * @func: callback invoked if the user selected a file - * @title: the title for the file selection dialog box - * @save: whether the file selection box should be shown in save mode or not - * - * This pops up a file selection dialog box with the given title - * and allows the user to select a single file. - * - **/ -void e_msg_composer_select_file(struct _EMsgComposer *composer, GtkWidget **w, EMsgComposerSelectFileFunc func, const char *title, int save) -{ - if (*w) { - gtk_window_present((GtkWindow *)*w); - return; - } - - *w = get_selector (composer, title, save ? SELECTOR_MODE_SAVE : 0); - g_signal_connect(*w, "response", G_CALLBACK(select_file_response), composer); - g_signal_connect(*w, "destroy", G_CALLBACK(gtk_widget_destroyed), w); - g_object_set_data((GObject *)*w, "callback", func); - gtk_widget_show(*w); -} - - -static void -select_attach_response(GtkWidget *selector, guint response, struct _EMsgComposer *composer) -{ - if (response == GTK_RESPONSE_OK) { - GSList *names; - EMsgComposerSelectAttachFunc func = g_object_get_data((GObject *)selector, "callback"); - GtkToggleButton *showinline = g_object_get_data((GObject *)selector, "show-inline"); - char *path = NULL; - - char *filename = NULL; - names = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (selector)); - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (selector)); - if (filename) { - path = g_path_get_dirname (filename); - g_free (filename); - } - if (path) - e_msg_composer_set_attach_path (composer, path); - g_free (path); - - func(composer, names, gtk_toggle_button_get_active(showinline)); - - e_msg_composer_show_attachments_ui (composer); - - - g_slist_foreach(names, (GFunc)g_free, NULL); - g_slist_free(names); - } - - gtk_widget_destroy(selector); -} - -void e_msg_composer_select_file_attachments(struct _EMsgComposer *composer, GtkWidget **w, EMsgComposerSelectAttachFunc func) -{ - if (*w) { - gtk_window_present((GtkWindow *)*w); - return; - } - - *w = get_selector (composer, _("Insert Attachment"), SELECTOR_MODE_MULTI|SELECTOR_SHOW_INLINE); - g_signal_connect(*w, "response", G_CALLBACK(select_attach_response), composer); - g_signal_connect(*w, "destroy", G_CALLBACK(gtk_widget_destroyed), w); - g_object_set_data((GObject *)*w, "callback", func); - gtk_widget_show(*w); -} diff --git a/composer/e-msg-composer-select-file.h b/composer/e-msg-composer-select-file.h deleted file mode 100644 index 42dbe62515..0000000000 --- a/composer/e-msg-composer-select-file.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* e-msg-composer-select-file.c - * - * Copyright (C) 2000 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * published by the Free Software Foundation; either version 2 of the - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Author: Ettore Perazzoli - */ - -#ifndef E_MSG_COMPOSER_SELECT_FILE_H -#define E_MSG_COMPOSER_SELECT_FILE_H - - -struct _EMsgComposer; - -typedef void (*EMsgComposerSelectFileFunc)(struct _EMsgComposer *composer, const char *filename); -typedef void (*EMsgComposerSelectAttachFunc)(struct _EMsgComposer *composer, GSList *names, int isinline); - -void e_msg_composer_select_file(struct _EMsgComposer *composer, GtkWidget **w, EMsgComposerSelectFileFunc func, const char *title, int save); -void e_msg_composer_select_file_attachments(struct _EMsgComposer *composer, GtkWidget **, EMsgComposerSelectAttachFunc func); - -#endif /* E_MSG_COMPOSER_SELECT_FILE_H */ 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; } diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h index a6c8e719fc..251c170426 100644 --- a/composer/e-msg-composer.h +++ b/composer/e-msg-composer.h @@ -21,187 +21,142 @@ * Author: Ettore Perazzoli */ - -#ifndef ___E_MSG_COMPOSER_H__ -#define ___E_MSG_COMPOSER_H__ - -typedef struct _EMsgComposer EMsgComposer; -typedef struct _EMsgComposerClass EMsgComposerClass; - -#include -#include - -#include "e-msg-composer-hdrs.h" -#include "Editor.h" - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - - -#define E_TYPE_MSG_COMPOSER (e_msg_composer_get_type ()) -#define E_MSG_COMPOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_MSG_COMPOSER, EMsgComposer)) -#define E_MSG_COMPOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER, EMsgComposerClass)) -#define E_IS_MSG_COMPOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_MSG_COMPOSER)) -#define E_IS_MSG_COMPOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER)) - - +#ifndef E_MSG_COMPOSER_H +#define E_MSG_COMPOSER_H + +#include +#include +#include +#include +#include + +#include "e-composer-header-table.h" + +/* Standard GObject macros */ +#define E_TYPE_MSG_COMPOSER \ + (e_msg_composer_get_type ()) +#define E_MSG_COMPOSER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MSG_COMPOSER, EMsgComposer)) +#define E_MSG_COMPOSER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MSG_COMPOSER, EMsgComposerClass)) +#define E_IS_MSG_COMPOSER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MSG_COMPOSER)) +#define E_IS_MSG_COMPOSER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((obj), E_TYPE_MSG_COMPOSER)) +#define E_MSG_COMPOSER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MSG_COMPOSER, EMsgComposerClass)) + +G_BEGIN_DECLS + +typedef struct _EMsgComposer EMsgComposer; +typedef struct _EMsgComposerClass EMsgComposerClass; +typedef struct _EMsgComposerPrivate EMsgComposerPrivate; + +struct _EMsgComposer { + GtkhtmlEditor parent; + EMsgComposerPrivate *priv; +}; + +struct _EMsgComposerClass { + GtkhtmlEditorClass parent_class; +}; struct _EAttachmentBar; -GtkType e_msg_composer_get_type (void); - -EMsgComposer *e_msg_composer_new (void); - #define E_MSG_COMPOSER_MAIL 1 #define E_MSG_COMPOSER_POST 2 #define E_MSG_COMPOSER_MAIL_POST E_MSG_COMPOSER_MAIL|E_MSG_COMPOSER_POST -EMsgComposer *e_msg_composer_new_with_type (int type); - -EMsgComposer *e_msg_composer_new_with_message (CamelMimeMessage *msg); -EMsgComposer *e_msg_composer_new_from_url (const char *url); -EMsgComposer *e_msg_composer_new_redirect (CamelMimeMessage *message, - const char *resent_from); -void e_msg_composer_show_attachments_ui (EMsgComposer *composer); - -void e_msg_composer_set_alternative (EMsgComposer *composer, - gboolean alt); - -void e_msg_composer_set_headers (EMsgComposer *composer, - const char *from, - EDestination **to, - EDestination **cc, - EDestination **bcc, - const char *subject); -void e_msg_composer_set_body_text (EMsgComposer *composer, - const char *text, - ssize_t len); -void e_msg_composer_set_body (EMsgComposer *composer, - const char *body, - const char *mime_type); -void e_msg_composer_add_header (EMsgComposer *composer, - const char *name, - const char *value); -void e_msg_composer_modify_header (EMsgComposer *composer, - const char *name, - const char *value); -void e_msg_composer_remove_header (EMsgComposer *composer, - const char *name); -void e_msg_composer_attach (EMsgComposer *composer, - CamelMimePart *attachment); -CamelMimePart *e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, - const char *filename); -void e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer, - CamelMimePart *part); -CamelMimeMessage *e_msg_composer_get_message (EMsgComposer *composer, - gboolean save_html_object_data); -CamelMimeMessage *e_msg_composer_get_message_print (EMsgComposer *composer, - gboolean save_html_object_data); -CamelMimeMessage *e_msg_composer_get_message_draft (EMsgComposer *composer); -void e_msg_composer_show_sig_file (EMsgComposer *composer); -gboolean e_msg_composer_get_send_html (EMsgComposer *composer); -void e_msg_composer_set_send_html (EMsgComposer *composer, - gboolean send_html); - -gboolean e_msg_composer_get_view_from (EMsgComposer *composer); -void e_msg_composer_set_view_from (EMsgComposer *composer, - gboolean view_from); -gboolean e_msg_composer_get_view_to (EMsgComposer *composer); -void e_msg_composer_set_view_to (EMsgComposer *composer, - gboolean view_replyto); -gboolean e_msg_composer_get_view_replyto (EMsgComposer *composer); -void e_msg_composer_set_view_replyto (EMsgComposer *composer, - gboolean view_replyto); -gboolean e_msg_composer_get_view_postto (EMsgComposer *composer); -void e_msg_composer_set_view_postto (EMsgComposer *composer, - gboolean view_replyto); -gboolean e_msg_composer_get_view_cc (EMsgComposer *composer); -void e_msg_composer_set_view_cc (EMsgComposer *composer, - gboolean view_cc); -gboolean e_msg_composer_get_view_bcc (EMsgComposer *composer); -void e_msg_composer_set_view_bcc (EMsgComposer *composer, - gboolean view_bcc); - -gboolean e_msg_composer_get_request_receipt (EMsgComposer *composer); -void e_msg_composer_set_request_receipt (EMsgComposer *composer, - gboolean request_receipt); - -gboolean e_msg_composer_get_priority (EMsgComposer *composer); -void e_msg_composer_set_priority (EMsgComposer *composer, - gboolean set_priority); - -EDestination **e_msg_composer_get_recipients (EMsgComposer *composer); -EDestination **e_msg_composer_get_to (EMsgComposer *composer); -EDestination **e_msg_composer_get_cc (EMsgComposer *composer); -EDestination **e_msg_composer_get_bcc (EMsgComposer *composer); -const char *e_msg_composer_get_subject (EMsgComposer *composer); - -EAccount *e_msg_composer_get_preferred_account (EMsgComposer *composer); -void e_msg_composer_clear_inlined_table (EMsgComposer *composer); -char *e_msg_composer_guess_mime_type (const char *file_name); -void e_msg_composer_set_changed (EMsgComposer *composer); -void e_msg_composer_unset_changed (EMsgComposer *composer); -gboolean e_msg_composer_is_dirty (EMsgComposer *composer); -void e_msg_composer_set_autosaved (EMsgComposer *composer); -void e_msg_composer_unset_autosaved (EMsgComposer *composer); -gboolean e_msg_composer_is_autosaved (EMsgComposer *composer); -void e_msg_composer_set_enable_autosave (EMsgComposer *composer, - gboolean enabled); - -/* PGP */ -void e_msg_composer_set_pgp_sign (EMsgComposer *composer, - gboolean pgp_sign); -gboolean e_msg_composer_get_pgp_sign (EMsgComposer *composer); -void e_msg_composer_set_pgp_encrypt (EMsgComposer *composer, - gboolean pgp_encrypt); -gboolean e_msg_composer_get_pgp_encrypt (EMsgComposer *composer); - -/* S/MIME */ -void e_msg_composer_set_smime_sign (EMsgComposer *composer, - gboolean smime_sign); -gboolean e_msg_composer_get_smime_sign (EMsgComposer *composer); -void e_msg_composer_set_smime_encrypt (EMsgComposer *composer, - gboolean smime_encrypt); -gboolean e_msg_composer_get_smime_encrypt (EMsgComposer *composer); -char *e_msg_composer_get_sig_file_content (const char *sigfile, - gboolean in_html); -void e_msg_composer_add_message_attachments (EMsgComposer *composer, - CamelMimeMessage *message, - gboolean just_inlines); -void e_msg_composer_ignore (EMsgComposer *composer, - const char *str); -void e_msg_composer_drop_editor_undo (EMsgComposer *composer); - -gboolean e_msg_composer_request_close_all (void); -void e_msg_composer_check_autosave (GtkWindow *parent); -int e_msg_composer_get_remote_download_count (EMsgComposer *composer); - - -void e_msg_composer_reply_indent (EMsgComposer *composer); -void e_msg_composer_insert_paragraph_before (EMsgComposer *composer); -void e_msg_composer_insert_paragraph_after (EMsgComposer *composer); -void e_msg_composer_delete (EMsgComposer *composer); -gchar* e_msg_composer_resolve_image_url (EMsgComposer *composer, gchar *url); -CamelMimePart* e_msg_composer_url_requested (EMsgComposer *composer, gchar *url); - -void e_msg_composer_link_clicked (EMsgComposer *composer, const gchar *url); - -EMsgComposerHdrs* e_msg_composer_get_hdrs (EMsgComposer *composer); -void e_msg_composer_set_saved (EMsgComposer *composer); -void e_msg_composer_set_send_options (EMsgComposer *composer, - gboolean send_enable); -GByteArray * e_msg_composer_get_raw_message_text (EMsgComposer *composer); - -struct _EAttachmentBar* e_msg_composer_get_attachment_bar (EMsgComposer *composer); - -void e_msg_composer_set_attach_path (EMsgComposer *composer, const gchar *path); -const gchar * e_msg_composer_get_attach_path (EMsgComposer *composer); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* ___E_MSG_COMPOSER_H__ */ +GType e_msg_composer_get_type (void); +EMsgComposer * e_msg_composer_new (void); +EMsgComposer * e_msg_composer_new_with_type (gint type); +EMsgComposer * e_msg_composer_new_with_message (CamelMimeMessage *msg); +EMsgComposer * e_msg_composer_new_from_url (const gchar *url); +EMsgComposer * e_msg_composer_new_redirect (CamelMimeMessage *message, + const gchar *resent_from); + +void e_msg_composer_send (EMsgComposer *composer); +void e_msg_composer_save_draft (EMsgComposer *composer); + +void e_msg_composer_set_alternative (EMsgComposer *composer, + gboolean alt); + +void e_msg_composer_set_body_text (EMsgComposer *composer, + const gchar *text, + gssize len); +void e_msg_composer_set_body (EMsgComposer *composer, + const gchar *body, + const gchar *mime_type); +void e_msg_composer_add_header (EMsgComposer *composer, + const gchar *name, + const gchar *value); +void e_msg_composer_modify_header (EMsgComposer *composer, + const gchar *name, + const gchar *value); +void e_msg_composer_remove_header (EMsgComposer *composer, + const gchar *name); +void e_msg_composer_attach (EMsgComposer *composer, + CamelMimePart *attachment); +CamelMimePart * e_msg_composer_add_inline_image_from_file + (EMsgComposer *composer, + const gchar *filename); +void e_msg_composer_add_inline_image_from_mime_part + (EMsgComposer *composer, + CamelMimePart *part); +CamelMimeMessage * + e_msg_composer_get_message (EMsgComposer *composer, + gboolean save_html_object_data); +CamelMimeMessage * + e_msg_composer_get_message_print(EMsgComposer *composer, + gboolean save_html_object_data); +CamelMimeMessage * + e_msg_composer_get_message_draft(EMsgComposer *composer); +void e_msg_composer_show_sig_file (EMsgComposer *composer); + +CamelInternetAddress * + e_msg_composer_get_from (EMsgComposer *composer); +CamelInternetAddress * + e_msg_composer_get_reply_to (EMsgComposer *composer); + +void e_msg_composer_clear_inlined_table + (EMsgComposer *composer); +gchar * e_msg_composer_guess_mime_type (const gchar *filename); +void e_msg_composer_set_enable_autosave + (EMsgComposer *composer, + gboolean enabled); + +gchar * e_msg_composer_get_sig_file_content + (const gchar *sigfile, + gboolean in_html); +void e_msg_composer_add_message_attachments + (EMsgComposer *composer, + CamelMimeMessage *message, + gboolean just_inlines); + +gboolean e_msg_composer_request_close_all(void); +EMsgComposer * e_msg_composer_load_from_file (const gchar *filename); +void e_msg_composer_check_autosave (GtkWindow *parent); +gint e_msg_composer_get_remote_download_count + (EMsgComposer *composer); + +void e_msg_composer_reply_indent (EMsgComposer *composer); + +EComposerHeaderTable * + e_msg_composer_get_header_table (EMsgComposer *composer); +void e_msg_composer_set_send_options (EMsgComposer *composer, + gboolean send_enable); +GByteArray * e_msg_composer_get_raw_message_text + (EMsgComposer *composer); + +struct _EAttachmentBar * + e_msg_composer_get_attachment_bar + (EMsgComposer *composer); + +G_END_DECLS + +#endif /* E_MSG_COMPOSER_H */ diff --git a/composer/evolution-composer.c b/composer/evolution-composer.c deleted file mode 100644 index 5c20c3a6aa..0000000000 --- a/composer/evolution-composer.c +++ /dev/null @@ -1,395 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* evolution-composer.c - * - * Copyright (C) 2000 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * published by the Free Software Foundation; either version 2 of the - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Author: Dan Winship - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include "evolution-composer.h" -#include "mail/mail-config.h" -#include "libedataserver/e-account-list.h" -#include -#include - -#define PARENT_TYPE BONOBO_OBJECT_TYPE -static BonoboObjectClass *parent_class = NULL; - -struct _EvolutionComposerPrivate { - int send_id; - int save_draft_id; - - void (*send_cb) (EMsgComposer *, gpointer); - void (*save_draft_cb) (EMsgComposer *, int, gpointer); -}; - -/* CORBA interface implementation. */ -static EDestination ** -corba_recipientlist_to_destv (const GNOME_Evolution_Composer_RecipientList *cl) -{ - GNOME_Evolution_Composer_Recipient *recip; - EDestination **destv; - int i; - - if (cl->_length == 0) - return NULL; - - destv = g_new (EDestination *, cl->_length+1); - - for (i = 0; i < cl->_length; ++i) { - recip = &(cl->_buffer[i]); - - destv[i] = e_destination_new (); - - if (*recip->name) - e_destination_set_name (destv[i], recip->name); - e_destination_set_email (destv[i], recip->address); - - } - destv[cl->_length] = NULL; - - return destv; -} - -static void -impl_Composer_set_headers (PortableServer_Servant servant, - const CORBA_char *from, - const GNOME_Evolution_Composer_RecipientList *to, - const GNOME_Evolution_Composer_RecipientList *cc, - const GNOME_Evolution_Composer_RecipientList *bcc, - const CORBA_char *subject, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - EDestination **tov, **ccv, **bccv; - EAccountList *accounts; - EAccount *account; - EIterator *iter; - int found = 0; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - account = mail_config_get_account_by_name (from); - if (!account) { - accounts = mail_config_get_accounts (); - iter = e_list_get_iterator ((EList *) accounts); - while (e_iterator_is_valid (iter)) { - account = (EAccount *) e_iterator_get (iter); - - if (!g_ascii_strcasecmp (account->id->address, from)) { - found = TRUE; - break; - } - - e_iterator_next (iter); - } - - g_object_unref (iter); - - if (!found) - account = mail_config_get_default_account (); - } - - tov = corba_recipientlist_to_destv (to); - ccv = corba_recipientlist_to_destv (cc); - bccv = corba_recipientlist_to_destv (bcc); - - e_msg_composer_set_headers (composer->composer, account->name, - tov, ccv, bccv, subject); - - e_destination_freev (tov); - e_destination_freev (ccv); - e_destination_freev (bccv); -} - -static void -impl_Composer_set_multipart_type (PortableServer_Servant servant, - GNOME_Evolution_Composer_MultipartType type, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - if (type == GNOME_Evolution_Composer_ALTERNATIVE) { - e_msg_composer_set_alternative (composer->composer, TRUE); - } -} - -static void -impl_Composer_set_body (PortableServer_Servant servant, - const CORBA_char *body, - const CORBA_char *mime_type, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - if (!g_ascii_strcasecmp (mime_type, "text/plain")) { - char *htmlbody = camel_text_to_html (body, CAMEL_MIME_FILTER_TOHTML_PRE, 0); - - e_msg_composer_set_body_text (composer->composer, htmlbody, -1); - g_free (htmlbody); - } else if (!g_ascii_strcasecmp (mime_type, "text/html")) - e_msg_composer_set_body_text (composer->composer, body, -1); - else - e_msg_composer_set_body (composer->composer, body, mime_type); -} - -static void -impl_Composer_attach_MIME (PortableServer_Servant servant, - const CORBA_char *data, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - CamelMimePart *attachment; - CamelStream *mem_stream; - int status; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - mem_stream = camel_stream_mem_new_with_buffer (data, strlen (data)); - attachment = camel_mime_part_new (); - status = camel_data_wrapper_construct_from_stream ( - CAMEL_DATA_WRAPPER (attachment), mem_stream); - camel_object_unref (mem_stream); - - if (status == -1) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, - ex_GNOME_Evolution_Composer_CouldNotParse, - NULL); - return; - } - - e_msg_composer_attach (composer->composer, attachment); - camel_object_unref (CAMEL_OBJECT (attachment)); -} - -static void -impl_Composer_attach_data (PortableServer_Servant servant, - const CORBA_char *content_type, - const CORBA_char *filename, - const CORBA_char *description, - const CORBA_boolean show_inline, - const GNOME_Evolution_Composer_AttachmentData *data, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - CamelMimePart *attachment; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - attachment = camel_mime_part_new (); - camel_mime_part_set_content (attachment, data->_buffer, data->_length, - content_type); - - if (*filename) - camel_mime_part_set_filename (attachment, filename); - if (*description) - camel_mime_part_set_description (attachment, description); - camel_mime_part_set_disposition (attachment, show_inline ? - "inline" : "attachment"); - - e_msg_composer_attach (composer->composer, attachment); - camel_object_unref (attachment); -} - -static void -impl_Composer_show (PortableServer_Servant servant, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - gtk_widget_show (GTK_WIDGET (composer->composer)); -} - -static void -impl_Composer_send (PortableServer_Servant servant, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EvolutionComposer *composer; - - bonobo_object = bonobo_object_from_servant (servant); - composer = EVOLUTION_COMPOSER (bonobo_object); - - composer->priv->send_cb (composer->composer, NULL); -} - -POA_GNOME_Evolution_Composer__epv * -evolution_composer_get_epv (void) -{ - POA_GNOME_Evolution_Composer__epv *epv; - - epv = g_new0 (POA_GNOME_Evolution_Composer__epv, 1); - epv->setHeaders = impl_Composer_set_headers; - epv->setMultipartType = impl_Composer_set_multipart_type; - epv->setBody = impl_Composer_set_body; - epv->attachMIME = impl_Composer_attach_MIME; - epv->attachData = impl_Composer_attach_data; - epv->show = impl_Composer_show; - epv->send = impl_Composer_send; - - return epv; -} - - -/* GObject stuff */ - -static void -finalise (GObject *object) -{ - EvolutionComposer *composer = EVOLUTION_COMPOSER (object); - struct _EvolutionComposerPrivate *p = composer->priv; - - g_signal_handler_disconnect(composer->composer, p->send_id); - g_signal_handler_disconnect(composer->composer, p->save_draft_id); - g_free(p); - - if (composer->composer) { - g_object_unref(composer->composer); - composer->composer = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -evolution_composer_class_init (EvolutionComposerClass *klass) -{ - GObjectClass *object_class; - POA_GNOME_Evolution_Composer__epv *epv; - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = finalise; - - parent_class = g_type_class_ref(bonobo_object_get_type ()); - - epv = &klass->epv; - - epv->setHeaders = impl_Composer_set_headers; - epv->setMultipartType = impl_Composer_set_multipart_type; - epv->setBody = impl_Composer_set_body; - epv->attachMIME = impl_Composer_attach_MIME; - epv->attachData = impl_Composer_attach_data; - epv->show = impl_Composer_show; - epv->send = impl_Composer_send; -} - -static void -evolution_composer_init (EvolutionComposer *composer) -{ - composer->composer = e_msg_composer_new (); - composer->priv = g_malloc0(sizeof(*composer->priv)); -} - -#if 0 -static Bonobo_ItemContainer_ObjectNames * -enum_objects (BonoboItemHandler *handler, gpointer data, CORBA_Environment *ev) -{ -} -#endif - -static Bonobo_Unknown -get_object (BonoboItemHandler *h, const char *item_name, gboolean only_if_exists, - gpointer data, CORBA_Environment *ev) -{ - EvolutionComposer *composer = data; - GSList *options, *l; - - options = bonobo_item_option_parse (item_name); - for (l = options; l; l = l->next){ - BonoboItemOption *option = l->data; - - if (strcmp (option->key, "visible") == 0){ - gboolean show = 1; - - if (option->value) - show = atoi (option->value); - - if (show) - gtk_widget_show (GTK_WIDGET (composer->composer)); - else - gtk_widget_hide (GTK_WIDGET (composer->composer)); - } - } - return bonobo_object_dup_ref ( - BONOBO_OBJECT (composer)->corba_objref, ev); -} - -void -evolution_composer_construct (EvolutionComposer *composer, - GNOME_Evolution_Composer corba_object) -{ - BonoboObject *item_handler; - - g_return_if_fail (composer != NULL); - g_return_if_fail (EVOLUTION_IS_COMPOSER (composer)); - g_return_if_fail (corba_object != CORBA_OBJECT_NIL); - - /*bonobo_object_construct (BONOBO_OBJECT (composer), corba_object);*/ - - item_handler = BONOBO_OBJECT ( - bonobo_item_handler_new (NULL, get_object, composer)); - bonobo_object_add_interface (BONOBO_OBJECT (composer), BONOBO_OBJECT (item_handler)); -} - -EvolutionComposer * -evolution_composer_new (void (*send) (EMsgComposer *, gpointer), - void (*save_draft) (EMsgComposer *, int, gpointer)) -{ - EvolutionComposer *new; - struct _EvolutionComposerPrivate *p; - - new = g_object_new(EVOLUTION_TYPE_COMPOSER, NULL); - evolution_composer_construct (new, bonobo_object_corba_objref((BonoboObject *)new)); - p = new->priv; - p->send_cb = send; - p->save_draft_cb = save_draft; - p->send_id = g_signal_connect (new->composer, "send", G_CALLBACK (send), NULL); - p->save_draft_id = g_signal_connect (new->composer, "save-draft", G_CALLBACK (save_draft), NULL); - - return new; -} - -BONOBO_TYPE_FUNC_FULL(EvolutionComposer, GNOME_Evolution_Composer, BONOBO_TYPE_OBJECT, evolution_composer) diff --git a/composer/evolution-composer.h b/composer/evolution-composer.h deleted file mode 100644 index 6ff2ac18cf..0000000000 --- a/composer/evolution-composer.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* evolution-composer.h - * - * Copyright (C) 2000 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * published by the Free Software Foundation; either version 2 of the - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Author: Dan Winship - */ - -#ifndef __EVOLUTION_COMPOSER_H__ -#define __EVOLUTION_COMPOSER_H__ - -#include - -#include "Composer.h" -#include "e-msg-composer.h" - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ - -#define EVOLUTION_TYPE_COMPOSER (evolution_composer_get_type ()) -#define EVOLUTION_COMPOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVOLUTION_TYPE_COMPOSER, EvolutionComposer)) -#define EVOLUTION_COMPOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EVOLUTION_TYPE_COMPOSER, EvolutionComposerClass)) -#define EVOLUTION_IS_COMPOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVOLUTION_TYPE_COMPOSER)) -#define EVOLUTION_IS_COMPOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVOLUTION_TYPE_COMPOSER)) - -typedef struct _EvolutionComposer EvolutionComposer; -typedef struct _EvolutionComposerClass EvolutionComposerClass; - -struct _EvolutionComposer { - BonoboObject parent; - - struct _EvolutionComposerPrivate *priv; - - EMsgComposer *composer; -}; - -struct _EvolutionComposerClass { - BonoboObjectClass parent_class; - - POA_GNOME_Evolution_Composer__epv epv; -}; - -POA_GNOME_Evolution_Composer__epv *evolution_composer_get_epv (void); - -GtkType evolution_composer_get_type (void); -void evolution_composer_construct (EvolutionComposer *, - GNOME_Evolution_Composer); -EvolutionComposer *evolution_composer_new (void (*send_cb) (EMsgComposer *, gpointer), - void (*save_draft_cb) (EMsgComposer *, int, gpointer)); - -void evolution_composer_factory_init (void (*send) (EMsgComposer *, gpointer), - void (*save_draft) (EMsgComposer *, int, gpointer)); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __EVOLUTION_COMPOSER_H__ */ diff --git a/composer/evolution-composer.ui b/composer/evolution-composer.ui new file mode 100644 index 0000000000..ab907e1ca9 --- /dev/null +++ b/composer/evolution-composer.ui @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer/gconf-bridge.c b/composer/gconf-bridge.c new file mode 100644 index 0000000000..49754cfe96 --- /dev/null +++ b/composer/gconf-bridge.c @@ -0,0 +1,1249 @@ +/* + * (C) 2005 OpenedHand Ltd. + * + * Author: Jorn Baayen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include +#include + +#include "gconf-bridge.h" + +struct _GConfBridge { + GConfClient *client; + + GHashTable *bindings; +}; + +/* The data structures for the different kinds of bindings */ +typedef enum { + BINDING_PROP, + BINDING_WINDOW, + BINDING_LIST_STORE +} BindingType; + +typedef struct { + BindingType type; + guint id; + + gboolean delayed_mode; + + char *key; + guint val_notify_id; + GSList *val_changes; /* List of changes made to GConf value, + that have not received change notification + yet. */ + + GObject *object; + GParamSpec *prop; + gulong prop_notify_id; + + guint sync_timeout_id; /* Used in delayed mode */ +} PropBinding; + +typedef struct { + BindingType type; + guint id; + + gboolean bind_size; + gboolean bind_pos; + + char *key_prefix; + + GtkWindow *window; + gulong configure_event_id; + gulong unmap_id; + guint sync_timeout_id; +} WindowBinding; + +typedef struct { + BindingType type; + guint id; + + char *key; + guint val_notify_id; + GSList *val_changes; /* List of changes made to GConf value, + that have not received change notification + yet. */ + + GtkListStore *list_store; + guint row_inserted_id; + guint row_changed_id; + guint row_deleted_id; + guint rows_reordered_id; + + guint sync_idle_id; +} ListStoreBinding; + +/* Some trickery to be able to treat the data structures generically */ +typedef union { + BindingType type; + + PropBinding prop_binding; + WindowBinding window_binding; + ListStoreBinding list_store_binding; +} Binding; + +/* Function prototypes */ +static void +unbind (Binding *binding); + +#if !HAVE_DECL_GCONF_VALUE_COMPARE /* Not in headers in GConf < 2.13 */ +int gconf_value_compare (const GConfValue *value_a, + const GConfValue *value_b); +#endif + +static GConfBridge *bridge = NULL; /* Global GConfBridge object */ + +/* Free up all resources allocated by the GConfBridge. Called on exit. */ +static void +destroy_bridge (void) +{ + g_hash_table_destroy (bridge->bindings); + g_object_unref (bridge->client); + + g_free (bridge); +} + +/** + * gconf_bridge_get + * + * Returns the #GConfBridge. This is a singleton object. + * + * Return value: The #GConfBridge. + **/ +GConfBridge * +gconf_bridge_get (void) +{ + if (bridge) + return bridge; + + gconf_bridge_install_default_error_handler (); + + bridge = g_new (GConfBridge, 1); + + bridge->client = gconf_client_get_default (); + bridge->bindings = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) unbind); + + g_atexit (destroy_bridge); + + return bridge; +} + +/** + * gconf_bridge_get_client + * @bridge: A #GConfBridge + * + * Returns the #GConfClient used by @bridge. This is the same #GConfClient + * as returned by gconf_client_get_default(). + * + * Return value: A #GConfClient. + **/ +GConfClient * +gconf_bridge_get_client (GConfBridge *bridge) +{ + g_return_val_if_fail (bridge != NULL, NULL); + + return bridge->client; +} + +/* Generate an ID for a new binding */ +static guint +new_id (void) +{ + static guint id_counter = 0; + + id_counter++; + + return id_counter; +} + +/* + * Property bindings + */ + +/* Syncs a value from GConf to an object property */ +static void +prop_binding_sync_pref_to_prop (PropBinding *binding, + GConfValue *pref_value) +{ + GValue src_value, value; + + /* Make sure we don't enter an infinite synchronizing loop */ + g_signal_handler_block (binding->object, binding->prop_notify_id); + + memset (&src_value, 0, sizeof (GValue)); + + /* First, convert GConfValue to GValue */ + switch (pref_value->type) { + case GCONF_VALUE_STRING: + g_value_init (&src_value, G_TYPE_STRING); + g_value_set_string (&src_value, + gconf_value_get_string (pref_value)); + break; + case GCONF_VALUE_INT: + g_value_init (&src_value, G_TYPE_INT); + g_value_set_int (&src_value, + gconf_value_get_int (pref_value)); + break; + case GCONF_VALUE_BOOL: + g_value_init (&src_value, G_TYPE_BOOLEAN); + g_value_set_boolean (&src_value, + gconf_value_get_bool (pref_value)); + break; + case GCONF_VALUE_FLOAT: + g_value_init (&src_value, G_TYPE_FLOAT); + g_value_set_float (&src_value, + gconf_value_get_float (pref_value)); + break; + default: + g_warning ("prop_binding_sync_pref_to_prop: Unhandled value " + "type '%d'.\n", pref_value->type); + + return; + } + + /* Then convert to the type expected by the object, if necessary */ + memset (&value, 0, sizeof (GValue)); + g_value_init (&value, + G_PARAM_SPEC_VALUE_TYPE (binding->prop)); + + if (src_value.g_type != value.g_type) { + if (!g_value_transform (&src_value, &value)) { + g_warning ("prop_binding_sync_pref_to_prop: Failed to " + "transform a \"%s\" to a \"%s\".", + g_type_name (src_value.g_type), + g_type_name (value.g_type)); + + goto done; + } + + g_object_set_property (binding->object, + binding->prop->name, &value); + } else { + g_object_set_property (binding->object, + binding->prop->name, &src_value); + } + +done: + g_value_unset (&src_value); + g_value_unset (&value); + + g_signal_handler_unblock (binding->object, binding->prop_notify_id); +} + +/* Syncs an object property to GConf */ +static void +prop_binding_sync_prop_to_pref (PropBinding *binding) +{ + GValue value; + GConfValue *gconf_value; + + memset (&value, 0, sizeof (GValue)); + + g_value_init (&value, + G_PARAM_SPEC_VALUE_TYPE (binding->prop)); + g_object_get_property (binding->object, + binding->prop->name, + &value); + + switch (value.g_type) { + case G_TYPE_STRING: + gconf_value = gconf_value_new (GCONF_VALUE_STRING); + gconf_value_set_string (gconf_value, + g_value_get_string (&value)); + break; + case G_TYPE_INT: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_int (&value)); + break; + case G_TYPE_UINT: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_uint (&value)); + break; + case G_TYPE_LONG: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_long (&value)); + break; + case G_TYPE_ULONG: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_ulong (&value)); + break; + case G_TYPE_INT64: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_int64 (&value)); + break; + case G_TYPE_UINT64: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_uint64 (&value)); + break; + case G_TYPE_CHAR: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_char (&value)); + break; + case G_TYPE_UCHAR: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_uchar (&value)); + break; + case G_TYPE_ENUM: + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_enum (&value)); + break; + case G_TYPE_BOOLEAN: + gconf_value = gconf_value_new (GCONF_VALUE_BOOL); + gconf_value_set_bool (gconf_value, + g_value_get_boolean (&value)); + break; + case G_TYPE_DOUBLE: + gconf_value = gconf_value_new (GCONF_VALUE_FLOAT); +#ifdef HAVE_CORBA_GCONF + /* FIXME we cast to a float explicitly as CORBA GConf + * uses doubles in its API, but treats them as floats + * when transporting them over CORBA. See #322837 */ + gconf_value_set_float (gconf_value, + (float) g_value_get_double (&value)); +#else + gconf_value_set_float (gconf_value, + g_value_get_double (&value)); +#endif + break; + case G_TYPE_FLOAT: + gconf_value = gconf_value_new (GCONF_VALUE_FLOAT); + gconf_value_set_float (gconf_value, + g_value_get_float (&value)); + break; + default: + if (g_type_is_a (value.g_type, G_TYPE_ENUM)) { + gconf_value = gconf_value_new (GCONF_VALUE_INT); + gconf_value_set_int (gconf_value, + g_value_get_enum (&value)); + } else { + g_warning ("prop_binding_sync_prop_to_pref: " + "Unhandled value type '%s'.\n", + g_type_name (value.g_type)); + + goto done; + } + + break; + } + + /* Set to GConf */ + gconf_client_set (bridge->client, binding->key, gconf_value, NULL); + + /* Store until change notification comes in, so that we are able + * to ignore it */ + binding->val_changes = g_slist_append (binding->val_changes, + gconf_value); + +done: + g_value_unset (&value); +} + +/* Called when a GConf value bound to an object property has changed */ +static void +prop_binding_pref_changed (GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + gpointer user_data) +{ + GConfValue *gconf_value; + PropBinding *binding; + GSList *l; + + gconf_value = gconf_entry_get_value (entry); + if (!gconf_value) + return; /* NULL means that the value has been unset */ + + binding = (PropBinding *) user_data; + + /* Check that this notification is not caused by sync_prop_to_pref() */ + l = g_slist_find_custom (binding->val_changes, + gconf_value, + (GCompareFunc) gconf_value_compare); + if (l) { + gconf_value_free (l->data); + + binding->val_changes = g_slist_delete_link + (binding->val_changes, l); + + return; + } + + prop_binding_sync_pref_to_prop (binding, gconf_value); +} + +/* Performs a scheduled prop-to-pref sync for a prop binding in + * delay mode */ +static gboolean +prop_binding_perform_scheduled_sync (PropBinding *binding) +{ + prop_binding_sync_prop_to_pref (binding); + + binding->sync_timeout_id = 0; + + g_object_unref (binding->object); + + return FALSE; +} + +#define PROP_BINDING_SYNC_DELAY 100 /* Delay for bindings with "delayed" + set to TRUE, in ms */ + +/* Called when an object property has changed */ +static void +prop_binding_prop_changed (GObject *object, + GParamSpec *param_spec, + PropBinding *binding) +{ + if (binding->delayed_mode) { + /* Just schedule a sync */ + if (binding->sync_timeout_id == 0) { + /* We keep a reference on the object as long as + * we haven't synced yet to make sure we don't + * lose any data */ + g_object_ref (binding->object); + + binding->sync_timeout_id = + g_timeout_add + (PROP_BINDING_SYNC_DELAY, + (GSourceFunc) + prop_binding_perform_scheduled_sync, + binding); + } + } else { + /* Directly sync */ + prop_binding_sync_prop_to_pref (binding); + } +} + +/* Called when an object is destroyed */ +static void +prop_binding_object_destroyed (gpointer user_data, + GObject *where_the_object_was) +{ + PropBinding *binding; + + binding = (PropBinding *) user_data; + binding->object = NULL; /* Don't do anything with the object + at unbind() */ + + g_hash_table_remove (bridge->bindings, + GUINT_TO_POINTER (binding->id)); +} + +/** + * gconf_bridge_bind_property_full + * @bridge: A #GConfBridge + * @key: A GConf key to be bound + * @object: A #GObject + * @prop: The property of @object to be bound + * @delayed_sync: TRUE if there should be a delay between property changes + * and syncs to GConf. Set to TRUE when binding to a rapidly-changing + * property, for example the "value" property on a #GtkAdjustment. + * + * Binds @key to @prop, causing them to have the same value at all times. + * + * The types of @key and @prop should be compatible. Floats and doubles, and + * ints, uints, longs, unlongs, int64s, uint64s, chars, uchars and enums + * can be matched up. Booleans and strings can only be matched to their + * respective types. + * + * On calling this function the current value of @key will be set to @prop. + * + * Return value: The ID of the new binding. + **/ +guint +gconf_bridge_bind_property_full (GConfBridge *bridge, + const char *key, + GObject *object, + const char *prop, + gboolean delayed_sync) +{ + GParamSpec *pspec; + PropBinding *binding; + char *signal; + GConfValue *val; + + g_return_val_if_fail (bridge != NULL, 0); + g_return_val_if_fail (key != NULL, 0); + g_return_val_if_fail (G_IS_OBJECT (object), 0); + g_return_val_if_fail (prop != NULL, 0); + + /* First, try to fetch the propertys GParamSpec off the object */ + pspec = g_object_class_find_property + (G_OBJECT_GET_CLASS (object), prop); + if (G_UNLIKELY (pspec == NULL)) { + g_warning ("gconf_bridge_bind_property_full: A property \"%s\" " + "was not found. Please make sure you are passing " + "the right property name.", prop); + + return 0; + } + + /* GParamSpec found: All good, create new binding. */ + binding = g_new (PropBinding, 1); + + binding->type = BINDING_PROP; + binding->id = new_id (); + binding->delayed_mode = delayed_sync; + binding->val_changes = NULL; + binding->key = g_strdup (key); + binding->object = object; + binding->prop = pspec; + binding->sync_timeout_id = 0; + + /* Watch GConf key */ + binding->val_notify_id = + gconf_client_notify_add (bridge->client, key, + prop_binding_pref_changed, + binding, NULL, NULL); + + /* Connect to property change notifications */ + signal = g_strconcat ("notify::", prop, NULL); + binding->prop_notify_id = + g_signal_connect (object, signal, + G_CALLBACK (prop_binding_prop_changed), + binding); + g_free (signal); + + /* Sync object to value from GConf, if set */ + val = gconf_client_get (bridge->client, key, NULL); + if (val) { + prop_binding_sync_pref_to_prop (binding, val); + gconf_value_free (val); + } + + /* Handle case where watched object gets destroyed */ + g_object_weak_ref (object, + prop_binding_object_destroyed, binding); + + /* Insert binding */ + g_hash_table_insert (bridge->bindings, + GUINT_TO_POINTER (binding->id), binding); + + /* Done */ + return binding->id; +} + +/* Unbinds a property binding */ +static void +prop_binding_unbind (PropBinding *binding) +{ + if (binding->delayed_mode && binding->sync_timeout_id > 0) { + /* Perform any scheduled syncs */ + g_source_remove (binding->sync_timeout_id); + + /* The object will still be around as we have + * a reference */ + prop_binding_perform_scheduled_sync (binding); + } + + gconf_client_notify_remove (bridge->client, + binding->val_notify_id); + g_free (binding->key); + + while (binding->val_changes) { + gconf_value_free (binding->val_changes->data); + + binding->val_changes = g_slist_delete_link + (binding->val_changes, binding->val_changes); + } + + /* The object might have been destroyed .. */ + if (binding->object) { + g_signal_handler_disconnect (binding->object, + binding->prop_notify_id); + + g_object_weak_unref (binding->object, + prop_binding_object_destroyed, binding); + } +} + +/* + * Window bindings + */ + +/* Performs a scheduled dimensions-to-prefs sync for a window binding */ +static gboolean +window_binding_perform_scheduled_sync (WindowBinding *binding) +{ + if (binding->bind_size) { + int width, height; + char *key; + GdkWindowState state; + + state = gdk_window_get_state (GTK_WIDGET (binding->window)->window); + + if (state & GDK_WINDOW_STATE_MAXIMIZED) { + key = g_strconcat (binding->key_prefix, "_maximized", NULL); + gconf_client_set_bool (bridge->client, key, TRUE, NULL); + g_free (key); + } else { + gtk_window_get_size (binding->window, &width, &height); + + key = g_strconcat (binding->key_prefix, "_width", NULL); + gconf_client_set_int (bridge->client, key, width, NULL); + g_free (key); + + key = g_strconcat (binding->key_prefix, "_height", NULL); + gconf_client_set_int (bridge->client, key, height, NULL); + g_free (key); + + key = g_strconcat (binding->key_prefix, "_maximized", NULL); + gconf_client_set_bool (bridge->client, key, FALSE, NULL); + g_free (key); + } + } + + if (binding->bind_pos) { + int x, y; + char *key; + + gtk_window_get_position (binding->window, &x, &y); + + key = g_strconcat (binding->key_prefix, "_x", NULL); + gconf_client_set_int (bridge->client, key, x, NULL); + g_free (key); + + key = g_strconcat (binding->key_prefix, "_y", NULL); + gconf_client_set_int (bridge->client, key, y, NULL); + g_free (key); + } + + binding->sync_timeout_id = 0; + + return FALSE; +} + +#define WINDOW_BINDING_SYNC_DELAY 1000 /* Delay before syncing new window + dimensions to GConf, in ms */ + +/* Called when the window han been resized or moved */ +static gboolean +window_binding_configure_event_cb (GtkWindow *window, + GdkEventConfigure *event, + WindowBinding *binding) +{ + /* Schedule a sync */ + if (binding->sync_timeout_id == 0) { + binding->sync_timeout_id = + g_timeout_add (WINDOW_BINDING_SYNC_DELAY, + (GSourceFunc) + window_binding_perform_scheduled_sync, + binding); + } + + return FALSE; +} + +/* Called when the window state is being changed */ +static gboolean +window_binding_state_event_cb (GtkWindow *window, + GdkEventWindowState *event, + WindowBinding *binding) +{ + window_binding_perform_scheduled_sync (binding); + + return FALSE; +} + +/* Called when the window is being unmapped */ +static gboolean +window_binding_unmap_cb (GtkWindow *window, + WindowBinding *binding) +{ + /* Force sync */ + if (binding->sync_timeout_id > 0) + g_source_remove (binding->sync_timeout_id); + + window_binding_perform_scheduled_sync (binding); + + return FALSE; +} + +/* Called when a window is destroyed */ +static void +window_binding_window_destroyed (gpointer user_data, + GObject *where_the_object_was) +{ + WindowBinding *binding; + + binding = (WindowBinding *) user_data; + binding->window = NULL; /* Don't do anything with the window + at unbind() */ + + g_hash_table_remove (bridge->bindings, + GUINT_TO_POINTER (binding->id)); +} + +/** + * gconf_bridge_bind_window + * @bridge: A #GConfBridge + * @key_prefix: The prefix of the GConf keys + * @window: A #GtkWindow + * @bind_size: TRUE to bind the size of @window + * @bind_pos: TRUE to bind the position of @window + * + * On calling this function @window will be resized to the values + * specified by "@key_prefix_width" and "@key_prefix_height" + * and maximixed if "@key_prefix_maximized is TRUE if + * @bind_size is TRUE, and moved to the values specified by + * "@key_prefix_x" and "@key_prefix_y" if @bind_pos is TRUE. + * The respective GConf values will be updated when the window is resized + * and/or moved. + * + * Return value: The ID of the new binding. + **/ +guint +gconf_bridge_bind_window (GConfBridge *bridge, + const char *key_prefix, + GtkWindow *window, + gboolean bind_size, + gboolean bind_pos) +{ + WindowBinding *binding; + + g_return_val_if_fail (bridge != NULL, 0); + g_return_val_if_fail (key_prefix != NULL, 0); + g_return_val_if_fail (GTK_IS_WINDOW (window), 0); + + /* Create new binding. */ + binding = g_new (WindowBinding, 1); + + binding->type = BINDING_WINDOW; + binding->id = new_id (); + binding->bind_size = bind_size; + binding->bind_pos = bind_pos; + binding->key_prefix = g_strdup (key_prefix); + binding->window = window; + binding->sync_timeout_id = 0; + + /* Set up GConf keys & sync window to GConf values */ + if (bind_size) { + char *key; + GConfValue *width_val, *height_val, *maximized_val; + + key = g_strconcat (key_prefix, "_width", NULL); + width_val = gconf_client_get (bridge->client, key, NULL); + g_free (key); + + key = g_strconcat (key_prefix, "_height", NULL); + height_val = gconf_client_get (bridge->client, key, NULL); + g_free (key); + + key = g_strconcat (key_prefix, "_maximized", NULL); + maximized_val = gconf_client_get (bridge->client, key, NULL); + g_free (key); + + if (width_val && height_val) { + gtk_window_resize (window, + gconf_value_get_int (width_val), + gconf_value_get_int (height_val)); + + gconf_value_free (width_val); + gconf_value_free (height_val); + } else if (width_val) { + gconf_value_free (width_val); + } else if (height_val) { + gconf_value_free (height_val); + } + + if (maximized_val) { + if (gconf_value_get_bool (maximized_val)) { + gtk_window_maximize (window); + } + gconf_value_free (maximized_val); + } + } + + if (bind_pos) { + char *key; + GConfValue *x_val, *y_val; + + key = g_strconcat (key_prefix, "_x", NULL); + x_val = gconf_client_get (bridge->client, key, NULL); + g_free (key); + + key = g_strconcat (key_prefix, "_y", NULL); + y_val = gconf_client_get (bridge->client, key, NULL); + g_free (key); + + if (x_val && y_val) { + gtk_window_move (window, + gconf_value_get_int (x_val), + gconf_value_get_int (y_val)); + + gconf_value_free (x_val); + gconf_value_free (y_val); + } else if (x_val) { + gconf_value_free (x_val); + } else if (y_val) { + gconf_value_free (y_val); + } + } + + /* Connect to window size change notifications */ + binding->configure_event_id = + g_signal_connect (window, + "configure-event", + G_CALLBACK + (window_binding_configure_event_cb), + binding); + + binding->configure_event_id = + g_signal_connect (window, + "window_state_event", + G_CALLBACK + (window_binding_state_event_cb), + binding); + binding->unmap_id = + g_signal_connect (window, + "unmap", + G_CALLBACK (window_binding_unmap_cb), + binding); + + /* Handle case where window gets destroyed */ + g_object_weak_ref (G_OBJECT (window), + window_binding_window_destroyed, binding); + + /* Insert binding */ + g_hash_table_insert (bridge->bindings, + GUINT_TO_POINTER (binding->id), binding); + + /* Done */ + return binding->id; +} + +/* Unbinds a window binding */ +static void +window_binding_unbind (WindowBinding *binding) +{ + if (binding->sync_timeout_id > 0) + g_source_remove (binding->sync_timeout_id); + + g_free (binding->key_prefix); + + /* The window might have been destroyed .. */ + if (binding->window) { + g_signal_handler_disconnect (binding->window, + binding->configure_event_id); + g_signal_handler_disconnect (binding->window, + binding->unmap_id); + + g_object_weak_unref (G_OBJECT (binding->window), + window_binding_window_destroyed, binding); + } +} + +/* + * List store bindings + */ + +/* Fills a GtkListStore with the string list from @value */ +static void +list_store_binding_sync_pref_to_store (ListStoreBinding *binding, + GConfValue *value) +{ + GSList *list, *l; + GtkTreeIter iter; + + /* Make sure we don't enter an infinite synchronizing loop */ + g_signal_handler_block (binding->list_store, + binding->row_inserted_id); + g_signal_handler_block (binding->list_store, + binding->row_deleted_id); + + gtk_list_store_clear (binding->list_store); + + list = gconf_value_get_list (value); + for (l = list; l; l = l->next) { + GConfValue *l_value; + const char *string; + + l_value = (GConfValue *) l->data; + string = gconf_value_get_string (l_value); + + gtk_list_store_insert_with_values (binding->list_store, + &iter, -1, + 0, string, + -1); + } + + g_signal_handler_unblock (binding->list_store, + binding->row_inserted_id); + g_signal_handler_unblock (binding->list_store, + binding->row_deleted_id); +} + +/* Sets a GConf value to the contents of a GtkListStore */ +static gboolean +list_store_binding_sync_store_to_pref (ListStoreBinding *binding) +{ + GtkTreeModel *tree_model; + GtkTreeIter iter; + GSList *list; + int res; + GConfValue *gconf_value; + + tree_model = GTK_TREE_MODEL (binding->list_store); + + /* Build list */ + list = NULL; + res = gtk_tree_model_get_iter_first (tree_model, &iter); + while (res) { + char *string; + GConfValue *tmp_value; + + gtk_tree_model_get (tree_model, &iter, + 0, &string, -1); + + tmp_value = gconf_value_new (GCONF_VALUE_STRING); + gconf_value_set_string (tmp_value, string); + + list = g_slist_append (list, tmp_value); + + res = gtk_tree_model_iter_next (tree_model, &iter); + } + + /* Create value */ + gconf_value = gconf_value_new (GCONF_VALUE_LIST); + gconf_value_set_list_type (gconf_value, GCONF_VALUE_STRING); + gconf_value_set_list_nocopy (gconf_value, list); + + /* Set */ + gconf_client_set (bridge->client, binding->key, gconf_value, NULL); + + /* Store until change notification comes in, so that we are able + * to ignore it */ + binding->val_changes = g_slist_append (binding->val_changes, + gconf_value); + + binding->sync_idle_id = 0; + + g_object_unref (binding->list_store); + + return FALSE; +} + +/* Pref changed: sync */ +static void +list_store_binding_pref_changed (GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + gpointer user_data) +{ + GConfValue *gconf_value; + ListStoreBinding *binding; + GSList *l; + + gconf_value = gconf_entry_get_value (entry); + if (!gconf_value) + return; /* NULL means that the value has been unset */ + + binding = (ListStoreBinding *) user_data; + + /* Check that this notification is not caused by + * sync_store_to_pref() */ + l = g_slist_find_custom (binding->val_changes, + gconf_value, + (GCompareFunc) gconf_value_compare); + if (l) { + gconf_value_free (l->data); + + binding->val_changes = g_slist_delete_link + (binding->val_changes, l); + + return; + } + + list_store_binding_sync_pref_to_store (binding, gconf_value); +} + +/* Called when an object is destroyed */ +static void +list_store_binding_store_destroyed (gpointer user_data, + GObject *where_the_object_was) +{ + ListStoreBinding *binding; + + binding = (ListStoreBinding *) user_data; + binding->list_store = NULL; /* Don't do anything with the store + at unbind() */ + + g_hash_table_remove (bridge->bindings, + GUINT_TO_POINTER (binding->id)); +} + +/* List store changed: Sync */ +static void +list_store_binding_store_changed_cb (ListStoreBinding *binding) +{ + if (binding->sync_idle_id == 0) { + g_object_ref (binding->list_store); + + binding->sync_idle_id = g_idle_add + ((GSourceFunc) list_store_binding_sync_store_to_pref, + binding); + } +} + +/** + * gconf_bridge_bind_string_list_store + * @bridge: A #GConfBridge + * @key: A GConf key to be bound + * @list_store: A #GtkListStore + * + * On calling this function single string column #GtkListStore @list_store + * will be kept synchronized with the GConf string list value pointed to by + * @key. On calling this function @list_store will be populated with the + * strings specified by the value of @key. + * + * Return value: The ID of the new binding. + **/ +guint +gconf_bridge_bind_string_list_store (GConfBridge *bridge, + const char *key, + GtkListStore *list_store) +{ + GtkTreeModel *tree_model; + gboolean have_one_column, is_string_column; + ListStoreBinding *binding; + GConfValue *val; + + g_return_val_if_fail (bridge != NULL, 0); + g_return_val_if_fail (key != NULL, 0); + g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), 0); + + /* Check list store suitability */ + tree_model = GTK_TREE_MODEL (list_store); + have_one_column = (gtk_tree_model_get_n_columns (tree_model) == 1); + is_string_column = (gtk_tree_model_get_column_type + (tree_model, 0) == G_TYPE_STRING); + if (G_UNLIKELY (!have_one_column || !is_string_column)) { + g_warning ("gconf_bridge_bind_string_list_store: Only " + "GtkListStores with exactly one string column are " + "supported."); + + return 0; + } + + /* Create new binding. */ + binding = g_new (ListStoreBinding, 1); + + binding->type = BINDING_LIST_STORE; + binding->id = new_id (); + binding->key = g_strdup (key); + binding->val_changes = NULL; + binding->list_store = list_store; + binding->sync_idle_id = 0; + + /* Watch GConf key */ + binding->val_notify_id = + gconf_client_notify_add (bridge->client, key, + list_store_binding_pref_changed, + binding, NULL, NULL); + + /* Connect to ListStore change notifications */ + binding->row_inserted_id = + g_signal_connect_swapped (list_store, "row-inserted", + G_CALLBACK + (list_store_binding_store_changed_cb), + binding); + binding->row_changed_id = + g_signal_connect_swapped (list_store, "row-inserted", + G_CALLBACK + (list_store_binding_store_changed_cb), + binding); + binding->row_deleted_id = + g_signal_connect_swapped (list_store, "row-inserted", + G_CALLBACK + (list_store_binding_store_changed_cb), + binding); + binding->rows_reordered_id = + g_signal_connect_swapped (list_store, "row-inserted", + G_CALLBACK + (list_store_binding_store_changed_cb), + binding); + + /* Sync object to value from GConf, if set */ + val = gconf_client_get (bridge->client, key, NULL); + if (val) { + list_store_binding_sync_pref_to_store (binding, val); + gconf_value_free (val); + } + + /* Handle case where watched object gets destroyed */ + g_object_weak_ref (G_OBJECT (list_store), + list_store_binding_store_destroyed, binding); + + /* Insert binding */ + g_hash_table_insert (bridge->bindings, + GUINT_TO_POINTER (binding->id), binding); + + /* Done */ + return binding->id; +} + +/* Unbinds a list store binding */ +static void +list_store_binding_unbind (ListStoreBinding *binding) +{ + /* Perform any scheduled syncs */ + if (binding->sync_idle_id > 0) { + g_source_remove (binding->sync_idle_id); + + /* The store will still be around as we added a reference */ + list_store_binding_sync_store_to_pref (binding); + } + + g_free (binding->key); + + while (binding->val_changes) { + gconf_value_free (binding->val_changes->data); + + binding->val_changes = g_slist_delete_link + (binding->val_changes, binding->val_changes); + } + + /* The store might have been destroyed .. */ + if (binding->list_store) { + g_signal_handler_disconnect (binding->list_store, + binding->row_inserted_id); + g_signal_handler_disconnect (binding->list_store, + binding->row_changed_id); + g_signal_handler_disconnect (binding->list_store, + binding->row_deleted_id); + g_signal_handler_disconnect (binding->list_store, + binding->rows_reordered_id); + + g_object_weak_unref (G_OBJECT (binding->list_store), + list_store_binding_store_destroyed, + binding); + } +} + +/* + * Generic unbinding + */ + +/* Unbinds a binding */ +static void +unbind (Binding *binding) +{ + /* Call specialized unbinding function */ + switch (binding->type) { + case BINDING_PROP: + prop_binding_unbind ((PropBinding *) binding); + break; + case BINDING_WINDOW: + window_binding_unbind ((WindowBinding *) binding); + break; + case BINDING_LIST_STORE: + list_store_binding_unbind ((ListStoreBinding *) binding); + break; + default: + g_warning ("Unknown binding type '%d'\n", binding->type); + break; + } + + g_free (binding); +} + +/** + * gconf_bridge_unbind + * @bridge: A #GConfBridge + * @binding_id: The ID of the binding to be removed + * + * Removes the binding with ID @binding_id. + **/ +void +gconf_bridge_unbind (GConfBridge *bridge, + guint binding_id) +{ + g_return_if_fail (bridge != NULL); + g_return_if_fail (binding_id > 0); + + /* This will trigger the hash tables value destruction + * function, which will take care of further cleanup */ + g_hash_table_remove (bridge->bindings, + GUINT_TO_POINTER (binding_id)); +} + +/* + * Error handling + */ + +/* This is the same dialog as used in eel */ +static void +error_handler (GConfClient *client, + GError *error) +{ + static gboolean shown_dialog = FALSE; + + g_warning ("GConf error:\n %s", error->message); + + if (!shown_dialog) { + char *message; + GtkWidget *dlg; + + message = g_strdup_printf (_("GConf error: %s"), + error->message); + dlg = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + message); + g_free (message); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dlg), + _("All further errors shown only on terminal.")); + gtk_window_set_title (GTK_WINDOW (dlg), ""); + + gtk_dialog_run (GTK_DIALOG (dlg)); + + gtk_widget_destroy (dlg); + + shown_dialog = TRUE; + } +} + +/** + * gconf_bridge_install_default_error_handler + * + * Sets up the default error handler. Any unhandled GConf errors will + * automatically be handled by presenting the user an error dialog. + **/ +void +gconf_bridge_install_default_error_handler (void) +{ + gconf_client_set_global_default_error_handler (error_handler); +} diff --git a/composer/gconf-bridge.h b/composer/gconf-bridge.h new file mode 100644 index 0000000000..aa7bfaefb8 --- /dev/null +++ b/composer/gconf-bridge.h @@ -0,0 +1,117 @@ +/* + * (C) 2005 OpenedHand Ltd. + * + * Author: Jorn Baayen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GCONF_BRIDGE_H__ +#define __GCONF_BRIDGE_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +void gconf_bridge_install_default_error_handler (void); + +typedef struct _GConfBridge GConfBridge; + +GConfBridge *gconf_bridge_get (void); + +GConfClient *gconf_bridge_get_client (GConfBridge *bridge); + +guint gconf_bridge_bind_property_full (GConfBridge *bridge, + const char *key, + GObject *object, + const char *prop, + gboolean delayed_sync); + +/** + * gconf_bridge_bind_property + * @bridge: A #GConfBridge + * @key: A GConf key to be bound + * @object: A #GObject + * @prop: The property of @object to be bound + * + * Binds @key to @prop without delays, causing them to have the same value at all times. See + * #gconf_bridge_bind_property_full for more details. + * + **/ +#define gconf_bridge_bind_property(bridge, key, object, prop) \ + gconf_bridge_bind_property_full ((bridge), (key), \ + (object), (prop), FALSE) + +/** + * gconf_bridge_bind_property_delayed + * @bridge: A #GConfBridge + * @key: A GConf key to be bound + * @object: A #GObject + * @prop: The property of @object to be bound + * + * Binds @key to @prop with a delay, causing them to have the same value at all + * times. See #gconf_bridge_bind_property_full for more details. + **/ +#define gconf_bridge_bind_property_delayed(bridge, key, object, prop) \ + gconf_bridge_bind_property_full ((bridge), (key), \ + (object), (prop), TRUE) + +guint gconf_bridge_bind_window (GConfBridge *bridge, + const char *key_prefix, + GtkWindow *window, + gboolean bind_size, + gboolean bind_pos); + +/** + * gconf_bridge_bind_window_size + * @bridge: A #GConfBridge + * @key_prefix: The prefix of the GConf keys + * @window: A #GtkWindow + * + * On calling this function @window will be resized to the values specified by + * "@key_prefix_width" and "@key_prefix_height". The respective + * GConf values will be updated when the window is resized. See + * #gconf_bridge_bind_window for more details. + **/ +#define gconf_bridge_bind_window_size(bridge, key_prefix, window) \ + gconf_bridge_bind_window ((bridge), (key_prefix), (window), TRUE, FALSE) + +/** + * gconf_bridge_bind_window_pos + * @bridge: A #GConfBridge + * @key_prefix: The prefix of the GConf keys + * @window: A #GtkWindow + * + * On calling this function @window will be moved to the values specified by + * "@key_prefix_x" and "@key_prefix_y". The respective GConf + * values will be updated when the window is moved. See + * #gconf_bridge_bind_window for more details. + **/ +#define gconf_bridge_bind_window_pos(bridge, key_prefix, window) \ + gconf_bridge_bind_window ((bridge), (key_prefix), (window), FALSE, TRUE) + +guint gconf_bridge_bind_string_list_store (GConfBridge *bridge, + const char *key, + GtkListStore *list_store); + +void gconf_bridge_unbind (GConfBridge *bridge, + guint binding_id); + +G_END_DECLS + +#endif /* __GCONF_BRIDGE_H__ */ diff --git a/composer/listener.c b/composer/listener.c deleted file mode 100644 index 80fb6dc6c6..0000000000 --- a/composer/listener.c +++ /dev/null @@ -1,156 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* This file is part of gnome-spell bonobo component - - Copyright (C) 2000 Ximian, Inc. - Authors: Radek Doulik - - This program is free software; you can redistribute it and/or - modify it under the terms of version 2 of the GNU General Public - License as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "listener.h" - -static BonoboObjectClass *listener_parent_class; - -inline static EditorListener * -listener_from_servant (PortableServer_Servant servant) -{ - return EDITOR_LISTENER (bonobo_object_from_servant (servant)); -} - -static CORBA_any * -get_any_null (void) -{ - CORBA_any *rv; - - rv = CORBA_any__alloc (); - rv->_type = TC_null; - - return rv; -} - -static void -insert_paragraph_before (EditorListener *l, CORBA_Environment * ev) -{ - e_msg_composer_insert_paragraph_before (l->composer); -} - -static void -insert_paragraph_after (EditorListener *l, CORBA_Environment * ev) -{ - e_msg_composer_insert_paragraph_after (l->composer); -} - -static CORBA_any * -impl_event (PortableServer_Servant _servant, - const CORBA_char * name, const CORBA_any * arg, - CORBA_Environment * ev) -{ - EditorListener *l = listener_from_servant (_servant); - CORBA_any *rv = NULL; - gchar *command; - - if (!strcmp (name, "command_before")) { - command = BONOBO_ARG_GET_STRING (arg); - if (!strcmp (command, "insert-paragraph")) { - insert_paragraph_before (l, ev); - } - } else if (!strcmp (name, "command_after")) { - command = BONOBO_ARG_GET_STRING (arg); - if (!strcmp (command, "insert-paragraph")) { - insert_paragraph_after (l, ev); - } - } else if (!strcmp (name, "image_url")) { - gchar *url; - - if ((url = e_msg_composer_resolve_image_url (l->composer, BONOBO_ARG_GET_STRING (arg)))) { - rv = bonobo_arg_new (BONOBO_ARG_STRING); - BONOBO_ARG_SET_STRING (rv, url); - /* printf ("new url: %s\n", url); */ - g_free (url); - } - } else if (!strcmp (name, "delete")) { - e_msg_composer_delete (l->composer); - - } else if (!strcmp (name, "url_requested")) { - GNOME_GtkHTML_Editor_URLRequestEvent *e = arg->_value; - CamelMimePart *part; - GByteArray *ba; - CamelStream *cstream; - CamelDataWrapper *wrapper; - - if (!e->url || e->stream == CORBA_OBJECT_NIL) - return get_any_null (); - - part = e_msg_composer_url_requested (l->composer, e->url); - - if (!part) - return get_any_null (); - - /* Write the data to a CamelStreamMem... */ - ba = g_byte_array_new (); - cstream = camel_stream_mem_new_with_byte_array (ba); - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - camel_data_wrapper_decode_to_stream (wrapper, cstream); - bonobo_stream_client_write (e->stream, ba->data, ba->len, ev); - - camel_object_unref (cstream); - - } else if (!strcmp (name, "link_clicked")) { - e_msg_composer_link_clicked (l->composer, BONOBO_ARG_GET_STRING (arg)); - } else if (!strcmp (name, "file_path_changed")) { - e_msg_composer_set_attach_path (l->composer, e_msg_composer_get_attach_path (l->composer)); - } - - return rv ? rv : get_any_null (); -} - -static void -listener_class_init (EditorListenerClass *klass) -{ - POA_GNOME_GtkHTML_Editor_Listener__epv *epv; - - listener_parent_class = g_type_class_ref(bonobo_object_get_type ()); - - epv = &klass->epv; - epv->event = impl_event; -} - -static void -listener_init(EditorListener *object) -{ -} - -BONOBO_TYPE_FUNC_FULL(EditorListener, GNOME_GtkHTML_Editor_Listener, BONOBO_TYPE_OBJECT, listener) - -EditorListener * -listener_new (EMsgComposer *composer) -{ - EditorListener *listener; - - listener = g_object_new (EDITOR_LISTENER_TYPE, NULL); - listener->composer = composer; - - return listener; -} diff --git a/composer/listener.h b/composer/listener.h deleted file mode 100644 index d9f13454c0..0000000000 --- a/composer/listener.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* This file is part of gnome-spell bonobo component - - Copyright (C) 2000 Ximian, Inc. - Authors: Radek Doulik - - This program is free software; you can redistribute it and/or - modify it under the terms of version 2 of the GNU General Public - License as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef LISTENER_H_ -#define LISTENER_H_ - -#include -#include "Editor.h" -#include "e-msg-composer.h" - -#define EDITOR_LISTENER_TYPE (listener_get_type ()) -#define EDITOR_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EDITOR_LISTENER_TYPE, EditorListener)) -#define EDITOR_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EDITOR_LISTENER_TYPE, EditorListenerClass)) -#define IS_EDITOR_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EDITOR_LISTENER_TYPE)) -#define IS_EDITOR_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EDITOR_LISTENER_TYPE)) - -typedef struct { - BonoboObject parent; - EMsgComposer *composer; -} EditorListener; - -typedef struct { - BonoboObjectClass parent_class; - - POA_GNOME_GtkHTML_Editor_Listener__epv epv; -} EditorListenerClass; - -GtkType listener_get_type (void); -EditorListener *listener_construct (EditorListener *listener, - GNOME_GtkHTML_Editor_Listener corba_listener); -EditorListener *listener_new (EMsgComposer *composer); - -#endif /* LISTENER_H_ */ -- cgit