diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2008-04-03 02:37:22 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@src.gnome.org> | 2008-04-03 02:37:22 +0800 |
commit | 16068d9b4191ea142a9e75a50eb8d260ed2bb406 (patch) | |
tree | 835a7909cd8b352d8c414986f1f5e27697b4de98 /composer | |
parent | ee50e5d68e4f1a793541f1ee4979818ed4940173 (diff) | |
download | gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.tar gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.tar.gz gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.tar.bz2 gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.tar.lz gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.tar.xz gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.tar.zst gsoc2013-evolution-16068d9b4191ea142a9e75a50eb8d260ed2bb406.zip |
** Merge the mbarnes-composer branch
2008-04-02 Matthew Barnes <mbarnes@redhat.com>
** 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
Diffstat (limited to 'composer')
36 files changed, 6981 insertions, 6487 deletions
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 <mbarnes@redhat.com> + + ** 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 <mbarnes@redhat.com> ** 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 <Evolution-Composer.idl> 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 <danw@ximian.com> - * - * (C) 2000 Ximian, Inc. - */ - -#include <Bonobo.idl> - -module GNOME { -module Evolution { - - interface Composer : Bonobo::Unknown { - struct Recipient { - string name; /* UTF-8 */ - string address; - }; - typedef sequence<Recipient> RecipientList; - - typedef sequence<char> 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 <errno.h> +#include <fcntl.h> +#include <e-util/e-error.h> +#include <mail/em-event.h> +#include <mail/em-format-html-print.h> + +#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..."), + "<Control>m", + N_("Attach a file"), + G_CALLBACK (action_attach_cb) }, + + { "close", + GTK_STOCK_CLOSE, + N_("_Close"), + "<Control>w", + N_("Close the current file"), + G_CALLBACK (action_close_cb) }, + + { "print", + GTK_STOCK_PRINT, + N_("_Print..."), + "<Control>p", + NULL, + G_CALLBACK (action_print_cb) }, + + { "print-preview", + GTK_STOCK_PRINT_PREVIEW, + N_("Print Pre_view"), + "<Shift><Control>p", + NULL, + G_CALLBACK (action_print_preview_cb) }, + + { "save", + GTK_STOCK_SAVE, + N_("_Save"), + "<Control>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"), + "<Shift><Control>s", + N_("Save as draft"), + G_CALLBACK (action_save_draft_cb) }, + + { "send", + "mail-send", + N_("S_end"), + "<Control>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 <errno.h> +#include <sys/stat.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <e-util/e-error.h> +#include <e-util/e-util.h> +#include <camel/camel-stream-fs.h> + +#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, @@ -14,9 +39,24 @@ composer_from_header_changed_cb (EAccountComboBox *combo_box, } 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 <libedataserver/e-account.h> #include <libedataserver/e-account-list.h> -#include <camel/camel-internet-address.h> #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 <string.h> +#include <glib/gi18n.h> +#include <libedataserverui/e-name-selector.h> + +#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 <libedataserver/e-account.h> +#include <libedataserver/e-account-list.h> +#include <libebook/e-destination.h> +#include <e-util/e-signature.h> +#include <e-util/e-signature-list.h> + +#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 ( @@ -189,6 +217,16 @@ composer_header_class_init (EComposerHeaderClass *class) 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, g_param_spec_boolean ( "visible", @@ -261,6 +299,32 @@ e_composer_header_get_label (EComposerHeader *header) } 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) { gboolean visible; 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 <glib/gi18n.h> @@ -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 <string.h> 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 <libedataserver/e-account.h> #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 <glib/gi18n.h> +#include <glib/gstdio.h> +#include "gconf-bridge.h" + +#include <libedataserver/e-iconv.h> + +#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 <config.h> -#endif - -#include <bonobo/bonobo-control.h> -#include <bonobo/bonobo-widget.h> - -#include <glib/gi18n.h> -#include <libedataserverui/e-name-selector.h> - -#include "Composer.h" - -#include <camel/camel.h> -#include <camel/camel-store.h> -#include <camel/camel-session.h> -#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 <gtk/gtk.h> - -#include <bonobo/bonobo-ui-component.h> - -#include <e-util/e-signature.h> -#include <libedataserver/e-account.h> -#include <camel/camel-mime-message.h> -#include <libebook/e-destination.h> - -#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 <ettore@ximian.com> - * Jeffrey Stedfast <fejj@ximian.com> - * Michael Zucchi <notzed@ximian.com> - * - * 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 <config.h> -#endif - -#include <gtk/gtkbox.h> -#include <gtk/gtkcheckbutton.h> -#include <gtk/gtkmain.h> -#include <gtk/gtksignal.h> -#include <gtk/gtkversion.h> - -#include <gtk/gtkfilechooser.h> -#include <gtk/gtkfilechooserdialog.h> -#include <gtk/gtkstock.h> - -#include <libgnomeui/gnome-uidefs.h> -#include <glib/gi18n.h> - -#include "e-msg-composer-select-file.h" -#include <e-util/e-icon-factory.h> -#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 <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> #include <stdlib.h> +#include <string.h> +#include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> #include <unistd.h> -#include <errno.h> -#include <ctype.h> #include <glib.h> -#include <glib/gstdio.h> #include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> #include <gconf/gconf.h> #include <gconf/gconf-client.h> -#include <libgnome/gnome-exec.h> -#include <libgnome/gnome-help.h> #include <libgnome/gnome-url.h> -#include <glib/gi18n.h> -#include <libgnomeui/gnome-uidefs.h> -#include <libgnomeui/gnome-window-icon.h> - -#include <bonobo/bonobo-exception.h> -#include <bonobo/bonobo-moniker-util.h> -#include <bonobo/bonobo-stream-memory.h> -#include <bonobo/bonobo-ui-util.h> -#include <bonobo/bonobo-widget.h> - #include <libgnomevfs/gnome-vfs.h> #include <glade/glade.h> -#include <libedataserver/e-iconv.h> - #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 <e-util/e-icon-factory.h> -#include "Editor.h" -#include "listener.h" - #ifdef HAVE_XFREE #include <X11/XF86keysym.h> #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; i<recipients->len; 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; i<recipients->len; 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; -static char * -get_file_content (EMsgComposer *composer, const char *file_name, gboolean want_html, guint flags, gboolean warn) + 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;i<sizeof (emcab_popups)/sizeof (emcab_popups[0]);i++) + menus = g_slist_prepend (menus, &emcab_popups[i]); + + /** @HookPoint-EMPopup: Composer Attachment Bar Context Menu + * @Id: org.gnome.evolution.mail.composer.attachmentbar.popup + * @Class: org.gnome.evolution.mail.popup:1.0 + * @Target: EMPopupTargetAttachments + * + * This is the context menu on the composer attachment bar. + */ + emp = em_popup_new ("org.gnome.evolution.mail.composer.attachmentbar.popup"); + e_popup_add_items ((EPopup *)emp, menus, NULL, emcab_popups_free, bar); + t = em_popup_target_new_attachments (emp, attachments); + t->target.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 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,137 +1182,12 @@ e_msg_composer_get_sig_file_content (const char *sigfile, gboolean in_html) FALSE); } -static void -prepare_engine (EMsgComposer *composer) -{ - 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); - - if ((ev._major != CORBA_NO_EXCEPTION) || (p->eeditor_listener == NULL)) { - CORBA_Environment err_ev; - - 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) +static gchar * +encode_signature_name (const gchar *name) { - const char *s; - char *ename, *e; - int len = 0; + const gchar *s; + gchar *ename, *e; + gint len = 0; s = name; while (*s) { @@ -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) "</TD></TR></TABLE>", encoded_uid ? encoded_uid : "", format_html ? "" : "<PRE>\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 ? "" : "</PRE>\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 "<TABLE WIDTH=\"100%%\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TR><TD> </TD></TR></TABLE>%s", text); } } else { - body = g_strdup(text); + 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; - } - - 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,944 +1456,123 @@ 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; -} - -static void -autosave_manager_query_load_orphans (AutosaveManager *am, GtkWindow *parent) -{ - 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; - } - - 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; - - while (match != NULL) { - GSList *next = match->next; - char *filename = match->data; - EMsgComposer *composer; - - if (load) { - /* FIXME: composer is never used */ - composer = autosave_load_draft (filename); - } else { - g_unlink (filename); - } - - g_free (filename); - g_slist_free_1 (match); - match = next; - } -} - -static void -autosave_run_foreach_cb (gpointer key, gpointer value, gpointer data) -{ - EMsgComposer *composer = E_MSG_COMPOSER (value); - EMsgComposerPrivate *p = composer->priv; - - if (p->enable_autosave) - autosave_save_draft (composer); -} +/* Miscellaneous callbacks. */ static gint -autosave_run (gpointer data) -{ - AutosaveManager *am = data; - - g_hash_table_foreach (am->table, (GHFunc)autosave_run_foreach_cb, am); - - autosave_manager_stop (am); - autosave_manager_start (am); - - return FALSE; -} - -static gboolean -autosave_init_file (EMsgComposer *composer) -{ - 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); - return TRUE; - } - return FALSE; -} - -static void -autosave_manager_start (AutosaveManager *am) -{ - if (am->id == 0) - am->id = g_timeout_add (AUTOSAVE_INTERVAL, autosave_run, am); -} - -static void -autosave_manager_stop (AutosaveManager *am) -{ - if (am->id) { - g_source_remove (am->id); - am->id = 0; - } -} - -static AutosaveManager * -autosave_manager_new (void) -{ - AutosaveManager *am; - GHashTable *table; - - table = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); - - am = g_new (AutosaveManager, 1); - am->table = table; - am->id = 0; - am->ask = TRUE; - - return am; -} - -static void -autosave_manager_register (AutosaveManager *am, EMsgComposer *composer) -{ - char *key; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (composer != NULL); - - 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); -} - -static void -autosave_manager_unregister (AutosaveManager *am, EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - gchar *key; - - if (!p->autosave_file) - return; - - key = g_path_get_basename (p->autosave_file); - g_hash_table_remove (am->table, key); - g_free (key); - - /* 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; - - if (g_hash_table_size (am->table) == 0) - autosave_manager_stop (am); -} - -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)); -} - -/* Exit dialog. (Displays a "Save composition to 'Drafts' before exiting?" warning before actually exiting.) */ - -static void -do_exit (EMsgComposer *composer) -{ - const char *subject; - int button; - EMsgComposerPrivate *p = composer->priv; - - if (!e_msg_composer_is_dirty (composer) && !e_msg_composer_is_autosaved (composer)) { - gtk_widget_destroy (GTK_WIDGET (composer)); - return; - } - - gdk_window_raise (GTK_WIDGET (composer)->window); - - subject = e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (p->hdrs)); - - button = e_error_run((GtkWindow *)composer, "mail-composer:exit-unsaved", - subject && subject[0] ? subject : _("Untitled Message"), NULL); - - 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)); - break; - case GTK_RESPONSE_NO: - /* Don't save */ - gtk_widget_destroy (GTK_WIDGET (composer)); - break; - case GTK_RESPONSE_CANCEL: - break; - } -} - -/* Menu callbacks. */ -static void -file_open_response(EMsgComposer *composer, const char *name) -{ - load (composer, name); -} - -static void -menu_file_open_cb (BonoboUIComponent *uic, - void *data, - const char *path) +attachment_bar_button_press_event_cb (EAttachmentBar *attachment_bar, + GdkEventButton *event) { - EMsgComposer *composer = E_MSG_COMPOSER(data); - EMsgComposerPrivate *p = composer->priv; + GnomeIconList *icon_list; + gint icon_number; - /* NB: This function is never used anymore */ - - e_msg_composer_select_file(composer, &p->load, file_open_response, _("Open File"), FALSE); -} - -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; - - CORBA_exception_init (&ev); - - file_name = Bonobo_PersistFile_getCurrentFile (p->persist_file_interface, &ev); + if (event->button != 3) + return FALSE; - if (ev._major != CORBA_NO_EXCEPTION) { - saveas (composer); - } else { - save (composer, file_name); - CORBA_free (file_name); + 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); } - CORBA_exception_free (&ev); -} -static void -menu_file_save_as_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - saveas (E_MSG_COMPOSER(data)); -} + emcab_popup (attachment_bar, event, icon_number); -static void -menu_file_send_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - g_signal_emit (GTK_OBJECT (data), signals[SEND], 0); -} - -static void -menu_file_close_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - EMsgComposer *composer; - - composer = E_MSG_COMPOSER (data); - do_exit (composer); -} - -/* this is the callback for the help menu */ -static void -menu_help_cb (BonoboUIComponent *uic, - void *data, - const char *path) -{ - EMsgComposer *composer = (EMsgComposer *) data; - - e_msg_composer_show_help (composer); -} - - -static void -add_to_bar (EMsgComposer *composer, GSList *names, int is_inline) -{ - EMsgComposerPrivate *p = composer->priv; - - while (names) { - CamelURL *url; - - if (!(url = camel_url_new (names->data, NULL))) - continue; - - 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"); - } - - camel_url_free (url); - names = names->next; - } -} - -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); + return TRUE; } static void -menu_edit_cut_cb (BonoboUIComponent *uic, void *data, const char *path) +attachment_bar_changed_cb (EAttachmentBar *attachment_bar, + EMsgComposer *composer) { - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + GtkWidget *widget; + guint attachment_num; - g_return_if_fail (p->focused_entry != NULL); + editor = GTKHTML_EDITOR (composer); + attachment_num = e_attachment_bar_get_num_attachments (attachment_bar); - 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 (); - } -} + if (attachment_num > 0) { + gchar *markup; -static void -menu_edit_copy_cb (BonoboUIComponent *uic, void *data, const char *path) -{ - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + markup = g_strdup_printf ( + "<b>%d</b> %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); - g_return_if_fail (p->focused_entry != NULL); + gtk_widget_show (composer->priv->attachment_expander_icon); - if (GTK_IS_ENTRY (p->focused_entry)) { - gtk_editable_copy_clipboard (GTK_EDITABLE (p->focused_entry)); + widget = composer->priv->attachment_expander; + gtk_expander_set_expanded (GTK_EXPANDER (widget), TRUE); } else { - /* happy happy joy joy, an EEntry. */ - g_return_if_reached (); - } -} - -static void -menu_edit_paste_cb (BonoboUIComponent *uic, void *data, const char *path) -{ - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + widget = composer->priv->attachment_expander_num; + gtk_label_set_text (GTK_LABEL (widget), ""); - g_return_if_fail (p->focused_entry != NULL); + gtk_widget_hide (composer->priv->attachment_expander_icon); - 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 (); + widget = composer->priv->attachment_expander; + gtk_expander_set_expanded (GTK_EXPANDER (widget), FALSE); } -} -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); - } + /* Mark the editor as changed so it prompts about unsaved + changes on close. */ + gtkhtml_editor_set_changed (editor, TRUE); } -static void -menu_edit_select_all_cb (BonoboUIComponent *uic, void *data, const char *path) +static gint +attachment_bar_key_press_event_cb (EAttachmentBar *attachment_bar, + GdkEventKey *event) { - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (p->focused_entry != NULL); - - 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 (); + if (event->keyval == GDK_Delete) { + e_attachment_bar_remove_selected (attachment_bar); + return TRUE; } -} - -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); - - CORBA_exception_free (&ev); - /* printf ("delete all\n"); */ -} - -static void -menu_format_html_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) - -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_send_html (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_security_pgp_sign_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) - -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_pgp_sign (E_MSG_COMPOSER (composer), atoi (state)); -} - -static void -menu_security_pgp_encrypt_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) - -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_pgp_encrypt (E_MSG_COMPOSER (composer), atoi (state)); -} - -static void -menu_security_smime_sign_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) - -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_smime_sign (E_MSG_COMPOSER (composer), atoi (state)); -} - -static void -menu_security_smime_encrypt_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer composer) - -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_smime_encrypt (E_MSG_COMPOSER (composer), atoi (state)); -} - - -static void -menu_view_from_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_from (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_view_replyto_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_replyto (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_view_to_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_to (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_view_postto_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_postto (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_view_cc_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_cc (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_view_bcc_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_view_bcc (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_insert_receipt_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_request_receipt (E_MSG_COMPOSER (user_data), atoi (state)); -} -static void -menu_insert_priority_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - e_msg_composer_set_priority (E_MSG_COMPOSER (user_data), atoi (state)); -} - -static void -menu_changed_charset_cb (BonoboUIComponent *component, - const char *path, - Bonobo_UIComponent_EventType type, - const char *state, - gpointer user_data) -{ - if (type != Bonobo_UIComponent_STATE_CHANGED) - return; - - 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-")); - } + return FALSE; } - -static BonoboUIVerb verbs [] = { - - 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), - - BONOBO_UI_VERB ("FileSend", menu_file_send_cb), - - BONOBO_UI_VERB ("DeleteAll", menu_edit_delete_all_cb), - BONOBO_UI_VERB ("InsertXSendOptions", menu_send_options_cb), - - BONOBO_UI_VERB_END -}; - -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), - - 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), - - E_PIXMAP_END -}; - - -static void -setup_ui (EMsgComposer *composer) +static gboolean +attachment_bar_popup_menu_cb (EAttachmentBar *attachment_bar) { - 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 - - /* 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); + emcab_popup (attachment_bar, NULL, -1); - bonobo_ui_component_add_listener ( - p->uic, "SecuritySMimeSign", - menu_security_smime_sign_cb, composer); - - /* 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); - - bonobo_ui_component_add_listener ( - p->uic, "SecuritySMimeEncrypt", - menu_security_smime_encrypt_cb, composer); - - bonobo_ui_component_thaw (p->uic, NULL); - - /* Create the UIComponent for the non-control entries */ - - p->entry_uic = bonobo_ui_component_new_default (); - - /* 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); + return TRUE; } - -/* Miscellaneous callbacks. */ - static void -attachment_bar_changed_cb (EAttachmentBar *bar, - void *data) +attachment_expander_notify_cb (GtkExpander *expander, + GParamSpec *pspec, + EMsgComposer *composer) { - EMsgComposer *composer = E_MSG_COMPOSER (data); - EMsgComposerPrivate *p = composer->priv; + GtkLabel *label; + const gchar *text; - 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 ("<b>%d</b> Attachment", "<b>%d</b> 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); - } - - - /* 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); + label = GTK_LABEL (composer->priv->attachment_expander_label); /* Update the expander label */ - if (show) - gtk_label_set_text_with_mnemonic (GTK_LABEL (composer->priv->attachment_expander_label), - _("Hide _Attachment Bar")); + if (gtk_expander_get_expanded (expander)) + text = _("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; - - composer = E_MSG_COMPOSER (data); + text = _("Show _Attachment Bar"); - gtk_window_set_title (GTK_WINDOW (composer), subject[0] ? subject : _("Compose Message")); + gtk_label_set_text_with_mnemonic (label, text); } static void -hdrs_changed_cb (EMsgComposerHdrs *hdrs, - void *data) +msg_composer_subject_changed_cb (EMsgComposer *composer) { - EMsgComposer *composer; + EComposerHeaderTable *table; + const gchar *subject; + + table = e_msg_composer_get_header_table (composer); + subject = e_composer_header_table_get_subject (table); - composer = E_MSG_COMPOSER (data); + if (subject == NULL || *subject == '\0') + subject = _("Compose Message"); - /* Mark the composer as changed so it prompts about unsaved changes on close */ - e_msg_composer_set_changed (composer); + gtk_window_set_title (GTK_WINDOW (composer), subject); } enum { @@ -2547,20 +1581,21 @@ enum { }; static void -update_auto_recipients (EMsgComposerHdrs *hdrs, int mode, const char *auto_addrs) +update_auto_recipients (EComposerHeaderTable *table, + gint mode, + const gchar *auto_addrs) { EDestination *dest, **destv = NULL; CamelInternetAddress *iaddr; - GList *list, *tail, *node; - int i, n = 0; - - tail = list = NULL; + GList *list = NULL; + guint length; + gint i; 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; + const gchar *name, *addr; if (!camel_internet_address_get (iaddr, i, &name, &addr)) continue; @@ -2574,20 +1609,7 @@ update_auto_recipients (EMsgComposerHdrs *hdrs, int mode, const char *auto_addrs if (addr) e_destination_set_email (dest, addr); - node = g_list_alloc (); - node->data = dest; - node->next = NULL; - - if (tail) { - node->prev = tail; - tail->next = node; - } else { - node->prev = NULL; - list = node; - } - - tail = node; - n++; + list = g_list_prepend (list, dest); } } @@ -2596,10 +1618,10 @@ update_auto_recipients (EMsgComposerHdrs *hdrs, int mode, const char *auto_addrs switch (mode) { case UPDATE_AUTO_CC: - destv = e_msg_composer_hdrs_get_cc (hdrs); + destv = e_composer_header_table_get_destinations_cc (table); break; case UPDATE_AUTO_BCC: - destv = e_msg_composer_hdrs_get_bcc (hdrs); + destv = e_composer_header_table_get_destinations_bcc (table); break; default: g_return_if_reached (); @@ -2608,35 +1630,27 @@ update_auto_recipients (EMsgComposerHdrs *hdrs, int mode, const char *auto_addrs 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; - - if (tail) { - node->prev = tail; - tail->next = node; - } else { - node->prev = NULL; - list = node; - } - - tail = node; - n++; + dest = e_destination_copy (destv[i]); + list = g_list_prepend (list, dest); } } e_destination_freev (destv); } - destv = destination_list_to_vector_sized (list, n); + list = g_list_reverse (list); + + length = g_list_length (list); + destv = destination_list_to_vector_sized (list, length); + g_list_free (list); switch (mode) { case UPDATE_AUTO_CC: - e_msg_composer_hdrs_set_cc (hdrs, destv); + e_composer_header_table_set_destinations_cc (table, destv); break; case UPDATE_AUTO_BCC: - e_msg_composer_hdrs_set_bcc (hdrs, destv); + e_composer_header_table_set_destinations_bcc (table, destv); break; default: g_return_if_reached (); @@ -2646,217 +1660,121 @@ update_auto_recipients (EMsgComposerHdrs *hdrs, int mode, const char *auto_addrs } static void -from_changed_cb (EMsgComposerHdrs *hdrs, void *data) +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; - 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); - } - - set_editor_signature (composer); - e_msg_composer_show_sig_file (composer); -} - - -/* GObject methods. */ - -static void -composer_finalise (GObject *object) -{ - EMsgComposer *composer = E_MSG_COMPOSER (object); - EMsgComposerPrivate *p = composer->priv; - - if (p->extra_hdr_names) { - int i; - - 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); - } - - g_hash_table_destroy (p->inline_images); - g_hash_table_destroy (p->inline_images_by_url); + table = e_msg_composer_get_header_table (composer); + account = e_composer_header_table_get_account (table); - g_free (p->charset); - g_free (p->mime_type); - g_free (p->mime_body); + if (account == NULL) + goto exit; - if (p->redirect) - camel_object_unref (p->redirect); + 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); + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + active = account->smime_sign_default; + gtk_toggle_action_set_active (action, active); - g_free (p); + action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT)); + active = account->smime_encrypt_default; + gtk_toggle_action_set_active (action, active); - p = NULL; - composer->priv = NULL; + if (account->always_cc) + cc_addrs = account->cc_addrs; + if (account->always_bcc) + bcc_addrs = account->bcc_addrs; - if (G_OBJECT_CLASS (parent_class)->finalize != NULL) - (* G_OBJECT_CLASS (parent_class)->finalize) (object); -} + 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 -composer_dispose(GObject *object) -{ - /* 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)); +exit: + update_auto_recipients (table, UPDATE_AUTO_CC, cc_addrs); + update_auto_recipients (table, UPDATE_AUTO_BCC, bcc_addrs); - if (G_OBJECT_CLASS (parent_class)->dispose != NULL) - (* G_OBJECT_CLASS (parent_class)->dispose) (object); + e_msg_composer_show_sig_file (composer); } -/* GtkObject methods */ static void -destroy (GtkObject *object) +msg_composer_attach_message (EMsgComposer *composer, + CamelMimeMessage *msg) { - EMsgComposer *composer = (EMsgComposer *)object; + CamelMimePart *mime_part; + GString *description; + const gchar *subject; EMsgComposerPrivate *p = composer->priv; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - if (p->menu) { - e_menu_update_target((EMenu *)p->menu, NULL); - g_object_unref(p->menu); - p->menu = NULL; - } - - if (p->load) { - gtk_widget_destroy(p->load); - p->load = NULL; - } - - if (p->saveas) { - gtk_widget_destroy(p->saveas); - p->saveas = NULL; - } - - if (p->uic) { - bonobo_object_unref (BONOBO_OBJECT (p->uic)); - p->uic = NULL; - } - - if (p->entry_uic) { - bonobo_object_unref (BONOBO_OBJECT (p->entry_uic)); - p->entry_uic = NULL; - } - - /* 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; - } - 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); - } - - 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; - } - - 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; - } + mime_part = camel_mime_part_new (); + camel_mime_part_set_disposition (mime_part, "inline"); + subject = camel_mime_message_get_subject (msg); - 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; - } + 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); - CORBA_exception_free (&ev); + camel_medium_set_content_object ( + (CamelMedium *) mime_part, (CamelDataWrapper *) msg); + camel_mime_part_set_content_type (mime_part, "message/rfc822"); - if (p->eeditor_listener) { - bonobo_object_unref (p->eeditor_listener); - p->eeditor_listener = NULL; - } + e_attachment_bar_attach_mime_part ( + E_ATTACHMENT_BAR (p->attachment_bar), mime_part); - if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); + camel_object_unref (mime_part); } - -/* show the help menu item of the composer */ static void -e_msg_composer_show_help (EMsgComposer *composer) +msg_composer_update_preferences (GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + EMsgComposer *composer) { + GtkhtmlEditor *editor; + gboolean enable; GError *error = NULL; - gnome_help_display ( - "evolution.xml", "usage-composer", &error); - if (error != NULL) { + editor = GTKHTML_EDITOR (composer); + + 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_error_free (error); + g_clear_error (&error); } -} - - -/* GtkWidget methods. */ - -static int -delete_event (GtkWidget *widget, - GdkEventAny *event) -{ - do_exit (E_MSG_COMPOSER (widget)); - return TRUE; -} - -static void -attach_message(EMsgComposer *composer, CamelMimeMessage *msg) -{ - CamelMimePart *mime_part; - const char *subject; - EMsgComposerPrivate *p = composer->priv; - - 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); - - camel_mime_part_set_description(mime_part, desc); - g_free(desc); - } else - camel_mime_part_set_description(mime_part, _("Attached message")); + 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_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); + 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); + } } struct _drop_data { @@ -2878,33 +1796,22 @@ struct _drop_data { 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)); -} - -static gchar * -attachment_guess_mime_type (const char *file_name) -{ - GnomeVFSFileInfo *info; - GnomeVFSResult result; - gchar *type = NULL; + EAttachmentBar *attachment_bar; - 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); + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), 0); - return type; + attachment_bar = E_ATTACHMENT_BAR (composer->priv->attachment_bar); + return e_attachment_bar_get_download_count (attachment_bar); } static void -drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, GtkSelectionData *selection, guint info, guint time, gboolean html_dnd) +drop_action (EMsgComposer *composer, + GdkDragContext *context, + guint32 action, + GtkSelectionData *selection, + guint info, + guint time, + gboolean html_dnd) { char *tmp, *str, **urls; CamelMimePart *mime_part; @@ -2916,24 +1823,24 @@ drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, Gtk 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 */ + 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); + if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *)msg, stream) != -1) { + msg_composer_attach_message (composer, msg); success = TRUE; delete = action == GDK_ACTION_MOVE; } - camel_object_unref(msg); - camel_object_unref(stream); + camel_object_unref (msg); + camel_object_unref (stream); break; case DND_TYPE_NETSCAPE_URL: - d(printf ("dropping a _NETSCAPE_URL\n")); + 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); @@ -2945,7 +1852,7 @@ drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, Gtk success = TRUE; break; case DND_TYPE_TEXT_URI_LIST: - d(printf ("dropping a text/uri-list\n")); + 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); @@ -2964,7 +1871,7 @@ drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, Gtk 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)); + d (printf ("dropping a %s\n", content_type)); mime_part = camel_mime_part_new (); camel_mime_part_set_content (mime_part, (const gchar *)selection->data, selection->length, content_type); @@ -2985,7 +1892,7 @@ drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, Gtk /* NB: This all runs synchronously, could be very slow/hang/block the ui */ - uids = g_ptr_array_new(); + uids = g_ptr_array_new (); inptr = (char*)selection->data; inend = (char*)(selection->data + selection->length); @@ -2996,103 +1903,107 @@ drop_action(EMsgComposer *composer, GdkDragContext *context, guint32 action, Gtk inptr++; if (start > (char *)selection->data) - g_ptr_array_add(uids, g_strndup(start, inptr-start)); + g_ptr_array_add (uids, g_strndup (start, inptr-start)); inptr++; } if (uids->len > 0) { - folder = mail_tool_uri_to_folder((const gchar *)selection->data, 0, &ex); + 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); + msg = camel_folder_get_message (folder, uids->pdata[0], &ex); if (msg == NULL) goto fail; - attach_message(composer, msg); + msg_composer_attach_message (composer, msg); } else { - CamelMultipart *mp = camel_multipart_new(); + CamelMultipart *mp = camel_multipart_new (); char *desc; - camel_data_wrapper_set_mime_type((CamelDataWrapper *)mp, "multipart/digest"); - camel_multipart_set_boundary(mp, NULL); + camel_data_wrapper_set_mime_type ((CamelDataWrapper *)mp, "multipart/digest"); + camel_multipart_set_boundary (mp, NULL); for (i=0;i<uids->len;i++) { - msg = camel_folder_get_message(folder, uids->pdata[i], &ex); + 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); + 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); + camel_object_unref (mp); goto fail; } } - mime_part = camel_mime_part_new(); - camel_medium_set_content_object((CamelMedium *)mime_part, (CamelDataWrapper *)mp); + 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); + 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)) { + if (camel_exception_is_set (&ex)) { char *name; - 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_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); + camel_object_unref (folder); } else { - e_error_run((GtkWindow *)composer, "mail-composer:attach-nomessages", - (const gchar*)selection->data, camel_exception_get_description(&ex), NULL); + e_error_run ((GtkWindow *)composer, "mail-composer:attach-nomessages", + (const gchar*)selection->data, camel_exception_get_description (&ex), NULL); } - camel_exception_clear(&ex); + camel_exception_clear (&ex); } - g_ptr_array_free(uids, TRUE); + g_ptr_array_free (uids, TRUE); break; } default: - d(printf ("dropping an unknown\n")); + d (printf ("dropping an unknown\n")); break; } - if (e_attachment_bar_get_num_attachments(E_ATTACHMENT_BAR(p->attachment_bar))) - show_attachments (composer, TRUE); - - gtk_drag_finish(context, success, delete, time); + gtk_drag_finish (context, success, delete, time); } static void -drop_popup_copy(EPopup *ep, EPopupItem *item, void *data) +drop_popup_copy (EPopup *ep, EPopupItem *item, gpointer data) { struct _drop_data *m = data; - drop_action(m->composer, m->context, GDK_ACTION_COPY, m->selection, m->info, m->time, FALSE); + + drop_action ( + m->composer, m->context, GDK_ACTION_COPY, + m->selection, m->info, m->time, FALSE); } static void -drop_popup_move(EPopup *ep, EPopupItem *item, void *data) +drop_popup_move (EPopup *ep, EPopupItem *item, gpointer data) { struct _drop_data *m = data; - drop_action(m->composer, m->context, GDK_ACTION_MOVE, m->selection, m->info, m->time, FALSE); + + drop_action ( + m->composer, m->context, GDK_ACTION_MOVE, + m->selection, m->info, m->time, FALSE); } static void -drop_popup_cancel(EPopup *ep, EPopupItem *item, void *data) +drop_popup_cancel (EPopup *ep, EPopupItem *item, gpointer data) { struct _drop_data *m = data; - gtk_drag_finish(m->context, FALSE, FALSE, m->time); + + gtk_drag_finish (m->context, FALSE, FALSE, m->time); } static EPopupItem drop_popup_menu[] = { @@ -3103,910 +2014,901 @@ static EPopupItem drop_popup_menu[] = { }; static void -drop_popup_free(EPopup *ep, GSList *items, void *data) +drop_popup_free (EPopup *ep, GSList *items, gpointer data) { struct _drop_data *m = data; - g_slist_free(items); + 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); + g_object_unref (m->context); + g_object_unref (m->composer); + g_free (m->selection->data); + g_free (m->selection); + g_free (m); } static void -drag_data_received (GtkWidget *w, GdkDragContext *context, - int x, int y, GtkSelectionData *selection, - guint info, guint time, - EMsgComposer *composer) +msg_composer_notify_header_cb (EMsgComposer *composer) { - if (selection->data == NULL || selection->length == -1) - return; - - if (context->action == GDK_ACTION_ASK) { - EMPopup *emp; - GSList *menus = NULL; - GtkMenu *menu; - int i; - struct _drop_data *m; - - 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; + GtkhtmlEditor *editor; - emp = em_popup_new("org.gnome.evolution.mail.composer.popup.drop"); - for (i=0;i<sizeof(drop_popup_menu)/sizeof(drop_popup_menu[0]);i++) - menus = g_slist_append(menus, &drop_popup_menu[i]); - - 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, w != GTK_WIDGET (composer)); - } + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_set_changed (editor, TRUE); } +static GObject * +msg_composer_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + 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 gboolean -drag_motion(GObject *o, GdkDragContext *context, gint x, gint y, guint time, EMsgComposer *composer) +static void +msg_composer_dispose (GObject *object) { - GList *targets; - GdkDragAction action, actions = 0; + EMsgComposer *composer = E_MSG_COMPOSER (object); - for (targets = context->targets; targets; targets = targets->next) { - int i; + e_composer_autosave_unregister (composer); + e_composer_private_dispose (composer); - for (i=0;i<sizeof(drag_info)/sizeof(drag_info[0]);i++) - if (targets->data == (void *)drag_info[i].atom) - actions |= drag_info[i].actions; - } + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} - 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; +static void +msg_composer_finalize (GObject *object) +{ + EMsgComposer *composer = E_MSG_COMPOSER (object); - gdk_drag_status(context, action, time); + e_composer_private_finalize (composer); - return action != 0; + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); } static void -class_init (EMsgComposerClass *klass) +msg_composer_destroy (GtkObject *object) { - GtkObjectClass *object_class; - GtkWidgetClass *widget_class; - GObjectClass *gobject_class; - int i; + EMsgComposer *composer = E_MSG_COMPOSER (object); + + all_composers = g_slist_remove (all_composers, object); - for (i=0;i<sizeof(drag_info)/sizeof(drag_info[0]);i++) - drag_info[i].atom = gdk_atom_intern(drag_info[i].target, FALSE); - - gobject_class = G_OBJECT_CLASS(klass); - object_class = GTK_OBJECT_CLASS (klass); - widget_class = GTK_WIDGET_CLASS (klass); - - gobject_class->finalize = 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); +#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 + + if (composer->priv->address_dialog != NULL) { + gtk_widget_destroy (composer->priv->address_dialog); + composer->priv->address_dialog = NULL; + } + + if (composer->priv->notify_id) { + GConfClient *client; + + client = gconf_client_get_default (); + gconf_client_notify_remove (client, composer->priv->notify_id); + composer->priv->notify_id = 0; + g_object_unref (client); + } + + /* Chain up to parent's destroy() method. */ + GTK_OBJECT_CLASS (parent_class)->destroy (object); } static void -init (EMsgComposer *composer) +msg_composer_map (GtkWidget *widget) { - 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); + EComposerHeaderTable *table; + GtkWidget *input_widget; + const gchar *text; - inline_images_by_url = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) camel_object_unref); + /* Chain up to parent's map() method. */ + GTK_WIDGET_CLASS (parent_class)->map (widget); - p->uic = NULL; + table = e_msg_composer_get_header_table (E_MSG_COMPOSER (widget)); - p->hdrs = NULL; - p->extra_hdr_names = g_ptr_array_new (); - p->extra_hdr_values = g_ptr_array_new (); - - p->focused_entry = NULL; + /* 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; + } - p->eeditor = NULL; + /* 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; + } - p->address_dialog = NULL; + /* Jump to the editor as a last resort. */ + gtkhtml_editor_run_command (GTKHTML_EDITOR (widget), "grab-focus"); +} - p->attachment_bar = NULL; - p->attachment_scrolled_window = NULL; +static gint +msg_composer_delete_event (GtkWidget *widget, + GdkEventAny *event) +{ + /* This is needed for the ACTION macro. */ + EMsgComposer *composer = E_MSG_COMPOSER (widget); - p->persist_file_interface = CORBA_OBJECT_NIL; - p->persist_stream_interface = CORBA_OBJECT_NIL; + gtk_action_activate (ACTION (CLOSE)); - p->eeditor_engine = CORBA_OBJECT_NIL; - p->inline_images = inline_images; - p->inline_images_by_url = inline_images_by_url; - p->current_images = NULL; + return TRUE; +} - 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; +static gboolean +msg_composer_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + EMsgComposer *composer = E_MSG_COMPOSER (widget); + GtkWidget *input_widget; - p->has_changed = FALSE; - p->autosaved = FALSE; + input_widget = + e_composer_header_table_get_header ( + e_msg_composer_get_header_table (composer), + E_COMPOSER_HEADER_SUBJECT)->input_widget; - p->redirect = NULL; - p->send_invoked = FALSE; - p->charset = NULL; +#ifdef HAVE_XFREE + if (event->keyval == XF86XK_Send) { + g_signal_emit (G_OBJECT (composer), signals[SEND], 0); + return TRUE; + } +#endif /* HAVE_XFREE */ - p->enable_autosave = TRUE; - p->autosave_file = NULL; - p->autosave_fd = -1; + if (event->keyval == GDK_Escape) { + gtk_action_activate (ACTION (CLOSE)); + 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"); + if (event->keyval == GDK_Tab && gtk_widget_is_focus (input_widget)) { + gtkhtml_editor_run_command ( + GTKHTML_EDITOR (composer), "grab-focus"); + return TRUE; + } - composer->priv = p; + /* Chain up to parent's key_press_event() method. */ + return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event); } - -GtkType -e_msg_composer_get_type (void) +static gboolean +msg_composer_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) { - static GType type = 0; + GList *targets; + GdkDragAction actions = 0; + GdkDragAction chosen_action; - if (type == 0) { - static const GTypeInfo info = { - sizeof (EMsgComposerClass), - NULL, NULL, - (GClassInitFunc) class_init, - NULL, NULL, - sizeof (EMsgComposer), - 0, - (GInstanceInitFunc) init, - }; + targets = context->targets; + while (targets != NULL) { + gint ii; + + for (ii = 0; ii < G_N_ELEMENTS (drag_info); ii++) + if (targets->data == (gpointer) drag_info[ii].atom) + actions |= drag_info[ii].actions; - type = g_type_register_static (bonobo_window_get_type (), "EMsgComposer", &info, 0); + targets = g_list_next (targets); } - return type; + actions &= context->actions; + chosen_action = context->suggested_action; + + /* 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; + + gdk_drag_status (context, chosen_action, time); + + return (chosen_action != 0); } static void -e_msg_composer_load_config (EMsgComposer *composer, int visible_mask) +msg_composer_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time) { - GConfClient *gconf; - EMsgComposerPrivate *p = composer->priv; + EMsgComposer *composer; - gconf = gconf_client_get_default (); + /* Widget may be EMsgComposer or GtkHTML. */ + composer = E_MSG_COMPOSER (gtk_widget_get_toplevel (widget)); - 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; + if (selection->data == NULL) + return; - /* ditto for post-to */ - if (visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO) - p->view_postto = TRUE; - else - p->view_postto = FALSE; + if (selection->length == -1) + return; + + if (context->action == GDK_ACTION_ASK) { + EMPopup *emp; + GSList *menus = NULL; + GtkMenu *menu; + gint ii; + struct _drop_data *m; - /* we set these to false initially if we're posting */ - if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_CC)) - p->view_cc = FALSE; + 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; - if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_BCC)) - p->view_bcc = FALSE; + 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]); - g_object_unref (gconf); + 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)); + } } -static int -e_msg_composer_get_visible_flags (EMsgComposer *composer) +static void +msg_composer_cut_clipboard (GtkhtmlEditor *editor) { - int flags = 0; - EMsgComposerPrivate *p = composer->priv; + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; - 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; + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); - /* - * 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 (parent == composer->priv->header_table) { + gtk_editable_cut_clipboard (GTK_EDITABLE (widget)); + return; + } + /* Chain up to parent's cut_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->cut_clipboard (editor); +} static void -map_default_cb (EMsgComposer *composer, gpointer user_data) +msg_composer_copy_clipboard (GtkhtmlEditor *editor) { + EMsgComposer *composer; + GtkWidget *parent; GtkWidget *widget; - CORBA_Environment ev; - const char *subject; - const char *text; - EMsgComposerPrivate *p = composer->priv; - - /* If the 'To:' field is empty, focus it */ - - widget = e_msg_composer_hdrs_get_to_entry (E_MSG_COMPOSER_HDRS (p->hdrs)); - text = gtk_entry_get_text (GTK_ENTRY (widget)); - if (!text || text[0] == '\0') { - gtk_widget_grab_focus (widget); + 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_copy_clipboard (GTK_EDITABLE (widget)); return; } - /* If not, check the subject field */ + /* Chain up to parent's copy_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->copy_clipboard (editor); +} - subject = e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (p->hdrs)); +static void +msg_composer_paste_clipboard (GtkhtmlEditor *editor) +{ + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; + + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); - if (!subject || subject[0] == '\0') { - widget = e_msg_composer_hdrs_get_subject_entry (E_MSG_COMPOSER_HDRS (p->hdrs)); - gtk_widget_grab_focus (widget); + if (parent == composer->priv->header_table) { + gtk_editable_paste_clipboard (GTK_EDITABLE (widget)); 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); + /* Chain up to parent's paste_clipboard() method. */ + GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor); } static void -msg_composer_destroy_notify (void *data) +msg_composer_select_all (GtkhtmlEditor *editor) { - EMsgComposer *composer = E_MSG_COMPOSER (data); + EMsgComposer *composer; + GtkWidget *parent; + GtkWidget *widget; + + composer = E_MSG_COMPOSER (editor); + widget = gtk_window_get_focus (GTK_WINDOW (editor)); + parent = gtk_widget_get_parent (widget); - all_composers = g_slist_remove (all_composers, composer); + 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); } -static int -composer_key_pressed (EMsgComposer *composer, GdkEventKey *event, void *user_data) +static void +msg_composer_command_before (GtkhtmlEditor *editor, + const gchar *command) { - GtkWidget *widget; - EMsgComposerPrivate *p = composer->priv; - widget = e_msg_composer_hdrs_get_subject_entry (E_MSG_COMPOSER_HDRS (p->hdrs)); + EMsgComposer *composer; + const gchar *data; -#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 */ + composer = E_MSG_COMPOSER (editor); - 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; -} + if (strcmp (command, "insert-paragraph") != 0) + return; + if (composer->priv->in_signature_insert) + return; -/* 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 -}; + 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; + }; -/* 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; + 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"); + } +} - p->focused_entry = widget; +static void +msg_composer_command_after (GtkhtmlEditor *editor, + const gchar *command) +{ + EMsgComposer *composer; + const gchar *data; - 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); + composer = E_MSG_COMPOSER (editor); - bonobo_ui_component_add_verb_list_with_data (p->entry_uic, entry_verbs, composer); + if (strcmp (command, "insert-paragraph") != 0) + return; - bonobo_ui_component_freeze (p->entry_uic, NULL); + if (composer->priv->in_signature_insert) + return; - 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); + gtkhtml_editor_run_command (editor, "italic-off"); - bonobo_ui_component_thaw (p->entry_uic, 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"); - return FALSE; -} + data = gtkhtml_editor_get_paragraph_data (editor, "signature"); + if (data == NULL || *data != '1') + return; -static gboolean -composer_entry_focus_out_event_cb (GtkWidget *widget, GdkEventFocus *event, gpointer user_data) -{ - EMsgComposer *composer = user_data; - EMsgComposerPrivate *p = composer->priv; + /* Clear the signature. */ + if (gtkhtml_editor_is_paragraph_empty (editor)) + gtkhtml_editor_set_paragraph_data (editor, "signature" ,"0"); - g_return_val_if_fail (p->focused_entry == widget, FALSE); - p->focused_entry = NULL; + else if (gtkhtml_editor_is_previous_paragraph_empty (editor) && + gtkhtml_editor_run_command (editor, "cursor-backward")) { - bonobo_ui_component_unset_container (p->entry_uic, NULL); + gtkhtml_editor_set_paragraph_data (editor, "signature", "0"); + gtkhtml_editor_run_command (editor, "cursor-forward"); + } - return FALSE; + gtkhtml_editor_run_command (editor, "text-default-color"); + gtkhtml_editor_run_command (editor, "italic-off"); } -static void -setup_cut_copy_paste (EMsgComposer *composer) +static gchar * +msg_composer_image_uri (GtkhtmlEditor *editor, + const gchar *uri) { - EMsgComposerHdrs *hdrs; - EMsgComposerPrivate *p = composer->priv; - GtkWidget *entry; + EMsgComposer *composer; + GHashTable *hash_table; + CamelMimePart *part; + const gchar *cid; - hdrs = (EMsgComposerHdrs *) p->hdrs; + composer = E_MSG_COMPOSER (editor); - 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); + hash_table = composer->priv->inline_images_by_url; + part = g_hash_table_lookup (hash_table, uri); - 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 (part == NULL && g_str_has_prefix (uri, "file:")) + part = e_msg_composer_add_inline_image_from_file ( + composer, uri + 5); + + if (part == NULL && g_str_has_prefix (uri, "cid:")) { + hash_table = composer->priv->inline_images; + part = g_hash_table_lookup (hash_table, uri); + } - 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); + if (part == NULL) + return NULL; - 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); + composer->priv->current_images = + g_list_prepend (composer->priv->current_images, 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); + cid = camel_mime_part_get_content_id (part); + if (cid == NULL) + return NULL; - 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); + return g_strconcat ("cid:", cid, NULL); } static void -composer_settings_update (GConfClient *gconf, guint cnxn_id, GConfEntry *entry, gpointer data) +msg_composer_link_clicked (GtkhtmlEditor *editor, + const gchar *uri) { - gboolean bool; - EMsgComposer *composer = data; - EMsgComposerPrivate *p = composer->priv; + GError *error = NULL; - 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); + if (uri == NULL || *uri == '\0') + return; - 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); + if (g_ascii_strncasecmp (uri, "mailto:", 7) == 0) + return; - 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); -} + if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) + return; -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 (g_ascii_strncasecmp (uri, "cid:", 4) == 0) + return; - gtk_window_get_size (GTK_WINDOW (composer), &width, &height); + gnome_url_show (uri, &error); - 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); + if (error) { + g_warning ("%s", error->message); + g_error_free (error); } - g_object_unref (gconf); } -/* Callbacks. */ - static void -emcab_add(EPopup *ep, EPopupItem *item, void *data) +msg_composer_object_deleted (GtkhtmlEditor *editor) { - 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)); + const gchar *data; - e_msg_composer_select_file_attachments (toplevel, attachment_selector, add_to_bar); -} + if (!gtkhtml_editor_is_paragraph_empty (editor)) + return; -static void -emcab_properties(EPopup *ep, EPopupItem *item, void *data) -{ - EAttachmentBar *bar = data; + 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"); + } - e_attachment_bar_edit_selected(bar); + data = gtkhtml_editor_get_paragraph_data (editor, "signature"); + if (data != NULL && *data == '1') + gtkhtml_editor_set_paragraph_data (editor, "signature", "0"); } static void -emcab_remove(EPopup *ep, EPopupItem *item, void *data) +msg_composer_uri_requested (GtkhtmlEditor *editor, + const gchar *uri, + GtkHTMLStream *stream) { - EAttachmentBar *bar = data; + EMsgComposer *composer; + GHashTable *hash_table; + GByteArray *array; + CamelDataWrapper *wrapper; + CamelStream *camel_stream; + CamelMimePart *part; + GtkHTML *html; - e_attachment_bar_remove_selected(bar); -} + /* XXX It's unfortunate we have to expose GtkHTML structs here. + * Maybe we could rework this to use a GOutputStream. */ -/* 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 }, -}; + composer = E_MSG_COMPOSER (editor); + html = gtkhtml_editor_get_html (editor); -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; + hash_table = composer->priv->inline_images_by_url; + part = g_hash_table_lookup (hash_table, uri); - gdk_window_get_origin (((GtkWidget*) bar)->window, x, y); + if (part == NULL) { + hash_table = composer->priv->inline_images; + part = g_hash_table_lookup (hash_table, uri); + } - selection = gnome_icon_list_get_selection (icon_list); - if (selection == NULL) + if (part == NULL) { + gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR); return; + } - image = gnome_icon_list_get_icon_pixbuf_item (icon_list, GPOINTER_TO_INT(selection->data)); - if (image == NULL) - return; + 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); - /* 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; + gtk_html_write ( + gtkhtml_editor_get_html (editor), stream, + (gchar *) array->data, array->len); + + camel_object_unref (camel_stream); + + gtk_html_end (html, stream, GTK_HTML_STREAM_OK); } static void -emcab_popups_free(EPopup *ep, GSList *l, void *data) +msg_composer_class_init (EMsgComposerClass *class) { - g_slist_free(l); + 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); } -/* if id != -1, then use it as an index for target of the popup */ - static void -emcab_popup(EAttachmentBar *bar, GdkEventButton *event, int id) +msg_composer_init (EMsgComposer *composer) { - GSList *attachments = NULL, *menus = NULL; - int i; - EMPopup *emp; - EMPopupTargetAttachments *t; - GtkMenu *menu; + EComposerHeaderTable *table; +#if 0 /* GTKHTML-EDITOR */ + EMMenuTargetWidget *target; +#endif + GtkHTML *html; - attachments = e_attachment_bar_get_attachment(bar, id); + composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer); - for (i=0;i<sizeof(emcab_popups)/sizeof(emcab_popups[0]);i++) - menus = g_slist_prepend(menus, &emcab_popups[i]); + e_composer_private_init (composer); - /** @HookPoint-EMPopup: Composer Attachment Bar Context Menu - * @Id: org.gnome.evolution.mail.composer.attachmentbar.popup - * @Class: org.gnome.evolution.mail.popup:1.0 - * @Target: EMPopupTargetAttachments + 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); + + gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message")); + gtk_window_set_icon_name (GTK_WINDOW (composer), "mail-message-new"); + + /* Drag-and-Drop Support */ + + 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); + + g_signal_connect ( + html, "drag-data-received", + G_CALLBACK (msg_composer_drag_data_received), NULL); + + /* Plugin Support */ + +#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 * - * This is the context menu on the composer attachment bar. + * The main menu of the composer window. The widget of the + * target will point to the EMsgComposer object. */ - emp = em_popup_new("org.gnome.evolution.mail.composer.attachmentbar.popup"); - e_popup_add_items((EPopup *)emp, menus, NULL,emcab_popups_free, bar); - t = em_popup_target_new_attachments(emp, attachments); - t->target.widget = (GtkWidget *)bar; - menu = e_popup_create_menu_once((EPopup *)emp, (EPopupTarget *)t, 0); + 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); - 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); -} +#endif -static gboolean -popup_menu_event (GtkWidget *widget) -{ - emcab_popup((EAttachmentBar *)widget, NULL, -1); - return TRUE; -} + /* Configure Headers */ + e_composer_header_table_set_account_list ( + table, mail_config_get_accounts ()); + e_composer_header_table_set_signature_list ( + table, mail_config_get_signatures ()); -static int -button_press_event (GtkWidget *widget, GdkEventButton *event) -{ - EAttachmentBar *bar = (EAttachmentBar *)widget; - GnomeIconList *icon_list = GNOME_ICON_LIST(widget); - int icon_number; + 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 (event->button != 3) - return FALSE; + msg_composer_account_changed_cb (composer); - 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); - } + /* Attachment Bar */ - emcab_popup(bar, event, icon_number); + 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); - return TRUE; + e_composer_autosave_register (composer); + + /* Initialization may have tripped the "changed" state. */ + gtkhtml_editor_set_changed (GTKHTML_EDITOR (composer), FALSE); } -static gint -key_press_event(GtkWidget *widget, GdkEventKey *event) +GType +e_msg_composer_get_type (void) { - EAttachmentBar *bar = E_ATTACHMENT_BAR(widget); + static GType type = 0; - if (event->keyval == GDK_Delete) { - e_attachment_bar_remove_selected (bar); - return 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 */ + }; + + type = g_type_register_static ( + GTKHTML_TYPE_EDITOR, "EMsgComposer", &type_info, 0); } - return FALSE; + return type; } +/* Callbacks. */ + static EMsgComposer * -create_composer (int visible_mask) +create_composer (gint visible_mask) { 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; EMsgComposerPrivate *p; + EComposerHeaderTable *table; + GtkToggleAction *action; + gboolean active; - composer = g_object_new (E_TYPE_MSG_COMPOSER, "win_name", _("Compose Message"), NULL); + composer = g_object_new (E_TYPE_MSG_COMPOSER, NULL); + table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table); 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); + /* Configure View Menu */ - 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; - } + /* 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); - control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (p->eeditor)); - bonobo_control_frame_set_autoactivate (control_frame, TRUE); + /* 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); - /* 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); - - 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)); - return NULL; + /* 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); } - /* 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"); - - if (html_widget) { - g_signal_connect (html_widget, "drag_data_received", G_CALLBACK (drag_data_received), composer); + /* 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); } - 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; + action = GTK_TOGGLE_ACTION (ACTION (VIEW_SUBJECT)); + gtk_toggle_action_set_active (action, TRUE); 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 + * Returns: 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); + EMsgComposer *composer; + gint visible_mask; 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); - } + case E_MSG_COMPOSER_MAIL: + visible_mask = E_MSG_COMPOSER_VISIBLE_MASK_MAIL; + break; + + case E_MSG_COMPOSER_POST: + visible_mask = E_MSG_COMPOSER_VISIBLE_MASK_POST; + break; - if (new) { - e_msg_composer_set_send_html (new, send_html); - set_editor_signature (new); - set_editor_text (new, "", 0, TRUE, TRUE); + default: + visible_mask = + E_MSG_COMPOSER_VISIBLE_MASK_MAIL | + E_MSG_COMPOSER_VISIBLE_MASK_POST; + break; } - return new; + composer = create_composer (visible_mask); + + set_editor_text (composer, "", TRUE); + + return composer; } /** @@ -4014,7 +2916,7 @@ e_msg_composer_new_with_type (int type) * * Create a new message composer widget. * - * Return value: A pointer to the newly created widget + * Returns: A pointer to the newly created widget **/ EMsgComposer * e_msg_composer_new (void) @@ -4023,7 +2925,7 @@ e_msg_composer_new (void) } static gboolean -is_special_header (const char *hdr_name) +is_special_header (const gchar *hdr_name) { /* Note: a header is a "special header" if it has any meaning: 1. it's not a X-* header or @@ -4041,36 +2943,42 @@ is_special_header (const char *hdr_name) } static void -e_msg_composer_set_pending_body (EMsgComposer *composer, char *text, ssize_t len) +e_msg_composer_set_pending_body (EMsgComposer *composer, + gchar *text, + gssize length) { - char *old; + g_object_set_data_full ( + G_OBJECT (composer), "body:text", + text, (GDestroyNotify) g_free); - 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)); + g_object_set_data ( + G_OBJECT (composer), "body:length", + GSIZE_TO_POINTER (length)); } static void -e_msg_composer_flush_pending_body (EMsgComposer *composer, gboolean apply) +e_msg_composer_flush_pending_body (EMsgComposer *composer) { - char *body; - ssize_t len; + const gchar *body; + gpointer data; + gssize length; - 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); + 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); - g_object_set_data ((GObject *) composer, "body:text", NULL); - g_free (body); - } + if (body != NULL) + set_editor_text (composer, body, FALSE); + + g_object_set_data (G_OBJECT (composer), "body:text", NULL); } static void -add_attachments_handle_mime_part (EMsgComposer *composer, CamelMimePart *mime_part, - gboolean just_inlines, gboolean related, int depth) +add_attachments_handle_mime_part (EMsgComposer *composer, + CamelMimePart *mime_part, + gboolean just_inlines, + gboolean related, + gint depth) { CamelContentType *content_type; CamelDataWrapper *wrapper; @@ -4083,51 +2991,59 @@ add_attachments_handle_mime_part (EMsgComposer *composer, CamelMimePart *mime_pa if (CAMEL_IS_MULTIPART (wrapper)) { /* another layer of multipartness... */ - add_attachments_from_multipart (composer, (CamelMultipart *) wrapper, just_inlines, depth + 1); + 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); + 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 { - if (camel_content_type_is (content_type, "text", "*")) { - /* do nothing */ - } else { - e_msg_composer_attach (composer, mime_part); - } + e_msg_composer_attach (composer, mime_part); } } static void -add_attachments_from_multipart (EMsgComposer *composer, CamelMultipart *multipart, - gboolean just_inlines, int depth) +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; - int i, nparts; + gint i, nparts; - related = camel_content_type_is (CAMEL_DATA_WRAPPER (multipart)->mime_type, "multipart", "related"); + 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); + 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? */ + /* XXX 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); + 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. @@ -4139,7 +3055,8 @@ add_attachments_from_multipart (EMsgComposer *composer, CamelMultipart *multipar * specified in @composer. */ void -e_msg_composer_add_message_attachments (EMsgComposer *composer, CamelMimeMessage *message, +e_msg_composer_add_message_attachments (EMsgComposer *composer, + CamelMimeMessage *message, gboolean just_inlines) { CamelDataWrapper *wrapper; @@ -4148,24 +3065,28 @@ e_msg_composer_add_message_attachments (EMsgComposer *composer, CamelMimeMessage if (!CAMEL_IS_MULTIPART (wrapper)) return; - /* there must be attachments... */ - add_attachments_from_multipart (composer, (CamelMultipart *) wrapper, just_inlines, 0); + add_attachments_from_multipart ( + composer, (CamelMultipart *) wrapper, just_inlines, 0); } - static void -handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, int depth) +handle_multipart_signed (EMsgComposer *composer, + CamelMultipart *multipart, + gint depth) { CamelContentType *content_type; CamelDataWrapper *content; CamelMimePart *mime_part; + GtkToggleAction *action; /* FIXME: make sure this isn't an s/mime signed part?? */ - e_msg_composer_set_pgp_sign (composer, TRUE); + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, TRUE); - mime_part = camel_multipart_get_part (multipart, CAMEL_MULTIPART_SIGNED_CONTENT); + mime_part = camel_multipart_get_part ( + multipart, CAMEL_MULTIPART_SIGNED_CONTENT); - if (!mime_part) + if (mime_part != NULL) return; content_type = camel_mime_part_get_content_type (mime_part); @@ -4194,18 +3115,20 @@ handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, int handle_multipart (composer, multipart, depth); } } else if (camel_content_type_is (content_type, "text", "*")) { - ssize_t len; - char *html; + gchar *html; + gssize length; - html = em_utils_part_to_html (mime_part, &len, NULL); - e_msg_composer_set_pending_body (composer, html, len); + 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); } } static void -handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, int depth) +handle_multipart_encrypted (EMsgComposer *composer, + CamelMimePart *multipart, + gint depth) { CamelContentType *content_type; CamelCipherContext *cipher; @@ -4213,19 +3136,21 @@ handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, in CamelMimePart *mime_part; CamelException ex; CamelCipherValidity *valid; + GtkToggleAction *action; /* FIXME: make sure this is a PGP/MIME encrypted part?? */ - e_msg_composer_set_pgp_encrypt (composer, TRUE); + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + gtk_toggle_action_set_active (action, 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); + 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); + camel_cipher_validity_free (valid); content_type = camel_mime_part_get_content_type (mime_part); @@ -4253,11 +3178,11 @@ handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, in handle_multipart (composer, content_multipart, depth); } } else if (camel_content_type_is (content_type, "text", "*")) { - ssize_t len; - char *html; + gchar *html; + gssize length; - html = em_utils_part_to_html (mime_part, &len, NULL); - e_msg_composer_set_pending_body (composer, html, len); + 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); } @@ -4266,11 +3191,13 @@ handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, in } static void -handle_multipart_alternative (EMsgComposer *composer, CamelMultipart *multipart, int depth) +handle_multipart_alternative (EMsgComposer *composer, + CamelMultipart *multipart, + gint depth) { /* Find the text/html part and set the composer body to it's contents */ CamelMimePart *text_part = NULL; - int i, nparts; + gint i, nparts; nparts = camel_multipart_get_number (multipart); @@ -4317,18 +3244,20 @@ handle_multipart_alternative (EMsgComposer *composer, CamelMultipart *multipart, } if (text_part) { - ssize_t len; - char *html; + gchar *html; + gssize length; - html = em_utils_part_to_html(text_part, &len, NULL); - e_msg_composer_set_pending_body(composer, html, len); + html = em_utils_part_to_html (text_part, &length, NULL); + e_msg_composer_set_pending_body (composer, html, length); } } static void -handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, int depth) +handle_multipart (EMsgComposer *composer, + CamelMultipart *multipart, + gint depth) { - int i, nparts; + gint i, nparts; nparts = camel_multipart_get_number (multipart); @@ -4363,12 +3292,13 @@ handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, int depth) handle_multipart (composer, mp, depth + 1); } } else if (depth == 0 && i == 0) { - ssize_t len; - char *html; + gchar *html; + gssize length; - /* 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); + /* 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 */ @@ -4383,36 +3313,31 @@ handle_multipart (EMsgComposer *composer, CamelMultipart *multipart, int depth) static void set_signature_gui (EMsgComposer *composer) { - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; - EMsgComposerHdrs *hdrs; + GtkhtmlEditor *editor; + EComposerHeaderTable *table; ESignature *signature = NULL; + const gchar *data; + gchar *decoded; - 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); - } + editor = GTKHTML_EDITOR (composer); + table = e_msg_composer_get_header_table (composer); + + if (!gtkhtml_editor_search_by_data (editor, 1, "ClueFlow", "signature", "1")) + return; - e_msg_composer_hdrs_set_signature (hdrs, signature); + 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); } - CORBA_exception_free (&ev); -} + e_composer_header_table_set_signature (table, signature); +} /** * e_msg_composer_new_with_message: @@ -4422,44 +3347,51 @@ set_signature_gui (EMsgComposer *composer) * * Note: Designed to work only for messages constructed using Evolution. * - * Return value: A pointer to the newly created widget + * 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 char *format, *subject; + 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; - char *account_name; - EMsgComposer *new; + gchar *account_name; + EMsgComposer *composer; + EComposerHeaderTable *table; + GtkToggleAction *action; struct _camel_header_raw *xev; - int len, i; + gint 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))); + 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 (postto != NULL) + composer = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_POST); + else + composer = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL); + p = composer->priv; - if (!new) { - g_list_foreach(postto, (GFunc)g_free, NULL); - g_list_free(postto); + if (!composer) { + g_list_foreach (postto, (GFunc)g_free, NULL); + g_list_free (postto); return NULL; } + table = e_msg_composer_get_header_table (composer); + 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); + e_composer_header_table_set_post_to_list (table, postto); + g_list_foreach (postto, (GFunc)g_free, NULL); + g_list_free (postto); postto = NULL; } @@ -4469,9 +3401,9 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) account_name = g_strdup (account_name); g_strstrip (account_name); - if ((account = mail_config_get_account_by_uid(account_name)) == NULL) + if ((account = mail_config_get_account_by_uid (account_name)) == NULL) /* 'old' setting */ - account = mail_config_get_account_by_name(account_name); + account = mail_config_get_account_by_name (account_name); if (account) { g_free (account_name); account_name = g_strdup (account->name); @@ -4497,7 +3429,7 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) 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; + const gchar *name, *addr; if (!camel_internet_address_get (iaddr, i, &name, &addr)) continue; @@ -4512,7 +3444,7 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) 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; + const gchar *name, *addr; if (!camel_internet_address_get (iaddr, i, &name, &addr)) continue; @@ -4530,7 +3462,7 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) len = CAMEL_ADDRESS (to)->addresses->len; for (i = 0; i < len; i++) { - const char *name, *addr; + const gchar *name, *addr; if (camel_internet_address_get (to, i, &name, &addr)) { EDestination *dest = e_destination_new (); @@ -4544,7 +3476,7 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) len = CAMEL_ADDRESS (cc)->addresses->len; for (i = 0; i < len; i++) { - const char *name, *addr; + const gchar *name, *addr; if (camel_internet_address_get (cc, i, &name, &addr)) { EDestination *dest = e_destination_new (); @@ -4564,7 +3496,7 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) len = CAMEL_ADDRESS (bcc)->addresses->len; for (i = 0; i < len; i++) { - const char *name, *addr; + const gchar *name, *addr; if (camel_internet_address_get (bcc, i, &name, &addr)) { EDestination *dest = e_destination_new (); @@ -4589,7 +3521,11 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) subject = camel_mime_message_get_subject (message); - e_msg_composer_set_headers (new, account_name, Tov, Ccv, Bccv, subject); + 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 (account_name); @@ -4600,43 +3536,52 @@ e_msg_composer_new_with_message (CamelMimeMessage *message) /* Restore the format editing preference */ format = camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Format"); if (format) { - char **flags; + gchar **flags; - while (*format && camel_mime_is_lwsp(*format)) + while (*format && camel_mime_is_lwsp (*format)) format++; - flags = g_strsplit(format, ", ", 0); + 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); + 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); + 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); + 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); + 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")) { - e_msg_composer_set_priority (new, TRUE); + if (camel_medium_get_header (CAMEL_MEDIUM (message), "X-Priority")) { + action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE)); + gtk_toggle_action_set_active (action, TRUE); } /* set extra headers */ @@ -4662,50 +3607,57 @@ e_msg_composer_new_with_message (CamelMimeMessage *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); + 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 (new, CAMEL_MIME_PART (message), 0); + 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 (new, multipart, 0); + handle_multipart_alternative (composer, multipart, 0); } else { /* there must be attachments... */ - handle_multipart (new, multipart, 0); + handle_multipart (composer, multipart, 0); } } else { - ssize_t length; - char *html; + gchar *html; + gssize length; - html = em_utils_part_to_html((CamelMimePart *)message, &length, NULL); - e_msg_composer_set_pending_body(new, html, length); + html = em_utils_part_to_html ((CamelMimePart *)message, &length, NULL); + e_msg_composer_set_pending_body (composer, 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); + /* 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 (new); + set_signature_gui (composer); - return new; + return composer; } static void disable_editor (EMsgComposer *composer) { - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + GtkAction *action; - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "editable-off", &ev); - CORBA_exception_free (&ev); + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + editor = GTKHTML_EDITOR (composer); + + gtkhtml_editor_run_command (editor, "editable-off"); + + action = GTKHTML_EDITOR_ACTION_EDIT_MENU (composer); + gtk_action_set_sensitive (action, FALSE); - gtk_widget_set_sensitive (p->attachment_bar, FALSE); + action = GTKHTML_EDITOR_ACTION_FORMAT_MENU (composer); + gtk_action_set_sensitive (action, 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); + action = GTKHTML_EDITOR_ACTION_INSERT_MENU (composer); + gtk_action_set_sensitive (action, FALSE); + + gtk_widget_set_sensitive (composer->priv->attachment_bar, FALSE); } /** @@ -4714,39 +3666,76 @@ disable_editor (EMsgComposer *composer) * * Create a new message composer widget. * - * Return value: A pointer to the newly created widget + * Returns: A pointer to the newly created widget **/ EMsgComposer * -e_msg_composer_new_redirect (CamelMimeMessage *message, const char *resent_from) +e_msg_composer_new_redirect (CamelMimeMessage *message, + const gchar *resent_from) { EMsgComposer *composer; - EMsgComposerPrivate *p; - const char *subject; + EComposerHeaderTable *table; + const gchar *subject; g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); composer = e_msg_composer_new_with_message (message); - p = composer->priv; + table = e_msg_composer_get_header_table (composer); subject = camel_mime_message_get_subject (message); - p->redirect = message; + composer->priv->redirect = message; camel_object_ref (message); - e_msg_composer_set_headers (composer, resent_from, NULL, NULL, NULL, subject); + e_composer_header_table_set_account_name (table, resent_from); + e_composer_header_table_set_subject (table, subject); disable_editor (composer); return composer; } +/** + * e_msg_composer_send: + * @composer: an #EMsgComposer + * + * Send the message in @composer. + **/ +void +e_msg_composer_send (EMsgComposer *composer) +{ + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + g_signal_emit (composer, signals[SEND], 0); +} + +/** + * e_msg_composer_save_draft: + * @composer: an #EMsgComposer + * + * Save the message in @composer to the selected account's Drafts folder. + **/ +void +e_msg_composer_save_draft (EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + 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 char *recips) +add_recipients (GList *list, const gchar *recips) { CamelInternetAddress *cia; - const char *name, *addr; - int num, i; + const gchar *name, *addr; + gint num, i; cia = camel_internet_address_new (); num = camel_address_decode (CAMEL_ADDRESS (cia), recips); @@ -4765,19 +3754,21 @@ add_recipients (GList *list, const char *recips) } static void -handle_mailto (EMsgComposer *composer, const char *mailto) +handle_mailto (EMsgComposer *composer, const gchar *mailto) { - EMsgComposerHdrs *hdrs; EMsgComposerPrivate *priv = composer->priv; + EComposerHeaderTable *table; 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; + gchar *subject = NULL, *body = NULL; + gchar *header, *content, *buf; + gsize nread, nwritten; + const gchar *p; + gint len, clen; CamelURL *url; + table = e_msg_composer_get_header_table (composer); + buf = g_strdup (mailto); /* Parse recipients (everything after ':' until '?' or eos). */ @@ -4888,34 +3879,37 @@ handle_mailto (EMsgComposer *composer, const char *mailto) 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_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_destination_freev (tov); e_destination_freev (ccv); e_destination_freev (bccv); - if (subject) { - e_msg_composer_hdrs_set_subject (hdrs, subject); - g_free (subject); - } + e_composer_header_table_set_subject (table, subject); + g_free (subject); if (body) { - char *htmlbody; + gchar *htmlbody; htmlbody = camel_text_to_html (body, CAMEL_MIME_FILTER_TOHTML_PRE, 0); - set_editor_text (composer, htmlbody, -1, FALSE, FALSE); + set_editor_text (composer, htmlbody, FALSE); g_free (htmlbody); } } static void -handle_uri (EMsgComposer *composer, const char *uri, gboolean html_dnd) +handle_uri (EMsgComposer *composer, + const gchar *uri, + gboolean html_dnd) { EMsgComposerPrivate *p = composer->priv; + GtkhtmlEditor *editor; + gboolean html_content; + + editor = GTKHTML_EDITOR (composer); + html_content = gtkhtml_editor_get_html_mode (editor); if (!g_ascii_strncasecmp (uri, "mailto:", 7)) { handle_mailto (composer, uri); @@ -4927,11 +3921,11 @@ handle_uri (EMsgComposer *composer, const char *uri, gboolean html_dnd) return; if (!g_ascii_strcasecmp (url->protocol, "file")) { - type = attachment_guess_mime_type (uri); + type = e_msg_composer_guess_mime_type (uri); if (!type) return; - if (strncmp (type, "image", 5) || !html_dnd || (!p->send_html && !strncmp (type, "image", 5))) { + 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"); } @@ -4952,7 +3946,7 @@ handle_uri (EMsgComposer *composer, const char *uri, gboolean html_dnd) * defined by the provided URL. **/ EMsgComposer * -e_msg_composer_new_from_url (const char *url) +e_msg_composer_new_from_url (const gchar *url) { EMsgComposer *composer; @@ -4968,41 +3962,6 @@ e_msg_composer_new_from_url (const char *url) } /** - * 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 @@ -5010,12 +3969,14 @@ e_msg_composer_set_headers (EMsgComposer *composer, * Loads the given HTML text into the editor. **/ void -e_msg_composer_set_body_text (EMsgComposer *composer, const char *text, ssize_t len) +e_msg_composer_set_body_text (EMsgComposer *composer, + const gchar *text, + gssize 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'); + set_editor_text (composer, text, TRUE); } /** @@ -5024,18 +3985,23 @@ e_msg_composer_set_body_text (EMsgComposer *composer, const char *text, ssize_t * @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. + * 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_body (EMsgComposer *composer, const char *body, - const char *mime_type) +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)); - set_editor_text (composer, _("<b>(The composer contains a non-text message body, which cannot be edited.)</b>"), -1, FALSE, FALSE); - e_msg_composer_set_send_html (composer, FALSE); + table = e_msg_composer_get_header_table (composer); + + set_editor_text (composer, _("<b>(The composer contains a non-text message body, which cannot be edited.)</b>"), FALSE); + gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (composer), FALSE); disable_editor (composer); g_free (p->mime_body); @@ -5044,16 +4010,18 @@ e_msg_composer_set_body (EMsgComposer *composer, const char *body, 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); + account = e_composer_header_table_get_account (table); + if (account && account->pgp_no_imip_sign) { + GtkToggleAction *action; + + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, FALSE); + } } } - /** * e_msg_composer_add_header: * @composer: a composer object @@ -5065,8 +4033,9 @@ e_msg_composer_set_body (EMsgComposer *composer, const char *body, * the message it outputs. **/ void -e_msg_composer_add_header (EMsgComposer *composer, const char *name, - const char *value) +e_msg_composer_add_header (EMsgComposer *composer, + const gchar *name, + const gchar *value) { EMsgComposerPrivate *p = composer->priv; @@ -5077,6 +4046,7 @@ e_msg_composer_add_header (EMsgComposer *composer, const char *name, 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 @@ -5089,8 +4059,9 @@ e_msg_composer_add_header (EMsgComposer *composer, const char *name, * 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) +e_msg_composer_modify_header (EMsgComposer *composer, + const gchar *name, + const gchar *change_value) { g_return_if_fail (E_IS_MSG_COMPOSER (composer)); g_return_if_fail (name != NULL); @@ -5108,10 +4079,11 @@ e_msg_composer_modify_header (EMsgComposer *composer, const char *name, * Searches for the header and if found it removes it . **/ void -e_msg_composer_remove_header (EMsgComposer *composer, const char *name) +e_msg_composer_remove_header (EMsgComposer *composer, + const gchar *name) { EMsgComposerPrivate *p; - int i; + gint i; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); g_return_if_fail (name != NULL); @@ -5120,12 +4092,12 @@ e_msg_composer_remove_header (EMsgComposer *composer, const char *name) 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 @@ -5144,38 +4116,33 @@ e_msg_composer_attach (EMsgComposer *composer, CamelMimePart *attachment) bar = E_ATTACHMENT_BAR (p->attachment_bar); e_attachment_bar_attach_mime_part (bar, attachment); - - show_attachments (composer, TRUE); } - /** * e_msg_composer_add_inline_image_from_file: * @composer: a composer object - * @file_name: the name of the file containing the image + * @filename: the name of the file containing the image * - * This reads in the image in @file_name and adds it to @composer + * 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 newly-created CamelMimePart (which must be reffed + * Returns: 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) + const gchar *filename) { - char *mime_type, *cid, *url, *name, *dec_file_name; + gchar *mime_type, *cid, *url, *name, *dec_file_name; CamelStream *stream; CamelDataWrapper *wrapper; CamelMimePart *part; - struct stat statbuf; EMsgComposerPrivate *p = composer->priv; - dec_file_name = g_strdup(file_name); - camel_url_decode(dec_file_name); + dec_file_name = g_strdup (filename); + camel_url_decode (dec_file_name); - /* check for regular file */ - if (g_stat (dec_file_name, &statbuf) < 0 || !S_ISREG (statbuf.st_mode)) + if (!g_file_test (dec_file_name, G_FILE_TEST_IS_REGULAR)) return NULL; stream = camel_stream_fs_new_with_name (dec_file_name, O_RDONLY, 0); @@ -5187,7 +4154,9 @@ e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, camel_object_unref (CAMEL_OBJECT (stream)); 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"); + if (mime_type == NULL) + mime_type = g_strdup ("application/octet-stream"); + camel_data_wrapper_set_mime_type (wrapper, mime_type); g_free (mime_type); part = camel_mime_part_new (); @@ -5196,9 +4165,9 @@ e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, cid = camel_header_msgid_generate (); camel_mime_part_set_content_id (part, cid); - name = g_path_get_basename(dec_file_name); + name = g_path_get_basename (dec_file_name); camel_mime_part_set_filename (part, name); - g_free(name); + g_free (name); camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64); url = g_strdup_printf ("file:%s", dec_file_name); @@ -5208,12 +4177,11 @@ e_msg_composer_add_inline_image_from_file (EMsgComposer *composer, g_hash_table_insert (p->inline_images, url, part); g_free (cid); - g_free(dec_file_name); + g_free (dec_file_name); return part; } - /** * e_msg_composer_add_inline_image_from_mime_part: * @composer: a composer object @@ -5226,8 +4194,8 @@ void e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer, CamelMimePart *part) { - char *url; - const char *location, *cid; + gchar *url; + const gchar *location, *cid; EMsgComposerPrivate *p = composer->priv; cid = camel_mime_part_get_content_id (part); @@ -5241,13 +4209,12 @@ e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer, camel_object_ref (part); location = camel_mime_part_get_content_location (part); - if (location) { - g_hash_table_insert (p->inline_images_by_url, - g_strdup (location), part); - } + if (location != NULL) + g_hash_table_insert ( + p->inline_images_by_url, + g_strdup (location), part); } - /** * e_msg_composer_get_message: * @composer: A message composer widget @@ -5256,65 +4223,98 @@ e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer, * 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 + * Returns: A pointer to the new CamelMimeMessage object **/ CamelMimeMessage * -e_msg_composer_get_message (EMsgComposer *composer, gboolean save_html_object_data) +e_msg_composer_get_message (EMsgComposer *composer, + gboolean save_html_object_data) { + GtkhtmlEditor *editor; + gboolean html_content; + 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)) { + + 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; } } - return build_message (composer, save_html_object_data); + editor = GTKHTML_EDITOR (composer); + html_content = gtkhtml_editor_get_html_mode (editor); + + return build_message (composer, html_content, save_html_object_data); +} + +static gchar * +msg_composer_get_message_print_helper (EMsgComposer *composer, + gboolean html_content) +{ + GtkToggleAction *action; + GString *string; + + string = g_string_sized_new (128); + + if (html_content) + g_string_append (string, "text/html"); + else + g_string_append (string, "text/plain"); + + 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); + + 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); + + 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); } CamelMimeMessage * -e_msg_composer_get_message_print (EMsgComposer *composer, gboolean save_html_object_data) +e_msg_composer_get_message_print (EMsgComposer *composer, + gboolean save_html_object_data) { + GtkhtmlEditor *editor; EMsgComposer *temp_composer; CamelMimeMessage *msg; - GString *flags; + gboolean html_content; + gchar *flags; + + editor = GTKHTML_EDITOR (composer); + html_content = gtkhtml_editor_get_html_mode (editor); - msg = build_message (composer, save_html_object_data); + 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); - /* 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"); - - /* 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; + /* Override composer flags. */ + flags = msg_composer_get_message_print_helper ( + temp_composer, html_content); - msg = build_message (temp_composer, save_html_object_data); + msg = build_message (temp_composer, TRUE, save_html_object_data); if (msg != NULL) camel_medium_set_header ( - CAMEL_MEDIUM (msg), - "X-Evolution-Format", flags->str); + CAMEL_MEDIUM (msg), "X-Evolution-Format", flags); - e_msg_composer_delete (temp_composer); - g_string_free (flags, TRUE); + gtk_widget_destroy (GTK_WIDGET (temp_composer)); + g_free (flags); return msg; } @@ -5322,87 +4322,84 @@ e_msg_composer_get_message_print (EMsgComposer *composer, gboolean save_html_obj CamelMimeMessage * e_msg_composer_get_message_draft (EMsgComposer *composer) { + GtkhtmlEditor *editor; + EComposerHeaderTable *table; + GtkToggleAction *action; CamelMimeMessage *msg; EAccount *account; - gboolean old_flags[4]; - gboolean old_send_html; + gboolean html_content; + gboolean pgp_encrypt; + gboolean pgp_sign; + gboolean smime_encrypt; + gboolean smime_sign; 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); + editor = GTKHTML_EDITOR (composer); + table = e_msg_composer_get_header_table (composer); + html_content = gtkhtml_editor_get_html_mode (editor); + + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + pgp_sign = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); + + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + pgp_encrypt = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); + + action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN)); + smime_sign = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, FALSE); + + 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; - 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]; + action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN)); + gtk_toggle_action_set_active (action, pgp_sign); + + action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT)); + gtk_toggle_action_set_active (action, pgp_encrypt); + + 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); + + if (msg == NULL) + return NULL; /* Attach account info to the draft. */ - account = e_msg_composer_get_preferred_account (composer); + account = e_composer_header_table_get_account (table); if (account && account->name) - camel_medium_set_header (CAMEL_MEDIUM (msg), "X-Evolution-Account", account->uid); + camel_medium_set_header ( + CAMEL_MEDIUM (msg), + "X-Evolution-Account", account->uid); - /* 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"); + 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? */ - for (i=0;i<4;i++) { - if (old_flags[i]) - g_string_append_printf(flags, ", %s", emc_draft_format_names[i]); - } + 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"); - camel_medium_set_header (CAMEL_MEDIUM (msg), "X-Evolution-Format", flags->str); - g_string_free(flags, TRUE); + camel_medium_set_header ( + CAMEL_MEDIUM (msg), "X-Evolution-Format", flags->str); + g_string_free (flags, TRUE); return msg; } - -static void -delete_old_signature (EMsgComposer *composer) -{ - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; - - /* 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); -} - - /** * e_msg_composer_show_sig: * @composer: A message composer widget @@ -5412,789 +4409,123 @@ delete_old_signature (EMsgComposer *composer) void e_msg_composer_show_sig_file (EMsgComposer *composer) { - CORBA_Environment ev; - char *html; - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + GtkhtmlEditor *editor; + GtkHTML *html; + gchar *html_text; - /* 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"); */ -} - - -/** - * e_msg_composer_set_send_html: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "Send HTML" flag set - * - * Set the status of the "Send HTML" toggle item. The user can override it. - **/ -void -e_msg_composer_set_send_html (EMsgComposer *composer, - gboolean send_html) -{ - CORBA_Environment ev; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if (p->send_html && send_html) - return; - - if (!p->send_html && !send_html) - return; - - p->send_html = send_html; - - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "block-redraw", &ev); - CORBA_exception_free (&ev); - - bonobo_ui_component_set_prop (p->uic, "/commands/FormatHtml", - "state", p->send_html ? "1" : "0", NULL); - - /* 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); - - - CORBA_exception_init (&ev); - GNOME_GtkHTML_Editor_Engine_runCommand (p->eeditor_engine, "unblock-redraw", &ev); - CORBA_exception_free (&ev); -} - - -/** - * 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; - - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->send_html; -} - - -/** - * 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; - - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - - hdrs = E_MSG_COMPOSER_HDRS (p->hdrs); - - return e_msg_composer_hdrs_get_from_account (hdrs); -} - - -/** - * 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)); - - if (p->pgp_sign && pgp_sign) - return; - if (!p->pgp_sign && !pgp_sign) - return; - - p->pgp_sign = pgp_sign; - e_msg_composer_set_changed (composer); - - bonobo_ui_component_set_prop (p->uic, "/commands/SecurityPGPSign", - "state", p->pgp_sign ? "1" : "0", NULL); -} - - -/** - * 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); - - return p->pgp_sign; -} - - -/** - * 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) -{ - 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; - - p->pgp_encrypt = pgp_encrypt; - e_msg_composer_set_changed (composer); - - bonobo_ui_component_set_prop (p->uic, "/commands/SecurityPGPEncrypt", - "state", p->pgp_encrypt ? "1" : "0", NULL); -} - + editor = GTKHTML_EDITOR (composer); + html = gtkhtml_editor_get_html (editor); -/** - * 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); - - return p->pgp_encrypt; -} - - -/** - * e_msg_composer_set_smime_sign: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "S/MIME Sign" flag set - * - * Set the status of the "S/MIME Sign" toggle item. The user can override it. - **/ -void -e_msg_composer_set_smime_sign (EMsgComposer *composer, gboolean smime_sign) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if (p->smime_sign && smime_sign) - return; - if (!p->smime_sign && !smime_sign) + if (composer->priv->redirect) return; - p->smime_sign = smime_sign; - e_msg_composer_set_changed (composer); + composer->priv->in_signature_insert = TRUE; - bonobo_ui_component_set_prop (p->uic, "/commands/SecuritySMimeSign", - "state", p->smime_sign ? "1" : "0", NULL); -} + gtkhtml_editor_freeze (editor); + gtkhtml_editor_run_command (editor, "cursor-position-save"); + gtkhtml_editor_undo_begin (editor, "Set signature", "Reset signature"); - -/** - * e_msg_composer_get_smime_sign: - * @composer: A message composer widget - * - * Get the status of the "S/MIME Sign" flag. - * - * Return value: The status of the "S/MIME Sign" flag. - **/ -gboolean -e_msg_composer_get_smime_sign (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->smime_sign; -} - - -/** - * e_msg_composer_set_smime_encrypt: - * @composer: A message composer widget - * @send_html: Whether the composer should have the "S/MIME Encrypt" flag set - * - * Set the status of the "S/MIME Encrypt" toggle item. The user can override it. - **/ -void -e_msg_composer_set_smime_encrypt (EMsgComposer *composer, gboolean smime_encrypt) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if (p->smime_encrypt && smime_encrypt) - return; - if (!p->smime_encrypt && !smime_encrypt) - return; - - p->smime_encrypt = smime_encrypt; - e_msg_composer_set_changed (composer); - - bonobo_ui_component_set_prop (p->uic, "/commands/SecuritySMimeEncrypt", - "state", p->smime_encrypt ? "1" : "0", NULL); -} - - -/** - * 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); - - return p->smime_encrypt; -} - - -/** - * e_msg_composer_get_view_from: - * @composer: A message composer widget - * - * Get the status of the "View From header" flag. - * - * Return value: The status of the "View From header" flag. - **/ -gboolean -e_msg_composer_get_view_from (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->view_from; -} - - -/** - * e_msg_composer_set_view_from: - * @composer: A message composer widget - * @state: whether to show or hide the From selector - * - * Controls the state of the From selector - */ -void -e_msg_composer_set_view_from (EMsgComposer *composer, gboolean view_from) -{ - EMsgComposerPrivate *p = composer->priv; - GConfClient *gconf; - GError *error = NULL; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - 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); + /* 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"); } - - 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_get_view_replyto: - * @composer: A message composer widget - * - * Get the status of the "View Reply-To header" flag. - * - * Return value: The status of the "View Reply-To header" flag. - **/ -gboolean -e_msg_composer_get_view_replyto (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->view_replyto; -} - - -/** - * e_msg_composer_set_view_replyto: - * @composer: A message composer widget - * @state: whether to show or hide the Reply-To selector - * - * Controls the state of the Reply-To selector - */ -void -e_msg_composer_set_view_replyto (EMsgComposer *composer, gboolean view_replyto) -{ - EMsgComposerPrivate *p = composer->priv; - GConfClient *gconf; - GError *error = NULL; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + gtkhtml_editor_run_command (editor, "unblock-selection"); - 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); + 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); } - 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_get_view_to: - * @composer: A message composer widget - * - * Get the status of the "View To header" flag. - * - * Return value: The status of the "View To header" flag. - **/ -gboolean -e_msg_composer_get_view_to (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->view_to; -} - - -/** - * 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)); - - if ((p->view_to && view_to) || - (!p->view_to && !view_to)) - return; - - p->view_to = view_to; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewTo", - "state", p->view_to ? "1" : "0", NULL); - - 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); - } - - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} - - - -/** - * 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); - - return p->view_postto; -} - - -/** - * e_msg_composer_set_view_postto: - * @composer: A message composer widget - * @state: whether to show or hide the PostTo selector - * - * Controls the state of the PostTo selector - */ -void -e_msg_composer_set_view_postto (EMsgComposer *composer, gboolean view_postto) -{ - GConfClient *gconf; - GError *error = NULL; - - 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); - } - - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} - - - -/** - * e_msg_composer_get_view_cc: - * @composer: A message composer widget - * - * Get the status of the "View CC header" flag. - * - * Return value: The status of the "View CC header" flag. - **/ -gboolean -e_msg_composer_get_view_cc (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->view_cc; -} - - -/** - * 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) -{ - GConfClient *gconf; - GError *error = NULL; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if ((p->view_cc && view_cc) || - (!p->view_cc && !view_cc)) - return; - - p->view_cc = view_cc; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewCC", - "state", p->view_cc ? "1" : "0", NULL); - - 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); - } - - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} - - - -/** - * 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) -{ - EMsgComposerPrivate *p = composer->priv; - - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->view_bcc; -} - - -/** - * 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) -{ - GConfClient *gconf; - GError *error = NULL; - EMsgComposerPrivate *p = composer->priv; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - - if ((p->view_bcc && view_bcc) || - (!p->view_bcc && !view_bcc)) - return; - - p->view_bcc = view_bcc; - bonobo_ui_component_set_prop (p->uic, "/commands/ViewBCC", - "state", p->view_bcc ? "1" : "0", NULL); - - 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); - } - - e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (p->hdrs), - e_msg_composer_get_visible_flags (composer)); -} - - - -/** - * 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); - - return p->request_receipt; -} - -/** - * 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)); + gtkhtml_editor_undo_end (editor); + gtkhtml_editor_run_command (editor, "cursor-position-restore"); + gtkhtml_editor_thaw (editor); - if ((p->request_receipt && request_receipt) || - (!p->request_receipt && !request_receipt)) - return; - - p->request_receipt = request_receipt; - bonobo_ui_component_set_prop (p->uic, "/commands/RequestReceipt", - "state", p->request_receipt ? "1" : "0", NULL); + composer->priv->in_signature_insert = FALSE; } - -/** - * 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) +CamelInternetAddress * +e_msg_composer_get_from (EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); - - return p->set_priority; -} - + CamelInternetAddress *address; + EComposerHeaderTable *table; + EAccount *account; -/** - * e_msg_composer_set_priority: - * @composer: A message composer widget - * @state: whether to set priority or not - * - * If set, a message is sent with a high priority - */ -void -e_msg_composer_set_priority (EMsgComposer *composer, gboolean set_priority) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - if ((p->set_priority && set_priority) || - (!p->set_priority && !set_priority)) - return; + table = e_msg_composer_get_header_table (composer); - p->set_priority= set_priority; - bonobo_ui_component_set_prop (p->uic, "/commands/SetPriority", - "state", p->set_priority ? "1" : "0", NULL); -} + account = e_composer_header_table_get_account (table); + if (account == NULL) + return NULL; -EDestination ** -e_msg_composer_get_recipients (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), 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_recipients (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; + return address; } -EDestination ** -e_msg_composer_get_to (EMsgComposer *composer) +CamelInternetAddress * +e_msg_composer_get_reply_to (EMsgComposer *composer) { - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL); - - return p->hdrs ? e_msg_composer_hdrs_get_to (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; -} + CamelInternetAddress *address; + EComposerHeaderTable *table; + const gchar *reply_to; -EDestination ** -e_msg_composer_get_cc (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; 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); - - return p->hdrs ? e_msg_composer_hdrs_get_bcc (E_MSG_COMPOSER_HDRS (p->hdrs)) : NULL; -} + reply_to = e_composer_header_table_get_reply_to (table); + if (reply_to == NULL || *reply_to == '\0') + return NULL; -const char * -e_msg_composer_get_subject (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), 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 p->hdrs ? e_msg_composer_hdrs_get_subject (E_MSG_COMPOSER_HDRS (p->hdrs)) : 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; -} + GtkhtmlEditor *editor; + GByteArray *array; + gchar *text; + gsize length; -EAttachmentBar* -e_msg_composer_get_attachment_bar (EMsgComposer *composer) -{ - EMsgComposerPrivate *p = composer->priv; - - 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; + return (all_composers == NULL); } -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; - } -} - -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); + /* Ask if the user wants to recover the orphaned files. */ + response = e_error_run ( + parent, "mail-composer:recover-autosave", NULL); - if (GNOME_GtkHTML_Editor_Engine_isParagraphEmpty (p->eeditor_engine, &ev)) { - CORBA_char *orig; - CORBA_char *signature; - - 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 <bonobo/bonobo-window.h> -#include <bonobo/bonobo-ui-component.h> - -#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 <camel/camel-internet-address.h> +#include <camel/camel-mime-message.h> +#include <libedataserver/e-account.h> +#include <libebook/e-destination.h> +#include <gtkhtml-editor.h> + +#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 <danw@ximian.com> - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <gtk/gtksignal.h> -#include <bonobo/bonobo-item-handler.h> -#include <bonobo/bonobo-generic-factory.h> -#include <e-util/e-util.h> -#include <misc/e-gui-utils.h> -#include "evolution-composer.h" -#include "mail/mail-config.h" -#include "libedataserver/e-account-list.h" -#include <camel/camel-mime-filter-tohtml.h> -#include <camel/camel-stream-mem.h> - -#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 <bonobo/bonobo-object.h> - -#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 @@ +<ui> + <menubar name='main-menu'> + <placeholder name='pre-edit-menu'> + <menu action='file-menu'> + <menuitem action='send'/> + <separator/> + <menuitem action='save'/> + <menuitem action='save-as'/> + <menuitem action='save-draft'/> + <separator/> + <menuitem action='print-preview'/> + <menuitem action='print'/> + <separator/> + <menuitem action='close'/> + </menu> + </placeholder> + <menu action='edit-menu'> + <placeholder name='pre-spell-check'> + <menu action='charset-menu'/> + <separator/> + </placeholder> + </menu> + <placeholder name='pre-insert-menu'> + <menu action='view-menu'> + <menuitem action='view-from'/> + <menuitem action='view-to'/> + <menuitem action='view-post-to'/> + <menuitem action='view-reply-to'/> + <menuitem action='view-cc'/> + <menuitem action='view-bcc'/> + <menuitem action='view-subject'/> + </menu> + </placeholder> + <menu action='insert-menu'> + <placeholder name='insert-menu-top'> + <menuitem action='attach'/> + <separator/> + <menuitem action='send-options'/> + <separator/> + </placeholder> + <separator/> + <menuitem action='request-read-receipt'/> + <menuitem action='prioritize-message'/> + </menu> + <menu action='security-menu'> + <menuitem action='pgp-sign'/> + <menuitem action='pgp-encrypt'/> + <menuitem action='smime-sign'/> + <menuitem action='smime-encrypt'/> + </menu> + </menubar> + <toolbar name='main-toolbar'> + <placeholder name='pre-main-toolbar'> + <toolitem action='send'/> + <separator/> + <toolitem action='save-draft'/> + <toolitem action='attach'/> + <separator/> + </placeholder> + </toolbar> +</ui> 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 <jorn@openedhand.com> + * + * 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 <config.h> + +#include <glib/gi18n-lib.h> +#include <gtk/gtkmessagedialog.h> +#include <string.h> + +#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 <jorn@openedhand.com> + * + * 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 <gconf/gconf-client.h> +#include <gtk/gtkwindow.h> +#include <gtk/gtkliststore.h> + +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 <rodo@ximian.com> - - 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 <config.h> -#endif - -#include <string.h> -#include <bonobo/bonobo-arg.h> -#include <bonobo/bonobo-object.h> -#include <bonobo/bonobo-stream-client.h> -#include <camel/camel-stream-mem.h> - -#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 <rodo@ximian.com> - - 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 <bonobo/bonobo-object.h> -#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_ */ |