aboutsummaryrefslogtreecommitdiffstats
path: root/composer
diff options
context:
space:
mode:
Diffstat (limited to 'composer')
-rw-r--r--composer/.cvsignore6
-rw-r--r--composer/ChangeLog20
-rw-r--r--composer/Makefile.am44
-rw-r--r--composer/e-msg-composer-address-dialog.c660
-rw-r--r--composer/e-msg-composer-address-dialog.glade575
-rw-r--r--composer/e-msg-composer-address-dialog.h75
-rw-r--r--composer/e-msg-composer-address-entry.c175
-rw-r--r--composer/e-msg-composer-address-entry.h63
-rw-r--r--composer/e-msg-composer-attachment-bar.c665
-rw-r--r--composer/e-msg-composer-attachment-bar.h74
-rw-r--r--composer/e-msg-composer-attachment.c480
-rw-r--r--composer/e-msg-composer-attachment.glade290
-rw-r--r--composer/e-msg-composer-attachment.h72
-rw-r--r--composer/e-msg-composer-hdrs.c355
-rw-r--r--composer/e-msg-composer-hdrs.h80
-rw-r--r--composer/e-msg-composer.c564
-rw-r--r--composer/e-msg-composer.glade587
-rw-r--r--composer/e-msg-composer.h87
-rw-r--r--composer/main.c52
19 files changed, 4924 insertions, 0 deletions
diff --git a/composer/.cvsignore b/composer/.cvsignore
new file mode 100644
index 0000000000..09980ae6ba
--- /dev/null
+++ b/composer/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.lo
+*.la
diff --git a/composer/ChangeLog b/composer/ChangeLog
new file mode 100644
index 0000000000..744d633eae
--- /dev/null
+++ b/composer/ChangeLog
@@ -0,0 +1,20 @@
+1999-11-17 Ettore Perazzoli <ettore@gnu.org>
+
+ * Makefile.am: New Makefile to compile the message composer
+ executable.
+
+ * main.c: New file.
+
+ * e-msg-composer-hdrs.c (e_msg_composer_hdrs_to_message): Use
+ `CAMEL_RECIPIENT*' macros instead of the old `RECIPIENT*' ones
+ that do not exist anymore.
+
+ * e-msg-composer-address-dialog.c
+ (e_msg_composer_address_dialog_construct): Use `E_GLADEDIR'
+ instead of `E_GUIDIR'.
+ * e-msg-composer-attachment.c (e_msg_composer_attachment_edit):
+ Likewise.
+ * e-msg-composer.c (e_msg_composer_construct): Likewise.
+
+(See `$(top_srcdir)/widgets/ChangeLog' for previous changes to the
+message composer.)
diff --git a/composer/Makefile.am b/composer/Makefile.am
new file mode 100644
index 0000000000..fe924bc9e3
--- /dev/null
+++ b/composer/Makefile.am
@@ -0,0 +1,44 @@
+gladedir = $(datadir)/evolution/glade
+
+glade_DATA = \
+ e-msg-composer-address-dialog.glade \
+ e-msg-composer-attachment.glade \
+ e-msg-composer.glade
+
+INCLUDES = \
+ -I$(top_srcdir)/camel \
+ -I$(top_builddir)/camel \
+ $(GNOME_INCLUDEDIR)
+
+CPPFLAGS = \
+ -DE_GLADEDIR=\"$(gladedir)\"
+
+bin_PROGRAMS = \
+ evolution-msg-composer
+
+evolution_msg_composer_SOURCES = \
+ e-msg-composer-address-dialog.c \
+ e-msg-composer-address-dialog.h \
+ e-msg-composer-address-entry.c \
+ e-msg-composer-address-entry.h \
+ e-msg-composer-attachment-bar.c \
+ e-msg-composer-attachment-bar.h \
+ e-msg-composer-attachment.c \
+ e-msg-composer-attachment.h \
+ e-msg-composer-hdrs.c \
+ e-msg-composer-hdrs.h \
+ e-msg-composer.c \
+ e-msg-composer.h \
+ main.c
+
+evolution_msg_composer_LDADD = \
+ $(top_builddir)/camel/libcamel.la \
+ $(GNOME_LIBDIR) \
+ $(GNOMEUI_LIBS) \
+ $(INTLLIBS) \
+ $(PTHREAD_LIB) \
+ $(EXTRA_GNOME_LIBS)
+
+
+EXTRA_DIST = \
+ ChangeLog
diff --git a/composer/e-msg-composer-address-dialog.c b/composer/e-msg-composer-address-dialog.c
new file mode 100644
index 0000000000..42a594756d
--- /dev/null
+++ b/composer/e-msg-composer-address-dialog.c
@@ -0,0 +1,660 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-address-dialog.c
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#include <gnome.h>
+#include "e-msg-composer-address-dialog.h"
+
+
+enum {
+ APPLY,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GnomeDialogClass *parent_class = NULL;
+
+
+/* This function should load the addresses we know of into the dialog. We
+ don't have a precise setup for the addressbook yet, so we will just put some
+ fake entries in. */
+static void
+load_addresses (EMsgComposerAddressDialog *dialog)
+{
+ gchar *text[][3] = {
+ { "Bertrand Guiheneuf", "Bertrand.Guiheneuf@aful.org", NULL },
+ { "Ettore Perazzoli", "ettore@gnu.org", NULL },
+ { "Miguel de Icaza", "miguel@gnu.org", NULL },
+ { "Nat Friedman", "nat@nat.org", NULL },
+ { NULL, NULL, NULL }
+ };
+ GtkCList *clist;
+ guint i;
+
+ clist = GTK_CLIST (glade_xml_get_widget (dialog->gui, "address_clist"));
+
+ for (i = 0; text[i][0] != NULL; i++)
+ gtk_clist_append (clist, text[i]);
+}
+
+/* Combine name and email into an address, e.g. "Ettore Perazzoli
+ <ettore@gnu.org>". FIXME FIXME FIXME this does not handle quoting (commas
+ will cause troubles), but it should. */
+static gchar *
+make_full_address (const gchar *name,
+ const gchar *email)
+{
+ return g_strconcat (name, " <", email, ">", NULL);
+}
+
+/* This loads the selected address in the address GtkCList into the requested
+ GtkList. */
+static void
+add_address (EMsgComposerAddressDialog *dialog,
+ const gchar *list_name)
+{
+ GtkCList *src_clist;
+ GtkCList *dest_clist;
+ gchar *name, *email;
+ gchar *text[2];
+ guint row;
+
+ src_clist = GTK_CLIST (glade_xml_get_widget (dialog->gui,
+ "address_clist"));
+ if (src_clist->selection == NULL)
+ return;
+
+ dest_clist = GTK_CLIST (glade_xml_get_widget (dialog->gui, list_name));
+ row = GPOINTER_TO_INT (src_clist->selection->data);
+
+ gtk_clist_get_text (src_clist, row, 0, &name);
+ gtk_clist_get_text (src_clist, row, 1, &email);
+
+ text[0] = make_full_address (name, email);
+ text[1] = NULL;
+
+ gtk_clist_append (dest_clist, text);
+
+ g_free (text[0]);
+}
+
+static void
+apply (EMsgComposerAddressDialog *dialog)
+{
+ gtk_signal_emit (GTK_OBJECT (dialog), signals[APPLY]);
+}
+
+
+/* Recipient list popup menu. */
+
+struct _RecipientListInfo {
+ EMsgComposerAddressDialog *dialog;
+ GtkCList *clist;
+ gint row; /* -1 if menu was popped up in an empty
+ area. */
+};
+typedef struct _RecipientListInfo RecipientListInfo;
+
+static void
+copy_recipient (RecipientListInfo *info,
+ gboolean remove)
+{
+ gchar *text;
+ gint row;
+
+ if (info->clist->selection == NULL)
+ return;
+
+ row = GPOINTER_TO_INT (info->clist->selection->data);
+ gtk_clist_get_text (info->clist, row, 0, &text);
+
+ g_free (info->dialog->cut_buffer);
+ info->dialog->cut_buffer = g_strdup (text);
+
+ if (remove)
+ gtk_clist_remove (info->clist, row);
+
+ gtk_selection_owner_set (GTK_WIDGET (info->clist),
+ GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+}
+
+static void
+copy_recipient_cb (GtkWidget *widget,
+ gpointer data)
+{
+ RecipientListInfo *info;
+
+ info = (RecipientListInfo *) data;
+ copy_recipient (info, FALSE);
+ g_free (info);
+}
+
+static void
+cut_recipient_cb (GtkWidget *widget,
+ gpointer data)
+{
+ RecipientListInfo *info;
+
+ info = (RecipientListInfo *) data;
+ copy_recipient (info, TRUE);
+ g_free (info);
+}
+
+static void
+paste_recipient_cb (GtkWidget *widget,
+ gpointer data)
+{
+ RecipientListInfo *info;
+ GdkAtom atom;
+ gchar *text[2];
+
+ info = (RecipientListInfo *) data;
+
+ atom = gdk_atom_intern ("STRING", FALSE);
+ gtk_selection_convert (GTK_WIDGET (info->clist),
+ GDK_SELECTION_PRIMARY,
+ atom,
+ GDK_CURRENT_TIME);
+
+ g_free (info);
+}
+
+static GnomeUIInfo recipient_list_item_popup_info[] = {
+ GNOMEUIINFO_ITEM_STOCK (N_("Cut"),
+ N_("Cut selected item into clipboard"),
+ cut_recipient_cb,
+ GNOME_STOCK_MENU_CUT),
+ GNOMEUIINFO_ITEM_STOCK (N_("Copy"),
+ N_("Copy selected item into clipboard"),
+ copy_recipient_cb,
+ GNOME_STOCK_MENU_COPY),
+ GNOMEUIINFO_ITEM_STOCK (N_("Paste"),
+ N_("Paste item from clipboard"),
+ paste_recipient_cb,
+ GNOME_STOCK_MENU_PASTE),
+ GNOMEUIINFO_END
+};
+
+static GnomeUIInfo recipient_list_popup_info[] = {
+ GNOMEUIINFO_ITEM_STOCK (N_("Paste"),
+ N_("Paste item from clipboard"),
+ paste_recipient_cb,
+ GNOME_STOCK_MENU_PASTE),
+ GNOMEUIINFO_END
+};
+
+
+/* Signals. */
+
+static void
+add_to_cb (GtkWidget *widget,
+ gpointer data)
+{
+ add_address (E_MSG_COMPOSER_ADDRESS_DIALOG (data), "to_clist");
+}
+
+static void
+add_cc_cb (GtkWidget *widget,
+ gpointer data)
+{
+ add_address (E_MSG_COMPOSER_ADDRESS_DIALOG (data), "cc_clist");
+}
+
+static void
+add_bcc_cb (GtkWidget *widget,
+ gpointer data)
+{
+ add_address (E_MSG_COMPOSER_ADDRESS_DIALOG (data), "bcc_clist");
+}
+
+static void
+glade_connect (GladeXML *gui,
+ const gchar *widget_name,
+ const gchar *signal_name,
+ GtkSignalFunc callback,
+ gpointer callback_data)
+{
+ GtkWidget *widget;
+
+ widget = glade_xml_get_widget (gui, widget_name);
+ if (widget == NULL)
+ g_warning ("Widget `%s' was not found.", widget_name);
+ else
+ gtk_signal_connect (GTK_OBJECT (widget), signal_name,
+ GTK_SIGNAL_FUNC (callback), callback_data);
+}
+
+static gint
+recipient_clist_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ EMsgComposerAddressDialog *dialog;
+ RecipientListInfo *info;
+ GtkWidget *popup;
+ GtkCList *clist;
+ gboolean on_row;
+ gint row, column;
+
+ dialog = E_MSG_COMPOSER_ADDRESS_DIALOG (data);
+
+ clist = GTK_CLIST (widget);
+
+ if (event->window != clist->clist_window || event->button != 3)
+ return FALSE;
+
+ on_row = gtk_clist_get_selection_info (clist, event->x, event->y,
+ &row, &column);
+
+ info = g_new (RecipientListInfo, 1);
+ info->dialog = dialog;
+ info->clist = clist;
+
+ if (on_row) {
+ gtk_clist_unselect_all (clist);
+ gtk_clist_select_row (clist, row, 0);
+ info->row = row;
+ popup = gnome_popup_menu_new (recipient_list_item_popup_info);
+ } else {
+ info->row = -1;
+ popup = gnome_popup_menu_new (recipient_list_popup_info);
+ }
+
+ gnome_popup_menu_do_popup_modal (popup, NULL, NULL, event, info);
+
+ gtk_widget_destroy (popup);
+
+ return TRUE;
+}
+
+/* FIXME needs more work. */
+static void
+recipient_clist_selection_received_cb (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ guint time,
+ gpointer data)
+{
+ GtkCList *clist;
+ gchar *text[2];
+ gchar *p;
+
+ puts (__FUNCTION__);
+
+ if (selection_data->length < 0)
+ return;
+
+ clist = GTK_CLIST (widget);
+
+ /* FIXME quoting. */
+ text[0] = g_strdup (selection_data->data);
+ text[1] = NULL;
+
+ /* It is a common mistake to paste `\n's, let's work around that. */
+ for (p = text[0]; *p != '\0'; p++) {
+ if (*p == '\n') {
+ *p = '\0';
+ break;
+ }
+ }
+
+ if (clist->selection != NULL) {
+ gint row;
+
+ row = GPOINTER_TO_INT (clist->selection->data);
+ gtk_clist_insert (clist, row, text);
+ } else {
+ gtk_clist_append (clist, text);
+ }
+
+ g_free (text[0]);
+}
+
+static void
+recipient_clist_selection_get_cb (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ EMsgComposerAddressDialog *dialog;
+ GdkAtom atom;
+
+ puts (__FUNCTION__);
+
+ dialog = E_MSG_COMPOSER_ADDRESS_DIALOG (data);
+ if (dialog->cut_buffer == NULL)
+ return; /* FIXME should I do something special? */
+
+ atom = gdk_atom_intern ("STRING", FALSE);
+ gtk_selection_data_set (selection_data, atom, 8,
+ dialog->cut_buffer,
+ strlen (dialog->cut_buffer));
+}
+
+static void
+recipient_clist_selection_clear_event_cb (GtkWidget *widget,
+ GdkEventSelection *selection,
+ gpointer data)
+{
+ EMsgComposerAddressDialog *dialog;
+
+ dialog = E_MSG_COMPOSER_ADDRESS_DIALOG (data);
+ g_free (dialog->cut_buffer);
+ dialog->cut_buffer = NULL;
+}
+
+static void
+setup_recipient_list_signals (EMsgComposerAddressDialog *dialog,
+ const gchar *name)
+{
+ glade_connect (dialog->gui, name, "button_press_event",
+ GTK_SIGNAL_FUNC (recipient_clist_button_press_cb),
+ dialog);
+ glade_connect (dialog->gui, name, "selection_received",
+ GTK_SIGNAL_FUNC (recipient_clist_selection_received_cb),
+ dialog);
+ glade_connect (dialog->gui, name, "selection_get",
+ GTK_SIGNAL_FUNC (recipient_clist_selection_get_cb),
+ dialog);
+ glade_connect (dialog->gui, name, "selection_clear_event",
+ GTK_SIGNAL_FUNC (recipient_clist_selection_clear_event_cb),
+ dialog);
+}
+
+static void
+setup_signals (EMsgComposerAddressDialog *dialog)
+{
+ glade_connect (dialog->gui, "to_add_button", "clicked",
+ GTK_SIGNAL_FUNC (add_to_cb), dialog);
+ glade_connect (dialog->gui, "cc_add_button", "clicked",
+ GTK_SIGNAL_FUNC (add_cc_cb), dialog);
+ glade_connect (dialog->gui, "bcc_add_button", "clicked",
+ GTK_SIGNAL_FUNC (add_bcc_cb), dialog);
+
+ setup_recipient_list_signals (dialog, "to_clist");
+ setup_recipient_list_signals (dialog, "cc_clist");
+ setup_recipient_list_signals (dialog, "bcc_clist");
+}
+
+
+static void
+setup_selection_targets (EMsgComposerAddressDialog *dialog)
+{
+ gtk_selection_add_target (glade_xml_get_widget (dialog->gui, "to_clist"),
+ GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING, 0);
+ gtk_selection_add_target (glade_xml_get_widget (dialog->gui, "cc_clist"),
+ GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING, 0);
+ gtk_selection_add_target (glade_xml_get_widget (dialog->gui, "bcc_clist"),
+ GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING, 0);
+}
+
+
+/* GnomeDialog methods. */
+
+static void
+clicked (GnomeDialog *dialog,
+ gint button_number)
+{
+ switch (button_number) {
+ case 0: /* OK */
+ apply (E_MSG_COMPOSER_ADDRESS_DIALOG (dialog));
+ gnome_dialog_close (dialog);
+ break;
+ case 1: /* Apply */
+ apply (E_MSG_COMPOSER_ADDRESS_DIALOG (dialog));
+ break;
+ case 2: /* Cancel */
+ gnome_dialog_close (dialog);
+ break;
+ }
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ EMsgComposerAddressDialog *dialog;
+ GtkCList *address_clist;
+ GList *p;
+
+ dialog = E_MSG_COMPOSER_ADDRESS_DIALOG (object);
+
+ gtk_object_unref (GTK_OBJECT (dialog->gui));
+ g_free (dialog->cut_buffer);
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+/* Initialization. */
+
+static void
+class_init (EMsgComposerAddressDialogClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeDialogClass *gnome_dialog_class;
+
+ object_class = GTK_OBJECT_CLASS (class);
+ object_class->destroy = destroy;
+
+ gnome_dialog_class = GNOME_DIALOG_CLASS (class);
+ gnome_dialog_class->clicked = clicked;
+
+ parent_class = gtk_type_class (gnome_dialog_get_type ());
+
+ signals[APPLY]
+ = gtk_signal_new ("apply",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EMsgComposerAddressDialogClass,
+ apply),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+init (EMsgComposerAddressDialog *dialog)
+{
+ dialog->gui = NULL;
+ dialog->cut_buffer = NULL;
+}
+
+
+GtkType
+e_msg_composer_address_dialog_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "EMsgComposerAddressDialog",
+ sizeof (EMsgComposerAddressDialog),
+ sizeof (EMsgComposerAddressDialogClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (gnome_dialog_get_type (), &info);
+ }
+
+ return type;
+}
+
+void
+e_msg_composer_address_dialog_construct (EMsgComposerAddressDialog *dialog)
+{
+ static const gchar *buttons[] = {
+ GNOME_STOCK_BUTTON_OK,
+ GNOME_STOCK_BUTTON_APPLY,
+ GNOME_STOCK_BUTTON_CANCEL,
+ NULL
+ };
+
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog));
+
+ gnome_dialog_constructv (GNOME_DIALOG (dialog),
+ _("Select recipients' addresses"),
+ buttons);
+
+ dialog->gui = glade_xml_new
+ (E_GLADEDIR "/e-msg-composer-address-dialog.glade",
+ "main_table");
+ if (dialog->gui == NULL) {
+ g_warning ("Cannot load `e-msg-composer-address-dialog.glade");
+ return;
+ }
+
+ gtk_container_add (GTK_CONTAINER (GNOME_DIALOG (dialog)->vbox),
+ glade_xml_get_widget (dialog->gui, "main_table"));
+
+ setup_selection_targets (dialog);
+ load_addresses (dialog);
+ setup_signals (dialog);
+}
+
+GtkWidget *
+e_msg_composer_address_dialog_new (void)
+{
+ EMsgComposerAddressDialog *new;
+
+ new = gtk_type_new (e_msg_composer_address_dialog_get_type ());
+ e_msg_composer_address_dialog_construct (new);
+
+ return GTK_WIDGET (new);
+}
+
+
+static void
+set_list (EMsgComposerAddressDialog *dialog,
+ const gchar *list_name,
+ GList *list)
+{
+ GtkCList *clist;
+ GList *p;
+ gchar *text[2];
+
+ clist = GTK_CLIST (glade_xml_get_widget (dialog->gui, list_name));
+
+ gtk_clist_freeze (clist);
+ gtk_clist_clear (clist);
+
+ text[1] = NULL;
+ for (p = list; p != NULL; p = p->next) {
+ text[0] = (gchar *) p->data;
+ gtk_clist_append (clist, text);
+ }
+
+ gtk_clist_thaw (clist);
+}
+
+void
+e_msg_composer_address_dialog_set_to_list (EMsgComposerAddressDialog *dialog,
+ GList *to_list)
+{
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog));
+
+ set_list (dialog, "to_clist", to_list);
+}
+
+void
+e_msg_composer_address_dialog_set_cc_list (EMsgComposerAddressDialog *dialog,
+ GList *cc_list)
+{
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog));
+
+ set_list (dialog, "cc_clist", cc_list);
+}
+
+void
+e_msg_composer_address_dialog_set_bcc_list (EMsgComposerAddressDialog *dialog,
+ GList *bcc_list)
+{
+ g_return_if_fail (dialog != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog));
+
+ set_list (dialog, "bcc_clist", bcc_list);
+}
+
+
+static GList *
+get_list (EMsgComposerAddressDialog *dialog,
+ const gchar *clist_name)
+{
+ GtkCList *address_clist;
+ GtkCList *clist;
+ GList *list;
+ guint i;
+
+ clist = GTK_CLIST (glade_xml_get_widget (dialog->gui, clist_name));
+
+ list = NULL;
+ for (i = 0; i < clist->rows; i++) {
+ gchar *addr;
+
+ gtk_clist_get_text (clist, i, 0, &addr);
+ list = g_list_prepend (list, g_strdup (addr));
+ }
+
+ return g_list_reverse (list);
+}
+
+GList *
+e_msg_composer_address_dialog_get_to_list (EMsgComposerAddressDialog *dialog)
+{
+ g_return_val_if_fail (dialog != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog), NULL);
+
+ return get_list (dialog, "to_clist");
+}
+
+GList *
+e_msg_composer_address_dialog_get_cc_list (EMsgComposerAddressDialog *dialog)
+{
+ g_return_val_if_fail (dialog != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog), NULL);
+
+ return get_list (dialog, "cc_clist");
+}
+
+GList *
+e_msg_composer_address_dialog_get_bcc_list (EMsgComposerAddressDialog *dialog)
+{
+ g_return_val_if_fail (dialog != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_ADDRESS_DIALOG (dialog), NULL);
+
+ return get_list (dialog, "bcc_clist");
+}
diff --git a/composer/e-msg-composer-address-dialog.glade b/composer/e-msg-composer-address-dialog.glade
new file mode 100644
index 0000000000..2249c27daf
--- /dev/null
+++ b/composer/e-msg-composer-address-dialog.glade
@@ -0,0 +1,575 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+ <name>address-composer</name>
+ <program_name>address-composer</program_name>
+ <directory></directory>
+ <source_directory>src</source_directory>
+ <pixmaps_directory>pixmaps</pixmaps_directory>
+ <language>C</language>
+ <gnome_support>True</gnome_support>
+ <gettext_support>True</gettext_support>
+ <use_widget_names>False</use_widget_names>
+ <output_main_file>True</output_main_file>
+ <output_support_files>True</output_support_files>
+ <output_build_files>True</output_build_files>
+ <backup_source_files>True</backup_source_files>
+ <main_source_file>interface.c</main_source_file>
+ <main_header_file>interface.h</main_header_file>
+ <handler_source_file>callbacks.c</handler_source_file>
+ <handler_header_file>callbacks.h</handler_header_file>
+ <support_source_file>support.c</support_source_file>
+ <support_header_file>support.h</support_header_file>
+ <translatable_strings_file></translatable_strings_file>
+</project>
+
+<widget>
+ <class>GnomeDialog</class>
+ <name>dialog1</name>
+ <type>GTK_WINDOW_TOPLEVEL</type>
+ <position>GTK_WIN_POS_NONE</position>
+ <modal>False</modal>
+ <allow_shrink>False</allow_shrink>
+ <allow_grow>False</allow_grow>
+ <auto_shrink>False</auto_shrink>
+ <auto_close>False</auto_close>
+ <hide_on_close>False</hide_on_close>
+
+ <widget>
+ <class>GtkVBox</class>
+ <child_name>GnomeDialog:vbox</child_name>
+ <name>dialog-vbox1</name>
+ <homogeneous>False</homogeneous>
+ <spacing>8</spacing>
+ <child>
+ <padding>4</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkTable</class>
+ <name>main_table</name>
+ <rows>3</rows>
+ <columns>2</columns>
+ <homogeneous>False</homogeneous>
+ <row_spacing>0</row_spacing>
+ <column_spacing>2</column_spacing>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label3</name>
+ <label></label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>2</top_attach>
+ <bottom_attach>3</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>False</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label1</name>
+ <label>Recipient list:</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>0</top_attach>
+ <bottom_attach>1</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkHBox</class>
+ <name>hbox1</name>
+ <homogeneous>False</homogeneous>
+ <spacing>5</spacing>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>0</top_attach>
+ <bottom_attach>1</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>False</xfill>
+ <yfill>False</yfill>
+ </child>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label2</name>
+ <label>Name:</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>False</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkEntry</class>
+ <name>name_entry</name>
+ <can_focus>True</can_focus>
+ <editable>True</editable>
+ <text_visible>True</text_visible>
+ <text_max_length>0</text_max_length>
+ <text></text>
+ <child>
+ <padding>5</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>search_button</name>
+ <width>65</width>
+ <can_focus>True</can_focus>
+ <label>Search...</label>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>False</fill>
+ </child>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkScrolledWindow</class>
+ <name>scrolledwindow1</name>
+ <hscrollbar_policy>GTK_POLICY_AUTOMATIC</hscrollbar_policy>
+ <vscrollbar_policy>GTK_POLICY_AUTOMATIC</vscrollbar_policy>
+ <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+ <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>1</top_attach>
+ <bottom_attach>2</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>5</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkCList</class>
+ <name>address_clist</name>
+ <width>180</width>
+ <height>200</height>
+ <can_focus>True</can_focus>
+ <columns>2</columns>
+ <column_widths>128,107</column_widths>
+ <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+ <show_titles>True</show_titles>
+ <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+ <widget>
+ <class>GtkLabel</class>
+ <child_name>CList:title</child_name>
+ <name>label5</name>
+ <width>50</width>
+ <label>Name</label>
+ <justify>GTK_JUSTIFY_LEFT</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ </widget>
+
+ <widget>
+ <class>GtkLabel</class>
+ <child_name>CList:title</child_name>
+ <name>label6</name>
+ <width>100</width>
+ <label>Address</label>
+ <justify>GTK_JUSTIFY_LEFT</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkHBox</class>
+ <name>hbox2</name>
+ <homogeneous>True</homogeneous>
+ <spacing>10</spacing>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>2</top_attach>
+ <bottom_attach>3</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button5</name>
+ <can_focus>True</can_focus>
+ <label>Properties...</label>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button6</name>
+ <can_focus>True</can_focus>
+ <label>Add...</label>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkTable</class>
+ <name>table2</name>
+ <rows>3</rows>
+ <columns>2</columns>
+ <homogeneous>False</homogeneous>
+ <row_spacing>0</row_spacing>
+ <column_spacing>0</column_spacing>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>1</top_attach>
+ <bottom_attach>2</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>5</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>to_add_button</name>
+ <width>60</width>
+ <can_focus>True</can_focus>
+ <label>To: &gt;&gt;</label>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>0</top_attach>
+ <bottom_attach>1</bottom_attach>
+ <xpad>5</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>cc_add_button</name>
+ <width>60</width>
+ <can_focus>True</can_focus>
+ <label>Cc: &gt;&gt;</label>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>1</top_attach>
+ <bottom_attach>2</bottom_attach>
+ <xpad>5</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>bcc_add_button</name>
+ <width>60</width>
+ <can_focus>True</can_focus>
+ <label>Bcc: &gt;&gt;</label>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>2</top_attach>
+ <bottom_attach>3</bottom_attach>
+ <xpad>5</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkScrolledWindow</class>
+ <name>scrolledwindow4</name>
+ <hscrollbar_policy>GTK_POLICY_AUTOMATIC</hscrollbar_policy>
+ <vscrollbar_policy>GTK_POLICY_AUTOMATIC</vscrollbar_policy>
+ <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+ <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>2</top_attach>
+ <bottom_attach>3</bottom_attach>
+ <xpad>2</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkCList</class>
+ <name>bcc_clist</name>
+ <width>180</width>
+ <height>100</height>
+ <can_focus>True</can_focus>
+ <columns>1</columns>
+ <column_widths>80</column_widths>
+ <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+ <show_titles>False</show_titles>
+ <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+ <widget>
+ <class>GtkLabel</class>
+ <child_name>CList:title</child_name>
+ <name>label9</name>
+ <label>label9</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkScrolledWindow</class>
+ <name>scrolledwindow2</name>
+ <hscrollbar_policy>GTK_POLICY_AUTOMATIC</hscrollbar_policy>
+ <vscrollbar_policy>GTK_POLICY_AUTOMATIC</vscrollbar_policy>
+ <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+ <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>0</top_attach>
+ <bottom_attach>1</bottom_attach>
+ <xpad>2</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkCList</class>
+ <name>to_clist</name>
+ <width>180</width>
+ <height>100</height>
+ <can_focus>True</can_focus>
+ <columns>1</columns>
+ <column_widths>80</column_widths>
+ <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+ <show_titles>False</show_titles>
+ <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+ <widget>
+ <class>GtkLabel</class>
+ <child_name>CList:title</child_name>
+ <name>label7</name>
+ <label>label7</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkScrolledWindow</class>
+ <name>scrolledwindow3</name>
+ <hscrollbar_policy>GTK_POLICY_AUTOMATIC</hscrollbar_policy>
+ <vscrollbar_policy>GTK_POLICY_AUTOMATIC</vscrollbar_policy>
+ <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+ <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>1</top_attach>
+ <bottom_attach>2</bottom_attach>
+ <xpad>2</xpad>
+ <ypad>4</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkCList</class>
+ <name>cc_clist</name>
+ <width>180</width>
+ <height>100</height>
+ <can_focus>True</can_focus>
+ <columns>1</columns>
+ <column_widths>80</column_widths>
+ <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+ <show_titles>False</show_titles>
+ <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+ <widget>
+ <class>GtkLabel</class>
+ <child_name>CList:title</child_name>
+ <name>label8</name>
+ <label>label8</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkHButtonBox</class>
+ <child_name>GnomeDialog:action_area</child_name>
+ <name>dialog-action_area1</name>
+ <layout_style>GTK_BUTTONBOX_END</layout_style>
+ <spacing>8</spacing>
+ <child_min_width>85</child_min_width>
+ <child_min_height>27</child_min_height>
+ <child_ipad_x>7</child_ipad_x>
+ <child_ipad_y>0</child_ipad_y>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>True</fill>
+ <pack>GTK_PACK_END</pack>
+ </child>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button1</name>
+ <can_default>True</can_default>
+ <has_default>True</has_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_OK</stock_button>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button2</name>
+ <can_default>True</can_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_APPLY</stock_button>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>button3</name>
+ <can_default>True</can_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_CANCEL</stock_button>
+ </widget>
+ </widget>
+ </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/composer/e-msg-composer-address-dialog.h b/composer/e-msg-composer-address-dialog.h
new file mode 100644
index 0000000000..19ffeda703
--- /dev/null
+++ b/composer/e-msg-composer-address-dialog.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-address-dialog.h
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+#ifndef __E_MSG_COMPOSER_ADDRESS_DIALOG_H__
+#define __E_MSG_COMPOSER_ADDRESS_DIALOG_H__
+
+#include <gnome.h>
+#include <glade/glade-xml.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+
+#define E_TYPE_MSG_COMPOSER_ADDRESS_DIALOG (e_msg_composer_address_dialog_get_type ())
+#define E_MSG_COMPOSER_ADDRESS_DIALOG(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MSG_COMPOSER_ADDRESS_DIALOG, EMsgComposerAddressDialog))
+#define E_MSG_COMPOSER_ADDRESS_DIALOG_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER_ADDRESS_DIALOG, EMsgComposerAddressDialogClass))
+#define E_IS_MSG_COMPOSER_ADDRESS_DIALOG(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MSG_COMPOSER_ADDRESS_DIALOG))
+#define E_IS_MSG_COMPOSER_ADDRESS_DIALOG_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER_ADDRESS_DIALOG))
+
+
+typedef struct _EMsgComposerAddressDialog EMsgComposerAddressDialog;
+typedef struct _EMsgComposerAddressDialogClass EMsgComposerAddressDialogClass;
+
+struct _EMsgComposerAddressDialog {
+ GnomeDialog parent;
+
+ GladeXML *gui;
+
+ gchar *cut_buffer;
+};
+
+struct _EMsgComposerAddressDialogClass {
+ GnomeDialogClass parent_class;
+
+ void (* apply) (EMsgComposerAddressDialog *dialog);
+};
+
+
+GtkType e_msg_composer_address_dialog_get_type (void);
+GtkWidget *e_msg_composer_address_dialog_new (void);
+void e_msg_composer_address_dialog_construct (EMsgComposerAddressDialog *dialog);
+void e_msg_composer_address_dialog_set_to_list (EMsgComposerAddressDialog *dialog, GList *to_list);
+void e_msg_composer_address_dialog_set_cc_list (EMsgComposerAddressDialog *dialog, GList *cc_list);
+void e_msg_composer_address_dialog_set_bcc_list (EMsgComposerAddressDialog *dialog, GList *bcc_list);
+GList *e_msg_composer_address_dialog_get_to_list (EMsgComposerAddressDialog *dialog);
+GList *e_msg_composer_address_dialog_get_cc_list (EMsgComposerAddressDialog *dialog);
+GList *e_msg_composer_address_dialog_get_bcc_list (EMsgComposerAddressDialog *dialog);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __E_MSG_COMPOSER_ADDRESS_DIALOG_H__ */
diff --git a/composer/e-msg-composer-address-entry.c b/composer/e-msg-composer-address-entry.c
new file mode 100644
index 0000000000..be63c815f5
--- /dev/null
+++ b/composer/e-msg-composer-address-entry.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-address-entry.c
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+/* This is a custom GtkEntry for entering address lists. For now, it does not
+ have any fancy features, but in the future we might want to make it
+ cooler. */
+
+#include <gnome.h>
+
+#include "e-msg-composer-address-entry.h"
+
+
+static GtkEntryClass *parent_class = NULL;
+
+
+/* Initialization. */
+
+static void
+class_init (EMsgComposerAddressEntryClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) klass;
+
+ parent_class = gtk_type_class (gtk_entry_get_type ());
+}
+
+static void
+init (EMsgComposerAddressEntry *msg_composer_address_entry)
+{
+}
+
+GtkType
+e_msg_composer_address_entry_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "EMsgComposerAddressEntry",
+ sizeof (EMsgComposerAddressEntry),
+ sizeof (EMsgComposerAddressEntryClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (gtk_entry_get_type (), &info);
+ }
+
+ return type;
+}
+
+
+GtkWidget *
+e_msg_composer_address_entry_new (void)
+{
+ GtkWidget *new;
+
+ new = gtk_type_new (e_msg_composer_address_entry_get_type ());
+
+ return new;
+}
+
+
+/**
+ * e_msg_composer_address_entry_get_addresses:
+ * @entry: An address entry widget
+ *
+ * Retrieve the list of addresses stored in @entry.
+ *
+ * Return value: A GList of pointers to strings representing the addresses.
+ * Notice that the strings must be freed by the caller when not needed anymore.
+ **/
+GList *
+e_msg_composer_address_entry_get_addresses (EMsgComposerAddressEntry *entry)
+{
+ GList *list;
+ const gchar *s;
+ const gchar *p, *oldp;
+ gboolean in_quotes;
+
+ s = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ in_quotes = FALSE;
+ list = NULL;
+
+ p = s;
+ oldp = s;
+
+ while (1) {
+ if (*p == '"') {
+ in_quotes = ! in_quotes;
+ p++;
+ } else if ((! in_quotes && *p == ',') || *p == 0) {
+ if (p != oldp) {
+ gchar *new_addr;
+
+ new_addr = g_strndup (oldp, p - oldp);
+ new_addr = g_strstrip (new_addr);
+ if (*new_addr != '\0')
+ list = g_list_prepend (list, new_addr);
+ else
+ g_free (new_addr);
+ }
+
+ while (*p == ',' || *p == ' ' || *p == '\t')
+ p++;
+
+ if (*p == 0)
+ break;
+
+ oldp = p;
+ } else {
+ p++;
+ }
+ }
+
+ return g_list_reverse (list);
+}
+
+/**
+ * e_msg_composer_address_entry_set_list:
+ * @entry: An address entry
+ * @list: List of pointers to strings representing the addresses that must
+ * appear in the entry
+ *
+ * Set the address list from @list.
+ **/
+void
+e_msg_composer_address_entry_set_list (EMsgComposerAddressEntry *entry,
+ GList *list)
+{
+ GString *string;
+ GList *p;
+
+ g_return_if_fail (entry != NULL);
+
+ if (list == NULL) {
+ gtk_editable_delete_text (GTK_EDITABLE (entry), -1, -1);
+ return;
+ }
+
+ string = g_string_new (NULL);
+ for (p = list; p != NULL; p = p->next) {
+ if (string->str[0] != '\0')
+ g_string_append (string, ", ");
+ g_string_append (string, p->data);
+ }
+
+ gtk_entry_set_text (GTK_ENTRY (entry), string->str);
+ g_string_free (string, TRUE);
+}
diff --git a/composer/e-msg-composer-address-entry.h b/composer/e-msg-composer-address-entry.h
new file mode 100644
index 0000000000..8e926272e4
--- /dev/null
+++ b/composer/e-msg-composer-address-entry.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-address-entry.h
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifndef __E_MSG_COMPOSER_ADDRESS_ENTRY_H__
+#define __E_MSG_COMPOSER_ADDRESS_ENTRY_H__
+
+#include <gnome.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define E_TYPE_MSG_COMPOSER_ADDRESS_ENTRY (e_msg_composer_address_entry_get_type ())
+#define E_MSG_COMPOSER_ADDRESS_ENTRY(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MSG_COMPOSER_ADDRESS_ENTRY, EMsgComposerAddressEntry))
+#define E_MSG_COMPOSER_ADDRESS_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER_ADDRESS_ENTRY, EMsgComposerAddressEntryClass))
+#define E_IS_MSG_COMPOSER_ADDRESS_ENTRY(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MSG_COMPOSER_ADDRESS_ENTRY))
+#define E_IS_MSG_COMPOSER_ADDRESS_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER_ADDRESS_ENTRY))
+
+
+typedef struct _EMsgComposerAddressEntry EMsgComposerAddressEntry;
+typedef struct _EMsgComposerAddressEntryClass EMsgComposerAddressEntryClass;
+
+struct _EMsgComposerAddressEntry {
+ GtkEntry parent;
+};
+
+struct _EMsgComposerAddressEntryClass {
+ GtkEntryClass parent_class;
+};
+
+
+GtkType e_msg_composer_address_entry_get_type (void);
+GtkWidget *e_msg_composer_address_entry_new (void);
+GList *e_msg_composer_address_entry_get_addresses (EMsgComposerAddressEntry *entry);
+void e_msg_composer_address_entry_set_list (EMsgComposerAddressEntry *entry,
+ GList *list);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __E_MSG_COMPOSER_ADDRESS_ENTRY_H__ */
diff --git a/composer/e-msg-composer-attachment-bar.c b/composer/e-msg-composer-attachment-bar.c
new file mode 100644
index 0000000000..b55a4cbad4
--- /dev/null
+++ b/composer/e-msg-composer-attachment-bar.c
@@ -0,0 +1,665 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-attachment-bar.c
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#include <gnome.h>
+#include <glade/glade.h>
+
+#include "e-msg-composer-attachment.h"
+#include "e-msg-composer-attachment-bar.h"
+
+
+#define ICON_WIDTH 64
+#define ICON_SEPARATORS " /-_"
+#define ICON_SPACING 2
+#define ICON_ROW_SPACING ICON_SPACING
+#define ICON_COL_SPACING ICON_SPACING
+#define ICON_BORDER 2
+#define ICON_TEXT_SPACING 2
+
+
+static GnomeIconListClass *parent_class = NULL;
+
+struct _EMsgComposerAttachmentBarPrivate {
+ GList *attachments;
+ guint num_attachments;
+
+ GtkWidget *context_menu;
+ GtkWidget *icon_context_menu;
+};
+
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static void update (EMsgComposerAttachmentBar *bar);
+
+
+static gchar *
+size_to_string (gulong size)
+{
+ gchar *size_string;
+
+ /* FIXME: The following should probably go into a separate module, as
+ we might have to do the same thing in other places as well. Also,
+ I am not sure this will be OK for all the languages. */
+
+ if (size < 1e3L) {
+ if (size == 1)
+ size_string = g_strdup (_("1 byte"));
+ else
+ size_string = g_strdup_printf (_("%u bytes"),
+ (guint) size);
+ } else {
+ gdouble displayed_size;
+
+ if (size < 1e6L) {
+ displayed_size = (gdouble) size / 1.0e3;
+ size_string = g_strdup_printf (_("%.1fK"),
+ displayed_size);
+ } else if (size < 1e9L) {
+ displayed_size = (gdouble) size / 1.0e6;
+ size_string = g_strdup_printf (_("%.1fM"),
+ displayed_size);
+ } else {
+ displayed_size = (gdouble) size / 1.0e9;
+ size_string = g_strdup_printf (_("%.1fG"),
+ displayed_size);
+ }
+ }
+
+ return size_string;
+}
+
+
+/* Sorting. */
+
+static gint
+attachment_sort_func (gconstpointer a, gconstpointer b)
+{
+ const EMsgComposerAttachment *attachment_a, *attachment_b;
+
+ attachment_a = (EMsgComposerAttachment *) a;
+ attachment_b = (EMsgComposerAttachment *) b;
+
+ return strcmp (attachment_a->description, attachment_b->description);
+}
+
+static void
+sort (EMsgComposerAttachmentBar *bar)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+
+ priv = bar->priv;
+
+ priv->attachments = g_list_sort (priv->attachments,
+ attachment_sort_func);
+}
+
+
+/* Attachment handling functions. */
+
+static void
+free_attachment_list (EMsgComposerAttachmentBar *bar)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+ GList *p;
+
+ priv = bar->priv;
+
+ for (p = priv->attachments; p != NULL; p = p->next)
+ gtk_object_unref (GTK_OBJECT (p->data));
+}
+
+static void
+attachment_changed_cb (EMsgComposerAttachment *attachment,
+ gpointer data)
+{
+ update (E_MSG_COMPOSER_ATTACHMENT_BAR (data));
+}
+
+static void
+add_from_file (EMsgComposerAttachmentBar *bar,
+ const gchar *file_name)
+{
+ EMsgComposerAttachment *attachment;
+
+ attachment = e_msg_composer_attachment_new (file_name);
+
+ gtk_signal_connect (GTK_OBJECT (attachment), "changed",
+ GTK_SIGNAL_FUNC (attachment_changed_cb),
+ bar);
+
+ bar->priv->attachments = g_list_append (bar->priv->attachments,
+ attachment);
+ bar->priv->num_attachments++;
+
+ sort (bar);
+ update (bar);
+
+ gtk_signal_emit (GTK_OBJECT (bar), signals[CHANGED]);
+}
+
+static void
+remove_attachment (EMsgComposerAttachmentBar *bar,
+ EMsgComposerAttachment *attachment)
+{
+ bar->priv->attachments = g_list_remove (bar->priv->attachments,
+ attachment);
+ bar->priv->num_attachments--;
+
+ gtk_object_unref (GTK_OBJECT (attachment));
+
+ gtk_signal_emit (GTK_OBJECT (bar), signals[CHANGED]);
+}
+
+
+/* Icon list contents handling. */
+
+static void
+update (EMsgComposerAttachmentBar *bar)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+ GnomeIconList *icon_list;
+ GList *p;
+
+ priv = bar->priv;
+ icon_list = GNOME_ICON_LIST (bar);
+
+ gnome_icon_list_freeze (icon_list);
+
+ gnome_icon_list_clear (icon_list);
+
+ /* FIXME could be faster, but we don't care. */
+
+ for (p = priv->attachments; p != NULL; p = p->next) {
+ EMsgComposerAttachment *attachment;
+ const gchar *icon_name;
+ gchar *size_string;
+ gchar *label;
+
+ attachment = p->data;
+ icon_name = gnome_mime_get_value (attachment->mime_type,
+ "icon-filename");
+
+ /* FIXME we need some better default icon. */
+ if (icon_name == NULL)
+ icon_name = gnome_mime_get_value ("text/plain",
+ "icon-filename");
+
+ size_string = size_to_string (attachment->size);
+
+ /* FIXME: If GnomeIconList honoured "\n", the result would be a
+ lot better. */
+ label = g_strconcat (attachment->description, "\n(",
+ size_string, ")", NULL);
+
+ gnome_icon_list_append (icon_list, icon_name, label);
+
+ g_free (label);
+ g_free (size_string);
+ }
+
+ gnome_icon_list_thaw (icon_list);
+}
+
+static void
+remove_selected (EMsgComposerAttachmentBar *bar)
+{
+ GnomeIconList *icon_list;
+ EMsgComposerAttachment *attachment;
+ GList *attachment_list;
+ GList *p;
+ gint num;
+
+ icon_list = GNOME_ICON_LIST (bar);
+
+ /* Weee! I am especially proud of this piece of cheesy code: it is
+ truly awful. But unless one attaches a huge number of files, it
+ will not be as greedy as intended. FIXME of course. */
+
+ attachment_list = NULL;
+ for (p = icon_list->selection; p != NULL; p = p->next) {
+ num = GPOINTER_TO_INT (p->data);
+ attachment = E_MSG_COMPOSER_ATTACHMENT
+ (g_list_nth (bar->priv->attachments, num)->data);
+ attachment_list = g_list_prepend (attachment_list, attachment);
+ }
+
+ for (p = attachment_list; p != NULL; p = p->next)
+ remove_attachment (bar, E_MSG_COMPOSER_ATTACHMENT (p->data));
+
+ g_list_free (attachment_list);
+
+ update (bar);
+}
+
+static void
+edit_selected (EMsgComposerAttachmentBar *bar)
+{
+ GnomeIconList *icon_list;
+ EMsgComposerAttachment *attachment;
+ gint num;
+
+ icon_list = GNOME_ICON_LIST (bar);
+
+ num = GPOINTER_TO_INT (icon_list->selection->data);
+ attachment = g_list_nth (bar->priv->attachments, num)->data;
+
+ e_msg_composer_attachment_edit (attachment, GTK_WIDGET (bar));
+}
+
+
+/* "Attach" dialog. */
+
+static void
+attach_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposerAttachmentBar *bar;
+ GtkWidget *file_selection;
+ const gchar *file_name;
+
+ file_selection = gtk_widget_get_toplevel (widget);
+ bar = E_MSG_COMPOSER_ATTACHMENT_BAR (data);
+
+ file_name = gtk_file_selection_get_filename
+ (GTK_FILE_SELECTION (file_selection));
+ add_from_file (bar, file_name);
+
+ gtk_widget_hide (file_selection);
+}
+
+static void
+add_from_user (EMsgComposerAttachmentBar *bar)
+{
+ GtkWidget *file_selection;
+ GtkWidget *cancel_button;
+ GtkWidget *ok_button;
+
+ file_selection = gtk_file_selection_new (_("Add attachment"));
+ gtk_window_set_position (GTK_WINDOW (file_selection),
+ GTK_WIN_POS_MOUSE);
+
+ ok_button = GTK_FILE_SELECTION (file_selection)->ok_button;
+ gtk_signal_connect (GTK_OBJECT (ok_button),
+ "clicked", GTK_SIGNAL_FUNC (attach_cb), bar);
+
+ cancel_button = GTK_FILE_SELECTION (file_selection)->cancel_button;
+ gtk_signal_connect_object (GTK_OBJECT (cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_hide),
+ GTK_OBJECT (file_selection));
+
+ gtk_widget_show (GTK_WIDGET (file_selection));
+}
+
+
+/* Callbacks. */
+
+static void
+add_cb (GtkWidget *widget,
+ gpointer data)
+{
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT_BAR (data));
+
+ add_from_user (E_MSG_COMPOSER_ATTACHMENT_BAR (data));
+}
+
+static void
+properties_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposerAttachmentBar *bar;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT_BAR (data));
+
+ bar = E_MSG_COMPOSER_ATTACHMENT_BAR (data);
+ edit_selected (data);
+}
+
+static void
+remove_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposerAttachmentBar *bar;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT_BAR (data));
+
+ bar = E_MSG_COMPOSER_ATTACHMENT_BAR (data);
+ remove_selected (bar);
+}
+
+
+/* Popup menu handling. */
+
+static GnomeUIInfo icon_context_menu_info[] = {
+ GNOMEUIINFO_ITEM (N_("Remove"),
+ N_("Remove selected items from the attachment list"),
+ remove_cb, NULL),
+ GNOMEUIINFO_MENU_PROPERTIES_ITEM (properties_cb, NULL),
+ GNOMEUIINFO_END
+};
+
+static GtkWidget *
+get_icon_context_menu (EMsgComposerAttachmentBar *bar)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+
+ priv = bar->priv;
+ if (priv->icon_context_menu == NULL)
+ priv->icon_context_menu = gnome_popup_menu_new
+ (icon_context_menu_info);
+
+ return priv->icon_context_menu;
+}
+
+static void
+popup_icon_context_menu (EMsgComposerAttachmentBar *bar,
+ gint num,
+ GdkEventButton *event)
+{
+ GtkWidget *menu;
+
+ menu = get_icon_context_menu (bar);
+ gnome_popup_menu_do_popup (menu, NULL, NULL, event, bar);
+}
+
+static GnomeUIInfo context_menu_info[] = {
+ GNOMEUIINFO_ITEM (N_("Add attachment..."),
+ N_("Attach a file to the message"),
+ add_cb, NULL),
+ GNOMEUIINFO_END
+};
+
+static GtkWidget *
+get_context_menu (EMsgComposerAttachmentBar *bar)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+
+ priv = bar->priv;
+ if (priv->context_menu == NULL)
+ priv->context_menu = gnome_popup_menu_new (context_menu_info);
+
+ return priv->context_menu;
+}
+
+static void
+popup_context_menu (EMsgComposerAttachmentBar *bar,
+ GdkEventButton *event)
+{
+ GtkWidget *menu;
+
+ menu = get_context_menu (bar);
+ gnome_popup_menu_do_popup (menu, NULL, NULL, event, bar);
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ EMsgComposerAttachmentBar *bar;
+
+ bar = E_MSG_COMPOSER_ATTACHMENT_BAR (object);
+
+ free_attachment_list (bar);
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+/* GtkWidget methods. */
+
+static gint
+button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EMsgComposerAttachmentBar *bar;
+ GnomeIconList *icon_list;
+ gint icon_number;
+
+ bar = E_MSG_COMPOSER_ATTACHMENT_BAR (widget);
+ icon_list = GNOME_ICON_LIST (widget);
+
+ if (event->button != 3)
+ return GTK_WIDGET_CLASS (parent_class)->button_press_event
+ (widget, event);
+
+ icon_number = gnome_icon_list_get_icon_at (icon_list,
+ event->x, event->y);
+
+ if (icon_number >= 0) {
+ gnome_icon_list_select_icon (icon_list, icon_number);
+ popup_icon_context_menu (bar, icon_number, event);
+ } else {
+ popup_context_menu (bar, event);
+ }
+
+ return TRUE;
+}
+
+
+/* GnomeIconList methods. */
+
+static gboolean
+text_changed (GnomeIconList *gil,
+ gint num,
+ const gchar *new_text)
+{
+ EMsgComposerAttachmentBar *bar;
+ EMsgComposerAttachment *attachment;
+ GList *p;
+
+ bar = E_MSG_COMPOSER_ATTACHMENT_BAR (gil);
+ p = g_list_nth (bar->priv->attachments, num);
+ attachment = p->data;
+
+ g_free (attachment->description);
+ attachment->description = g_strdup (new_text);
+
+ return TRUE;
+}
+
+
+/* Initialization. */
+
+static void
+class_init (EMsgComposerAttachmentBarClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GnomeIconListClass *icon_list_class;
+
+ object_class = GTK_OBJECT_CLASS (class);
+ widget_class = GTK_WIDGET_CLASS (class);
+ icon_list_class = GNOME_ICON_LIST_CLASS (class);
+
+ parent_class = gtk_type_class (gnome_icon_list_get_type ());
+
+ object_class->destroy = destroy;
+
+ widget_class->button_press_event = button_press_event;
+
+ icon_list_class->text_changed = text_changed;
+
+ /* Setup signals. */
+
+ signals[CHANGED] =
+ gtk_signal_new ("changed",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EMsgComposerAttachmentBarClass,
+ changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+init (EMsgComposerAttachmentBar *bar)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+ guint icon_size;
+
+ priv = g_new (EMsgComposerAttachmentBarPrivate, 1);
+
+ priv->attachments = NULL;
+ priv->context_menu = NULL;
+ priv->icon_context_menu = NULL;
+
+ priv->num_attachments = 0;
+
+ bar->priv = priv;
+
+ /* FIXME partly hardcoded. We should compute height from the font, and
+ allow at least 2 lines for every item. */
+ icon_size = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING;
+ icon_size += 24;
+
+ gtk_widget_set_usize (GTK_WIDGET (bar), icon_size * 4, icon_size);
+}
+
+
+GtkType
+e_msg_composer_attachment_bar_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "EMsgComposerAttachmentBar",
+ sizeof (EMsgComposerAttachmentBar),
+ sizeof (EMsgComposerAttachmentBarClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (gnome_icon_list_get_type (), &info);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_msg_composer_attachment_bar_new (GtkAdjustment *adj)
+{
+ EMsgComposerAttachmentBar *new;
+ GnomeIconList *icon_list;
+
+ gtk_widget_push_visual (gdk_imlib_get_visual ());
+ gtk_widget_push_colormap (gdk_imlib_get_colormap ());
+ new = gtk_type_new (e_msg_composer_attachment_bar_get_type ());
+ gtk_widget_pop_visual ();
+ gtk_widget_pop_colormap ();
+
+ icon_list = GNOME_ICON_LIST (new);
+
+ gnome_icon_list_construct (icon_list, ICON_WIDTH, adj, 0);
+
+ gnome_icon_list_set_separators (icon_list, ICON_SEPARATORS);
+ gnome_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING);
+ gnome_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING);
+ gnome_icon_list_set_icon_border (icon_list, ICON_BORDER);
+ gnome_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
+ gnome_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
+
+ return GTK_WIDGET (new);
+}
+
+
+static void
+attach_to_multipart (CamelMultipart *multipart,
+ EMsgComposerAttachment *attachment)
+{
+ CamelMimeBodyPart *part;
+
+ /* FIXME encoding etc. etc. ? */
+ /* FIXME I am not sure how to add an attachment through the Camel
+ API. :-/ */
+
+ part = camel_mime_body_part_new ();
+ camel_mime_part_set_disposition (CAMEL_MIME_PART (part), "attachment");
+ camel_mime_part_set_filename (CAMEL_MIME_PART (part),
+ g_strdup (g_basename (attachment->file_name)));
+ camel_mime_part_set_description (CAMEL_MIME_PART (part),
+ g_strdup (attachment->description));
+ camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (part),
+ g_strdup (attachment->mime_type));
+}
+
+void
+e_msg_composer_attachment_bar_to_multipart (EMsgComposerAttachmentBar *bar,
+ CamelMultipart *multipart)
+{
+ EMsgComposerAttachmentBarPrivate *priv;
+ GList *p;
+
+ g_return_if_fail (bar != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT_BAR (bar));
+ g_return_if_fail (multipart != NULL);
+ g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
+
+ priv = bar->priv;
+
+ for (p = priv->attachments; p != NULL; p = p->next) {
+ EMsgComposerAttachment *attachment;
+
+ attachment = E_MSG_COMPOSER_ATTACHMENT (p->data);
+ attach_to_multipart (multipart, attachment);
+ }
+}
+
+
+guint
+e_msg_composer_attachment_bar_get_num_attachments (EMsgComposerAttachmentBar *bar)
+{
+ g_return_val_if_fail (bar != NULL, 0);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT_BAR (bar), 0);
+
+ return bar->priv->num_attachments;
+}
+
+
+void
+e_msg_composer_attachment_bar_attach (EMsgComposerAttachmentBar *bar,
+ const gchar *file_name)
+{
+ g_return_if_fail (bar != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT_BAR (bar));
+
+ if (file_name == NULL)
+ add_from_user (bar);
+ else
+ add_from_file (bar, file_name);
+}
diff --git a/composer/e-msg-composer-attachment-bar.h b/composer/e-msg-composer-attachment-bar.h
new file mode 100644
index 0000000000..803b51a954
--- /dev/null
+++ b/composer/e-msg-composer-attachment-bar.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* msg-composer-attachment-bar.h
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifndef __E_MSG_COMPOSER_ATTACHMENT_BAR_H__
+#define __E_MSG_COMPOSER_ATTACHMENT_BAR_H__
+
+#include <gnome.h>
+#include <camel/camel-multipart.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define E_TYPE_MSG_COMPOSER_ATTACHMENT_BAR \
+ (e_msg_composer_attachment_bar_get_type ())
+#define E_MSG_COMPOSER_ATTACHMENT_BAR(obj) \
+ (GTK_CHECK_CAST ((obj), E_TYPE_MSG_COMPOSER_ATTACHMENT_BAR, EMsgComposerAttachmentBar))
+#define E_MSG_COMPOSER_ATTACHMENT_BAR_CLASS(klass) \
+ (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER_ATTACHMENT_BAR, EMsgComposerAttachmentBarClass))
+#define E_IS_MSG_COMPOSER_ATTACHMENT_BAR(obj) \
+ (GTK_CHECK_TYPE ((obj), E_TYPE_MSG_COMPOSER_ATTACHMENT_BAR))
+#define E_IS_MSG_COMPOSER_ATTACHMENT_BAR_CLASS(klass) \
+ (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER_ATTACHMENT_BAR))
+
+
+typedef struct _EMsgComposerAttachmentBarPrivate EMsgComposerAttachmentBarPrivate;
+
+struct _EMsgComposerAttachmentBar {
+ GnomeIconList parent;
+
+ EMsgComposerAttachmentBarPrivate *priv;
+};
+typedef struct _EMsgComposerAttachmentBar EMsgComposerAttachmentBar;
+
+struct _EMsgComposerAttachmentBarClass {
+ GnomeIconListClass parent_class;
+
+ void (* changed) (EMsgComposerAttachmentBar *bar);
+};
+typedef struct _EMsgComposerAttachmentBarClass EMsgComposerAttachmentBarClass;
+
+
+GtkType e_msg_composer_attachment_bar_get_type (void);
+GtkWidget *e_msg_composer_attachment_bar_new (GtkAdjustment *adj);
+void e_msg_composer_attachment_bar_to_multipart (EMsgComposerAttachmentBar *bar, CamelMultipart *multipart);
+guint e_msg_composer_attachment_bar_get_num_attachments (EMsgComposerAttachmentBar *bar);
+void e_msg_composer_attachment_bar_attach (EMsgComposerAttachmentBar *bar, const gchar *file_name);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __E_MSG_COMPOSER_ATTACHMENT_BAR_H__ */
diff --git a/composer/e-msg-composer-attachment.c b/composer/e-msg-composer-attachment.c
new file mode 100644
index 0000000000..f4741d987f
--- /dev/null
+++ b/composer/e-msg-composer-attachment.c
@@ -0,0 +1,480 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-attachment.c
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+/* This is the object representing an email attachment. It is implemented as a
+ GtkObject to make it easier for the application to handle it. For example,
+ the "changed" signal is emitted whenever something changes in the
+ attachment. Also, this contains the code to let users edit the
+ attachment manually. */
+
+#include <sys/stat.h>
+
+#include <gnome.h>
+
+#include "e-msg-composer-attachment.h"
+
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GtkObjectClass *parent_class = NULL;
+
+
+/* Utility functions. */
+
+static const gchar *
+get_mime_type (const gchar *file_name)
+{
+ const gchar *mime_type;
+
+ mime_type = gnome_mime_type_of_file (file_name);
+ if (mime_type == NULL)
+ mime_type = "application/octet-stream";
+
+ return mime_type;
+}
+
+static void
+init_mime_type (EMsgComposerAttachment *attachment)
+{
+ attachment->mime_type = g_strdup (get_mime_type (attachment->file_name));
+}
+
+static void
+set_mime_type (EMsgComposerAttachment *attachment)
+{
+ g_free (attachment->mime_type);
+ init_mime_type (attachment);
+}
+
+static void
+changed (EMsgComposerAttachment *attachment)
+{
+ gtk_signal_emit (GTK_OBJECT (attachment), signals[CHANGED]);
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ EMsgComposerAttachment *attachment;
+
+ attachment = E_MSG_COMPOSER_ATTACHMENT (object);
+
+ g_free (attachment->file_name);
+ g_free (attachment->description);
+ g_free (attachment->mime_type);
+}
+
+
+/* Signals. */
+
+static void
+real_changed (EMsgComposerAttachment *msg_composer_attachment)
+{
+ g_return_if_fail (msg_composer_attachment != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT (msg_composer_attachment));
+}
+
+
+static void
+class_init (EMsgComposerAttachmentClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) klass;
+
+ parent_class = gtk_type_class (gtk_object_get_type ());
+
+ object_class->destroy = destroy;
+
+ signals[CHANGED] = gtk_signal_new ("changed",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET
+ (EMsgComposerAttachmentClass,
+ changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+ klass->changed = real_changed;
+}
+
+static void
+init (EMsgComposerAttachment *msg_composer_attachment)
+{
+ msg_composer_attachment->editor_gui = NULL;
+ msg_composer_attachment->file_name = NULL;
+ msg_composer_attachment->description = NULL;
+ msg_composer_attachment->mime_type = NULL;
+ msg_composer_attachment->size = 0;
+}
+
+GtkType
+e_msg_composer_attachment_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "EMsgComposerAttachment",
+ sizeof (EMsgComposerAttachment),
+ sizeof (EMsgComposerAttachmentClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (gtk_object_get_type (), &info);
+ }
+
+ return type;
+}
+
+
+/**
+ * e_msg_composer_attachment_new:
+ * @file_name:
+ *
+ * Return value:
+ **/
+EMsgComposerAttachment *
+e_msg_composer_attachment_new (const gchar *file_name)
+{
+ EMsgComposerAttachment *new;
+ struct stat statbuf;
+
+ g_return_val_if_fail (file_name != NULL, NULL);
+
+ new = gtk_type_new (e_msg_composer_attachment_get_type ());
+
+ new->editor_gui = NULL;
+
+ new->file_name = g_strdup (file_name);
+ new->description = g_strdup (g_basename (new->file_name));
+
+ if (stat (file_name, &statbuf) < 0)
+ new->size = 0;
+ else
+ new->size = statbuf.st_size;
+
+ init_mime_type (new);
+
+ return new;
+}
+
+
+/* The attachment property dialog. */
+
+struct _DialogData {
+ GtkWidget *dialog;
+ GtkEntry *file_name_entry;
+ GtkEntry *description_entry;
+ GtkEntry *mime_type_entry;
+ GtkWidget *browse_widget;
+ EMsgComposerAttachment *attachment;
+};
+typedef struct _DialogData DialogData;
+
+static void
+destroy_dialog_data (DialogData *data)
+{
+ if (data->browse_widget != NULL)
+ gtk_widget_destroy (data->browse_widget);
+ g_free (data);
+}
+
+static void
+update_mime_type (DialogData *data)
+{
+ const gchar *mime_type;
+ const gchar *file_name;
+
+ file_name = gtk_entry_get_text (data->file_name_entry);
+ mime_type = get_mime_type (file_name);
+
+ gtk_entry_set_text (data->mime_type_entry, mime_type);
+}
+
+static void
+browse_ok_cb (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *file_selection;
+ DialogData *dialog_data;
+ const gchar *file_name;
+
+ dialog_data = (DialogData *) data;
+ file_selection = gtk_widget_get_toplevel (widget);
+
+ file_name = gtk_file_selection_get_filename
+ (GTK_FILE_SELECTION (file_selection));
+
+ gtk_entry_set_text (dialog_data->file_name_entry, file_name);
+
+ update_mime_type (dialog_data);
+
+ gtk_widget_hide (file_selection);
+}
+
+static void
+browse (DialogData *data)
+{
+ if (data->browse_widget == NULL) {
+ GtkWidget *file_selection;
+ GtkWidget *cancel_button;
+ GtkWidget *ok_button;
+
+ file_selection
+ = gtk_file_selection_new (_("Select attachment"));
+ gtk_window_set_position (GTK_WINDOW (file_selection),
+ GTK_WIN_POS_MOUSE);
+ gtk_window_set_transient_for (GTK_WINDOW (file_selection),
+ GTK_WINDOW (data->dialog));
+
+ ok_button = GTK_FILE_SELECTION (file_selection)->ok_button;
+ gtk_signal_connect (GTK_OBJECT (ok_button),
+ "clicked", GTK_SIGNAL_FUNC (browse_ok_cb),
+ data);
+
+ cancel_button
+ = GTK_FILE_SELECTION (file_selection)->cancel_button;
+ gtk_signal_connect_object (GTK_OBJECT (cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_hide),
+ GTK_OBJECT (file_selection));
+
+ data->browse_widget = file_selection;
+ }
+
+ gtk_widget_show (GTK_WIDGET (data->browse_widget));
+}
+
+static void
+set_entry (GladeXML *xml,
+ const gchar *widget_name,
+ const gchar *value)
+{
+ GtkEntry *entry;
+
+ entry = GTK_ENTRY (glade_xml_get_widget (xml, widget_name));
+ if (entry == NULL)
+ g_warning ("Entry for `%s' not found.", widget_name);
+ gtk_entry_set_text (entry, value);
+}
+
+static void
+connect_entry_changed (GladeXML *gui,
+ const gchar *name,
+ GtkSignalFunc func,
+ gpointer data)
+{
+ GtkWidget *widget;
+
+ widget = glade_xml_get_widget (gui, name);
+ gtk_signal_connect (GTK_OBJECT (widget), "changed", func, data);
+}
+
+static void
+connect_widget (GladeXML *gui,
+ const gchar *name,
+ const gchar *signal_name,
+ GtkSignalFunc func,
+ gpointer data)
+{
+ GtkWidget *widget;
+
+ widget = glade_xml_get_widget (gui, name);
+ gtk_signal_connect (GTK_OBJECT (widget), signal_name, func, data);
+}
+
+static void
+apply (DialogData *data)
+{
+ EMsgComposerAttachment *attachment;
+
+ attachment = data->attachment;
+
+ g_free (attachment->file_name);
+ attachment->file_name = g_strdup (gtk_entry_get_text
+ (data->file_name_entry));
+
+ g_free (attachment->description);
+ attachment->description = g_strdup (gtk_entry_get_text
+ (data->description_entry));
+
+ g_free (attachment->mime_type);
+ attachment->mime_type = g_strdup (gtk_entry_get_text
+ (data->mime_type_entry));
+
+ changed (attachment);
+}
+
+static void
+entry_changed_cb (GtkWidget *widget, gpointer data)
+{
+ DialogData *dialog_data;
+ GladeXML *gui;
+ GtkWidget *apply_button;
+
+ dialog_data = (DialogData *) data;
+ gui = dialog_data->attachment->editor_gui;
+
+ apply_button = glade_xml_get_widget (gui, "apply_button");
+ gtk_widget_set_sensitive (apply_button, TRUE);
+}
+
+static void
+close_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposerAttachment *attachment;
+ DialogData *dialog_data;
+
+ dialog_data = (DialogData *) data;
+ attachment = dialog_data->attachment;
+
+ gtk_widget_destroy (glade_xml_get_widget (attachment->editor_gui,
+ "dialog"));
+ gtk_object_unref (GTK_OBJECT (attachment->editor_gui));
+ attachment->editor_gui = NULL;
+
+ destroy_dialog_data (dialog_data);
+}
+
+static void
+apply_cb (GtkWidget *widget,
+ gpointer data)
+{
+ DialogData *dialog_data;
+
+ dialog_data = (DialogData *) data;
+ apply (dialog_data);
+}
+
+static void
+ok_cb (GtkWidget *widget,
+ gpointer data)
+{
+ apply_cb (widget, data);
+ close_cb (widget, data);
+}
+
+static void
+browse_cb (GtkWidget *widget,
+ gpointer data)
+{
+ DialogData *dialog_data;
+
+ dialog_data = (DialogData *) data;
+ browse (dialog_data);
+}
+
+static void
+file_name_focus_out_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer data)
+{
+ DialogData *dialog_data;
+
+ dialog_data = (DialogData *) data;
+ update_mime_type (dialog_data);
+}
+
+
+void
+e_msg_composer_attachment_edit (EMsgComposerAttachment *attachment,
+ GtkWidget *parent)
+{
+ DialogData *dialog_data;
+ GladeXML *editor_gui;
+
+ g_return_if_fail (attachment != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_ATTACHMENT (attachment));
+
+ if (attachment->editor_gui != NULL) {
+ GtkWidget *window;
+
+ window = glade_xml_get_widget (attachment->editor_gui,
+ "dialog");
+ gdk_window_show (window->window);
+ return;
+ }
+
+ editor_gui = glade_xml_new (E_GLADEDIR "/e-msg-composer-attachment.glade",
+ NULL);
+ if (editor_gui == NULL) {
+ g_warning ("Cannot load `e-msg-composer-attachment.glade'");
+ return;
+ }
+
+ attachment->editor_gui = editor_gui;
+
+ gtk_window_set_transient_for
+ (GTK_WINDOW (glade_xml_get_widget (editor_gui, "dialog")),
+ GTK_WINDOW (gtk_widget_get_toplevel (parent)));
+
+ dialog_data = g_new (DialogData, 1);
+ dialog_data->browse_widget = NULL;
+ dialog_data->attachment = attachment;
+ dialog_data->dialog = glade_xml_get_widget (editor_gui, "dialog");
+ dialog_data->file_name_entry = GTK_ENTRY (glade_xml_get_widget
+ (editor_gui,
+ "file_name_entry"));
+ dialog_data->description_entry = GTK_ENTRY (glade_xml_get_widget
+ (editor_gui,
+ "description_entry"));
+ dialog_data->mime_type_entry = GTK_ENTRY (glade_xml_get_widget
+ (editor_gui,
+ "mime_type_entry"));
+
+ if (attachment != NULL) {
+ set_entry (editor_gui, "file_name_entry", attachment->file_name);
+ set_entry (editor_gui, "description_entry", attachment->description);
+ set_entry (editor_gui, "mime_type_entry", attachment->mime_type);
+ }
+
+ connect_entry_changed (editor_gui, "file_name_entry",
+ entry_changed_cb, dialog_data);
+ connect_entry_changed (editor_gui, "description_entry",
+ entry_changed_cb, dialog_data);
+
+ connect_widget (editor_gui, "ok_button", "clicked", ok_cb, dialog_data);
+ connect_widget (editor_gui, "apply_button", "clicked", apply_cb, dialog_data);
+ connect_widget (editor_gui, "close_button", "clicked", close_cb, dialog_data);
+
+ connect_widget (editor_gui, "browse_button", "clicked", browse_cb, dialog_data);
+
+ connect_widget (editor_gui, "file_name_entry", "focus_out_event",
+ file_name_focus_out_cb, dialog_data);
+}
diff --git a/composer/e-msg-composer-attachment.glade b/composer/e-msg-composer-attachment.glade
new file mode 100644
index 0000000000..72c0132639
--- /dev/null
+++ b/composer/e-msg-composer-attachment.glade
@@ -0,0 +1,290 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+ <name>e-msg-composer-attachment</name>
+ <program_name>e-msg-composer-attachment</program_name>
+ <directory></directory>
+ <source_directory>src</source_directory>
+ <pixmaps_directory>pixmaps</pixmaps_directory>
+ <language>C</language>
+ <gnome_support>True</gnome_support>
+ <gettext_support>True</gettext_support>
+ <use_widget_names>False</use_widget_names>
+ <output_main_file>True</output_main_file>
+ <output_support_files>True</output_support_files>
+ <output_build_files>True</output_build_files>
+ <backup_source_files>True</backup_source_files>
+ <main_source_file>interface.c</main_source_file>
+ <main_header_file>interface.h</main_header_file>
+ <handler_source_file>callbacks.c</handler_source_file>
+ <handler_header_file>callbacks.h</handler_header_file>
+ <support_source_file>support.c</support_source_file>
+ <support_header_file>support.h</support_header_file>
+ <translatable_strings_file></translatable_strings_file>
+</project>
+
+<widget>
+ <class>GnomeDialog</class>
+ <name>dialog</name>
+ <title>Attachment properties</title>
+ <type>GTK_WINDOW_TOPLEVEL</type>
+ <position>GTK_WIN_POS_NONE</position>
+ <modal>False</modal>
+ <allow_shrink>False</allow_shrink>
+ <allow_grow>False</allow_grow>
+ <auto_shrink>False</auto_shrink>
+ <auto_close>False</auto_close>
+ <hide_on_close>False</hide_on_close>
+
+ <widget>
+ <class>GtkVBox</class>
+ <child_name>GnomeDialog:vbox</child_name>
+ <name>dialog-vbox1</name>
+ <homogeneous>False</homogeneous>
+ <spacing>8</spacing>
+ <child>
+ <padding>4</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkTable</class>
+ <name>table1</name>
+ <rows>3</rows>
+ <columns>2</columns>
+ <homogeneous>False</homogeneous>
+ <row_spacing>10</row_spacing>
+ <column_spacing>5</column_spacing>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label1</name>
+ <label>Description:</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>1</top_attach>
+ <bottom_attach>2</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>False</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label3</name>
+ <label>MIME type:</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>2</top_attach>
+ <bottom_attach>3</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>False</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkEntry</class>
+ <name>description_entry</name>
+ <can_focus>True</can_focus>
+ <editable>True</editable>
+ <text_visible>True</text_visible>
+ <text_max_length>0</text_max_length>
+ <text></text>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>1</top_attach>
+ <bottom_attach>2</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkLabel</class>
+ <name>label2</name>
+ <label>File name:</label>
+ <justify>GTK_JUSTIFY_CENTER</justify>
+ <wrap>False</wrap>
+ <xalign>0.5</xalign>
+ <yalign>0.5</yalign>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <child>
+ <left_attach>0</left_attach>
+ <right_attach>1</right_attach>
+ <top_attach>0</top_attach>
+ <bottom_attach>1</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>False</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>False</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkHBox</class>
+ <name>hbox3</name>
+ <homogeneous>False</homogeneous>
+ <spacing>10</spacing>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>0</top_attach>
+ <bottom_attach>1</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>True</yexpand>
+ <xshrink>True</xshrink>
+ <yshrink>True</yshrink>
+ <xfill>True</xfill>
+ <yfill>True</yfill>
+ </child>
+
+ <widget>
+ <class>GtkEntry</class>
+ <name>file_name_entry</name>
+ <width>290</width>
+ <can_focus>True</can_focus>
+ <editable>True</editable>
+ <text_visible>True</text_visible>
+ <text_max_length>0</text_max_length>
+ <text></text>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>browse_button</name>
+ <width>80</width>
+ <can_focus>True</can_focus>
+ <label>Browse...</label>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>False</fill>
+ </child>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkEntry</class>
+ <name>mime_type_entry</name>
+ <sensitive>False</sensitive>
+ <can_focus>True</can_focus>
+ <editable>False</editable>
+ <text_visible>True</text_visible>
+ <text_max_length>0</text_max_length>
+ <text></text>
+ <child>
+ <left_attach>1</left_attach>
+ <right_attach>2</right_attach>
+ <top_attach>2</top_attach>
+ <bottom_attach>3</bottom_attach>
+ <xpad>0</xpad>
+ <ypad>0</ypad>
+ <xexpand>True</xexpand>
+ <yexpand>False</yexpand>
+ <xshrink>False</xshrink>
+ <yshrink>False</yshrink>
+ <xfill>True</xfill>
+ <yfill>False</yfill>
+ </child>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkHButtonBox</class>
+ <child_name>GnomeDialog:action_area</child_name>
+ <name>dialog-action_area1</name>
+ <layout_style>GTK_BUTTONBOX_END</layout_style>
+ <spacing>8</spacing>
+ <child_min_width>85</child_min_width>
+ <child_min_height>27</child_min_height>
+ <child_ipad_x>7</child_ipad_x>
+ <child_ipad_y>0</child_ipad_y>
+ <child>
+ <padding>0</padding>
+ <expand>False</expand>
+ <fill>True</fill>
+ <pack>GTK_PACK_END</pack>
+ </child>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>ok_button</name>
+ <can_default>True</can_default>
+ <has_default>True</has_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_OK</stock_button>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>apply_button</name>
+ <sensitive>False</sensitive>
+ <can_default>True</can_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_APPLY</stock_button>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <name>close_button</name>
+ <can_default>True</can_default>
+ <can_focus>True</can_focus>
+ <stock_button>GNOME_STOCK_BUTTON_CLOSE</stock_button>
+ </widget>
+ </widget>
+ </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/composer/e-msg-composer-attachment.h b/composer/e-msg-composer-attachment.h
new file mode 100644
index 0000000000..7e3c88238b
--- /dev/null
+++ b/composer/e-msg-composer-attachment.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer-attachment.h
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+#ifndef __E_MSG_COMPOSER_ATTACHMENT_H__
+#define __E_MSG_COMPOSER_ATTACHMENT_H__
+
+#include <gnome.h>
+#include <glade/glade-xml.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define E_TYPE_MSG_COMPOSER_ATTACHMENT (e_msg_composer_attachment_get_type ())
+#define E_MSG_COMPOSER_ATTACHMENT(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MSG_COMPOSER_ATTACHMENT, EMsgComposerAttachment))
+#define E_MSG_COMPOSER_ATTACHMENT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER_ATTACHMENT, EMsgComposerAttachmentClass))
+#define E_IS_MSG_COMPOSER_ATTACHMENT(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MSG_COMPOSER_ATTACHMENT))
+#define E_IS_MSG_COMPOSER_ATTACHMENT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER_ATTACHMENT))
+
+
+typedef struct _EMsgComposerAttachment EMsgComposerAttachment;
+typedef struct _EMsgComposerAttachmentClass EMsgComposerAttachmentClass;
+
+struct _EMsgComposerAttachment {
+ GtkObject parent;
+
+ GladeXML *editor_gui;
+
+ gchar *file_name;
+ gchar *description;
+ gchar *mime_type;
+
+ gulong size;
+};
+
+struct _EMsgComposerAttachmentClass {
+ GtkObjectClass parent_class;
+
+ void (*changed) (EMsgComposerAttachment *msg_composer_attachment);
+};
+
+
+GtkType e_msg_composer_attachment_get_type (void);
+EMsgComposerAttachment *e_msg_composer_attachment_new (const gchar *file_name);
+void e_msg_composer_attachment_edit (EMsgComposerAttachment *attachment,
+ GtkWidget *parent);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __E_MSG_COMPOSER_ATTACHMENT_H__ */
diff --git a/composer/e-msg-composer-hdrs.c b/composer/e-msg-composer-hdrs.c
new file mode 100644
index 0000000000..2c27531bb8
--- /dev/null
+++ b/composer/e-msg-composer-hdrs.c
@@ -0,0 +1,355 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* msg-composer-hdrs.c
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifdef _HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gnome.h>
+#include <camel/camel.h>
+
+#include "e-msg-composer-address-entry.h"
+#include "e-msg-composer-hdrs.h"
+
+
+struct _EMsgComposerHdrsPrivate {
+ /* Total number of headers that we have. */
+ guint num_hdrs;
+
+ /* The tooltips. */
+ GtkTooltips *tooltips;
+
+ /* Standard headers. */
+ GtkWidget *to_entry;
+ GtkWidget *cc_entry;
+ GtkWidget *bcc_entry;
+ GtkWidget *subject_entry;
+};
+
+
+static GtkTableClass *parent_class = NULL;
+
+enum {
+ SHOW_ADDRESS_DIALOG,
+ LAST_SIGNAL
+};
+
+static gint signals[LAST_SIGNAL];
+
+
+static void
+address_button_clicked_cb (GtkButton *button,
+ gpointer data)
+{
+ gtk_signal_emit (GTK_OBJECT (data), signals[SHOW_ADDRESS_DIALOG]);
+}
+
+static GtkWidget *
+add_header (EMsgComposerHdrs *hdrs,
+ const gchar *name,
+ const gchar *tip,
+ const gchar *tip_private,
+ gboolean addrbook_button)
+{
+ EMsgComposerHdrsPrivate *priv;
+ GtkWidget *label;
+ GtkWidget *entry;
+ guint pad;
+
+ priv = hdrs->priv;
+
+ if (addrbook_button) {
+ label = gtk_button_new_with_label (name);
+ gtk_signal_connect (GTK_OBJECT (label), "clicked",
+ GTK_SIGNAL_FUNC (address_button_clicked_cb),
+ hdrs);
+ pad = 2;
+ gtk_tooltips_set_tip (hdrs->priv->tooltips, label,
+ _("Click here for the address book"),
+ NULL);
+ } else {
+ label = gtk_label_new (name);
+ pad = GNOME_PAD;
+ }
+
+ gtk_table_attach (GTK_TABLE (hdrs), label,
+ 0, 1, priv->num_hdrs, priv->num_hdrs + 1,
+ GTK_FILL, GTK_FILL,
+ pad, pad);
+ gtk_widget_show (label);
+
+ entry = e_msg_composer_address_entry_new ();
+ gtk_table_attach (GTK_TABLE (hdrs), entry,
+ 1, 2, priv->num_hdrs, priv->num_hdrs + 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL,
+ 2, 2);
+ gtk_widget_show (entry);
+
+ gtk_tooltips_set_tip (hdrs->priv->tooltips, entry, tip, tip_private);
+
+ priv->num_hdrs++;
+
+ return entry;
+}
+
+static void
+setup_headers (EMsgComposerHdrs *hdrs)
+{
+ EMsgComposerHdrsPrivate *priv;
+
+ priv = hdrs->priv;
+
+ priv->to_entry = add_header
+ (hdrs, _("To:"),
+ _("Enter the recipients of the message"),
+ NULL,
+ TRUE);
+ priv->cc_entry = add_header
+ (hdrs, _("Cc:"),
+ _("Enter the addresses that will receive a carbon copy of "
+ "the message"),
+ NULL,
+ TRUE);
+ priv->bcc_entry = add_header
+ (hdrs, _("Bcc:"),
+ _("Enter the addresses that will receive a carbon copy of "
+ "the message without appearing in the recipient list of "
+ "the message."),
+ NULL,
+ TRUE);
+ priv->subject_entry = add_header
+ (hdrs, _("Subject:"),
+ _("Enter the subject of the mail"),
+ NULL,
+ FALSE);
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ EMsgComposerHdrs *hdrs;
+ EMsgComposerHdrsPrivate *priv;
+
+ hdrs = E_MSG_COMPOSER_HDRS (object);
+ priv = hdrs->priv;
+
+ gtk_object_destroy (GTK_OBJECT (priv->tooltips));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+class_init (EMsgComposerHdrsClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = GTK_OBJECT_CLASS (class);
+ object_class->destroy = destroy;
+
+ parent_class = gtk_type_class (gtk_table_get_type ());
+
+ signals[SHOW_ADDRESS_DIALOG] =
+ gtk_signal_new ("show_address_dialog",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EMsgComposerHdrsClass,
+ show_address_dialog),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+init (EMsgComposerHdrs *hdrs)
+{
+ EMsgComposerHdrsPrivate *priv;
+
+ priv = g_new (EMsgComposerHdrsPrivate, 1);
+
+ priv->to_entry = NULL;
+ priv->cc_entry = NULL;
+ priv->bcc_entry = NULL;
+ priv->subject_entry = NULL;
+
+ priv->tooltips = gtk_tooltips_new ();
+
+ priv->num_hdrs = 0;
+
+ hdrs->priv = priv;
+}
+
+
+GtkType
+e_msg_composer_hdrs_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "EMsgComposerHdrs",
+ sizeof (EMsgComposerHdrs),
+ sizeof (EMsgComposerHdrsClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (gtk_table_get_type (), &info);
+ }
+
+ return type;
+}
+
+GtkWidget *
+e_msg_composer_hdrs_new (void)
+{
+ EMsgComposerHdrs *new;
+
+ new = gtk_type_new (e_msg_composer_hdrs_get_type ());
+
+ setup_headers (E_MSG_COMPOSER_HDRS (new));
+
+ return GTK_WIDGET (new);
+}
+
+
+static void
+set_recipients (CamelMimeMessage *msg,
+ GtkWidget *entry_widget,
+ const gchar *type)
+{
+ EMsgComposerAddressEntry *entry;
+ GList *list;
+ GList *p;
+
+ entry = E_MSG_COMPOSER_ADDRESS_ENTRY (entry_widget);
+ list = e_msg_composer_address_entry_get_addresses (entry);
+
+ /* FIXME leak? */
+
+ for (p = list; p != NULL; p = p->next) {
+ printf ("Adding `%s:' header: %s\n", type, (gchar *) p->data);
+ camel_mime_message_add_recipient (msg, type, (gchar *) p->data);
+ }
+
+ g_list_free (list);
+}
+
+void
+e_msg_composer_hdrs_to_message (EMsgComposerHdrs *hdrs,
+ CamelMimeMessage *msg)
+{
+ const gchar *s;
+
+ g_return_if_fail (hdrs != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs));
+ g_return_if_fail (msg != NULL);
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg));
+
+ s = gtk_entry_get_text (GTK_ENTRY (hdrs->priv->subject_entry));
+ camel_mime_message_set_subject (msg, g_strdup (s));
+
+ set_recipients (msg, hdrs->priv->to_entry, CAMEL_RECIPIENT_TYPE_TO);
+ set_recipients (msg, hdrs->priv->cc_entry, CAMEL_RECIPIENT_TYPE_CC);
+ set_recipients (msg, hdrs->priv->bcc_entry, CAMEL_RECIPIENT_TYPE_BCC);
+}
+
+
+void
+e_msg_composer_hdrs_set_to (EMsgComposerHdrs *hdrs,
+ GList *to_list)
+{
+ EMsgComposerAddressEntry *entry;
+
+ g_return_if_fail (hdrs != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs));
+
+ entry = E_MSG_COMPOSER_ADDRESS_ENTRY (hdrs->priv->to_entry);
+ e_msg_composer_address_entry_set_list (entry, to_list);
+}
+
+void
+e_msg_composer_hdrs_set_cc (EMsgComposerHdrs *hdrs,
+ GList *cc_list)
+{
+ EMsgComposerAddressEntry *entry;
+
+ g_return_if_fail (hdrs != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs));
+
+ entry = E_MSG_COMPOSER_ADDRESS_ENTRY (hdrs->priv->cc_entry);
+ e_msg_composer_address_entry_set_list (entry, cc_list);
+}
+
+void
+e_msg_composer_hdrs_set_bcc (EMsgComposerHdrs *hdrs,
+ GList *bcc_list)
+{
+ EMsgComposerAddressEntry *entry;
+
+ g_return_if_fail (hdrs != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs));
+
+ entry = E_MSG_COMPOSER_ADDRESS_ENTRY (hdrs->priv->bcc_entry);
+ e_msg_composer_address_entry_set_list (entry, bcc_list);
+}
+
+
+GList *
+e_msg_composer_hdrs_get_to (EMsgComposerHdrs *hdrs)
+{
+ g_return_val_if_fail (hdrs != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL);
+
+ return e_msg_composer_address_entry_get_addresses
+ (E_MSG_COMPOSER_ADDRESS_ENTRY (hdrs->priv->to_entry));
+}
+
+GList *
+e_msg_composer_hdrs_get_cc (EMsgComposerHdrs *hdrs)
+{
+ g_return_val_if_fail (hdrs != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL);
+
+ return e_msg_composer_address_entry_get_addresses
+ (E_MSG_COMPOSER_ADDRESS_ENTRY (hdrs->priv->cc_entry));
+}
+
+GList *
+e_msg_composer_hdrs_get_bcc (EMsgComposerHdrs *hdrs)
+{
+ g_return_val_if_fail (hdrs != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL);
+
+ return e_msg_composer_address_entry_get_addresses
+ (E_MSG_COMPOSER_ADDRESS_ENTRY (hdrs->priv->bcc_entry));
+}
+
diff --git a/composer/e-msg-composer-hdrs.h b/composer/e-msg-composer-hdrs.h
new file mode 100644
index 0000000000..d053573d85
--- /dev/null
+++ b/composer/e-msg-composer-hdrs.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* msg-composer-hdrs.h
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifndef ___E_MSG_COMPOSER_HDRS_H__
+#define ___E_MSG_COMPOSER_HDRS_H__
+
+#include <gnome.h>
+#include <camel/camel-mime-message.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define E_TYPE_MSG_COMPOSER_HDRS (e_msg_composer_hdrs_get_type ())
+#define E_MSG_COMPOSER_HDRS(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MSG_COMPOSER_HDRS, EMsgComposerHdrs))
+#define E_MSG_COMPOSER_HDRS_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER_HDRS, EMsgComposerHdrsClass))
+#define E_IS_MSG_COMPOSER_HDRS(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MSG_COMPOSER_HDRS))
+#define E_IS_MSG_COMPOSER_HDRS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER_HDRS))
+
+
+typedef struct _EMsgComposerHdrs EMsgComposerHdrs;
+typedef struct _EMsgComposerHdrsClass EMsgComposerHdrsClass;
+typedef struct _EMsgComposerHdrsPrivate EMsgComposerHdrsPrivate;
+
+struct _EMsgComposerHdrs {
+ GtkTable parent;
+
+ EMsgComposerHdrsPrivate *priv;
+};
+
+struct _EMsgComposerHdrsClass {
+ GtkTableClass parent_class;
+
+ void (* show_address_dialog) (EMsgComposerHdrs *hdrs);
+};
+
+
+GtkType e_msg_composer_hdrs_get_type (void);
+GtkWidget *e_msg_composer_hdrs_new (void);
+void e_msg_composer_hdrs_to_message (EMsgComposerHdrs *hdrs,
+ CamelMimeMessage *msg);
+
+void e_msg_composer_hdrs_set_to (EMsgComposerHdrs *hdrs,
+ GList *to_list);
+void e_msg_composer_hdrs_set_cc (EMsgComposerHdrs *hdrs,
+ GList *cc_list);
+void e_msg_composer_hdrs_set_bcc (EMsgComposerHdrs *hdrs,
+ GList *bcc_list);
+
+GList *e_msg_composer_hdrs_get_to (EMsgComposerHdrs *hdrs);
+GList *e_msg_composer_hdrs_get_cc (EMsgComposerHdrs *hdrs);
+GList *e_msg_composer_hdrs_get_bcc (EMsgComposerHdrs *hdrs);
+
+#ifdef _cplusplus
+}
+#endif /* _cplusplus */
+
+
+#endif /* __E_MSG_COMPOSER_HDRS_H__ */
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
new file mode 100644
index 0000000000..f4271ea7e6
--- /dev/null
+++ b/composer/e-msg-composer.c
@@ -0,0 +1,564 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer.c
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+/*
+
+ TODO
+
+ - Somehow users should be able to see if any file(s) are attached even when
+ the attachment bar is not shown.
+
+*/
+
+#ifdef _HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gnome.h>
+#include <glade/glade.h>
+#include <camel/camel.h>
+
+#include "e-msg-composer.h"
+#include "e-msg-composer-address-dialog.h"
+#include "e-msg-composer-attachment-bar.h"
+#include "e-msg-composer-hdrs.h"
+
+
+#define DEFAULT_WIDTH 600
+#define DEFAULT_HEIGHT 500
+
+
+enum {
+ SEND,
+ POSTPONE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GnomeAppClass *parent_class = NULL;
+
+
+static void
+free_string_list (GList *list)
+{
+ GList *p;
+
+ if (list == NULL)
+ return;
+
+ for (p = list; p != NULL; p = p->next)
+ g_free (p->data);
+
+ g_list_free (list);
+}
+
+/* This functions builds a CamelMimeMessage for the message that the user has
+ composed in `composer'. */
+static CamelMimeMessage *
+build_message (EMsgComposer *composer)
+{
+ CamelMimeMessage *new;
+ CamelMimeBodyPart *body_part;
+ CamelMultipart *multipart;
+ gchar *text;
+
+ new = camel_mime_message_new_with_session (NULL);
+
+ e_msg_composer_hdrs_to_message (E_MSG_COMPOSER_HDRS (composer->hdrs),
+ new);
+
+ multipart = camel_multipart_new ();
+ body_part = camel_mime_body_part_new ();
+
+ text = gtk_editable_get_chars (GTK_EDITABLE (composer->text), 0, -1);
+ camel_mime_part_set_text (CAMEL_MIME_PART (body_part), text);
+ camel_multipart_add_part (multipart, body_part);
+
+ e_msg_composer_attachment_bar_to_multipart
+ (E_MSG_COMPOSER_ATTACHMENT_BAR (composer->attachment_bar),
+ multipart);
+
+ camel_medium_set_content_object (CAMEL_MEDIUM (new),
+ CAMEL_DATA_WRAPPER (multipart));
+
+ /* FIXME refcounting is most certainly wrong. We want all the stuff to
+ be destroyed when we unref() the message. */
+
+ return new;
+}
+
+
+static void
+show_attachments (EMsgComposer *composer,
+ gboolean show)
+{
+ if (show) {
+ gtk_widget_show (composer->attachment_scrolled_window);
+ gtk_widget_show (composer->attachment_bar);
+ } else {
+ gtk_widget_hide (composer->attachment_scrolled_window);
+ gtk_widget_hide (composer->attachment_bar);
+ }
+
+ composer->attachment_bar_visible = show;
+
+ /* Update the GUI. */
+
+ gtk_check_menu_item_set_active
+ (GTK_CHECK_MENU_ITEM
+ (glade_xml_get_widget (composer->menubar_gui,
+ "menu_view_attachments")),
+ show);
+
+ /* XXX we should update the toggle toolbar item as well. At
+ this point, it is not a toggle because Glade is broken. */
+}
+
+
+/* Address dialog callbacks. */
+
+static void
+address_dialog_destroy_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposer *composer;
+
+ composer = E_MSG_COMPOSER (data);
+ composer->address_dialog = NULL;
+}
+
+static void
+address_dialog_apply_cb (EMsgComposerAddressDialog *dialog,
+ gpointer data)
+{
+ EMsgComposerHdrs *hdrs;
+ GList *list;
+
+ hdrs = E_MSG_COMPOSER_HDRS (E_MSG_COMPOSER (data)->hdrs);
+
+ list = e_msg_composer_address_dialog_get_to_list (dialog);
+ e_msg_composer_hdrs_set_to (hdrs, list);
+
+ list = e_msg_composer_address_dialog_get_cc_list (dialog);
+ e_msg_composer_hdrs_set_cc (hdrs, list);
+
+ list = e_msg_composer_address_dialog_get_bcc_list (dialog);
+ e_msg_composer_hdrs_set_bcc (hdrs, list);
+}
+
+
+/* Message composer window callbacks. */
+
+static void
+send_cb (GtkWidget *widget,
+ gpointer data)
+{
+ gtk_signal_emit (GTK_OBJECT (data), signals[SEND]);
+}
+
+static void
+menu_view_attachments_activate_cb (GtkWidget *widget,
+ gpointer data)
+{
+ e_msg_composer_show_attachments (E_MSG_COMPOSER (data),
+ GTK_CHECK_MENU_ITEM (widget)->active);
+}
+
+static void
+toolbar_view_attachments_clicked_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposer *composer;
+
+ composer = E_MSG_COMPOSER (data);
+
+ e_msg_composer_show_attachments (composer,
+ ! composer->attachment_bar_visible);
+}
+
+static void
+add_attachment_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposer *composer;
+
+ composer = E_MSG_COMPOSER (data);
+
+ e_msg_composer_attachment_bar_attach
+ (E_MSG_COMPOSER_ATTACHMENT_BAR (composer->attachment_bar),
+ NULL);
+}
+
+/* Create the address dialog if not created already. */
+static void
+setup_address_dialog (EMsgComposer *composer)
+{
+ EMsgComposerAddressDialog *dialog;
+ EMsgComposerHdrs *hdrs;
+ GList *list;
+
+ if (composer->address_dialog != NULL)
+ return;
+
+ composer->address_dialog = e_msg_composer_address_dialog_new ();
+ dialog = E_MSG_COMPOSER_ADDRESS_DIALOG (composer->address_dialog);
+ hdrs = E_MSG_COMPOSER_HDRS (composer->hdrs);
+
+ gtk_signal_connect (GTK_OBJECT (dialog),
+ "destroy", address_dialog_destroy_cb, composer);
+ gtk_signal_connect (GTK_OBJECT (dialog),
+ "apply", address_dialog_apply_cb, composer);
+
+ list = e_msg_composer_hdrs_get_to (hdrs);
+ e_msg_composer_address_dialog_set_to_list (dialog, list);
+
+ list = e_msg_composer_hdrs_get_cc (hdrs);
+ e_msg_composer_address_dialog_set_cc_list (dialog, list);
+
+ list = e_msg_composer_hdrs_get_bcc (hdrs);
+ e_msg_composer_address_dialog_set_bcc_list (dialog, list);
+}
+
+static void
+address_dialog_cb (GtkWidget *widget,
+ gpointer data)
+{
+ EMsgComposer *composer;
+
+ /* FIXME maybe we should hide the dialog on Cancel/OK instead of
+ destroying it. */
+
+ composer = E_MSG_COMPOSER (data);
+
+ setup_address_dialog (composer);
+
+ gtk_widget_show (composer->address_dialog);
+ gdk_window_show (composer->address_dialog->window);
+}
+
+static void
+glade_connect (GladeXML *gui,
+ const gchar *widget_name,
+ const gchar *signal_name,
+ GtkSignalFunc callback,
+ gpointer callback_data)
+{
+ GtkWidget *widget;
+
+ widget = glade_xml_get_widget (gui, widget_name);
+ if (widget == NULL)
+ g_warning ("Widget `%s' was not found.", widget_name);
+ else
+ gtk_signal_connect (GTK_OBJECT (widget), signal_name,
+ GTK_SIGNAL_FUNC (callback), callback_data);
+}
+
+static void
+attachment_bar_changed (EMsgComposerAttachmentBar *bar,
+ gpointer data)
+{
+ EMsgComposer *composer;
+
+ composer = E_MSG_COMPOSER (data);
+
+ if (e_msg_composer_attachment_bar_get_num_attachments (bar) > 0)
+ e_msg_composer_show_attachments (composer, TRUE);
+ else
+ e_msg_composer_show_attachments (composer, FALSE);
+}
+
+static void
+setup_signals (EMsgComposer *composer)
+{
+ glade_connect (composer->menubar_gui, "menu_send",
+ "activate", GTK_SIGNAL_FUNC (send_cb), composer);
+ glade_connect (composer->toolbar_gui, "toolbar_send",
+ "clicked", GTK_SIGNAL_FUNC (send_cb), composer);
+
+ glade_connect (composer->menubar_gui, "menu_view_attachments",
+ "activate",
+ GTK_SIGNAL_FUNC (menu_view_attachments_activate_cb),
+ composer);
+ glade_connect (composer->toolbar_gui, "toolbar_view_attachments",
+ "clicked",
+ GTK_SIGNAL_FUNC (toolbar_view_attachments_clicked_cb),
+ composer);
+
+ glade_connect (composer->menubar_gui, "menu_add_attachment",
+ "activate",
+ GTK_SIGNAL_FUNC (add_attachment_cb), composer);
+ glade_connect (composer->toolbar_gui, "toolbar_add_attachment",
+ "clicked",
+ GTK_SIGNAL_FUNC (add_attachment_cb), composer);
+
+ glade_connect (composer->menubar_gui, "menubar_address_dialog",
+ "activate",
+ GTK_SIGNAL_FUNC (address_dialog_cb), composer);
+ glade_connect (composer->toolbar_gui, "toolbar_address_dialog",
+ "clicked",
+ GTK_SIGNAL_FUNC (address_dialog_cb), composer);
+
+ gtk_signal_connect (GTK_OBJECT (composer->attachment_bar),
+ "changed",
+ GTK_SIGNAL_FUNC (attachment_bar_changed),
+ composer);
+
+ gtk_signal_connect (GTK_OBJECT (composer->hdrs), "show_address_dialog",
+ GTK_SIGNAL_FUNC (address_dialog_cb),
+ composer);
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ EMsgComposer *composer;
+
+ composer = E_MSG_COMPOSER (object);
+
+ gtk_object_unref (GTK_OBJECT (composer->menubar_gui));
+ gtk_object_unref (GTK_OBJECT (composer->toolbar_gui));
+ gtk_object_unref (GTK_OBJECT (composer->appbar_gui));
+
+ if (composer->address_dialog != NULL)
+ gtk_widget_destroy (composer->address_dialog);
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+class_init (EMsgComposerClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass *) klass;
+
+ object_class->destroy = destroy;
+
+ parent_class = gtk_type_class (gnome_app_get_type ());
+
+ signals[SEND] =
+ gtk_signal_new ("send",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EMsgComposerClass, send),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ signals[POSTPONE] =
+ gtk_signal_new ("postpone",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EMsgComposerClass, postpone),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+init (EMsgComposer *composer)
+{
+ composer->menubar_gui = NULL;
+ composer->toolbar_gui = NULL;
+ composer->appbar_gui = NULL;
+
+ composer->hdrs = NULL;
+
+ composer->text = NULL;
+ composer->text_scrolled_window = NULL;
+
+ composer->address_dialog = NULL;
+
+ composer->attachment_bar = NULL;
+ composer->attachment_scrolled_window = NULL;
+}
+
+
+GtkType
+e_msg_composer_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "EMsgComposer",
+ sizeof (EMsgComposer),
+ sizeof (EMsgComposerClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (gnome_app_get_type (), &info);
+ }
+
+ return type;
+}
+
+
+/**
+ * e_msg_composer_construct:
+ * @composer: A message composer widget
+ *
+ * Construct @composer.
+ **/
+void
+e_msg_composer_construct (EMsgComposer *composer)
+{
+ GtkWidget *vbox;
+
+ gtk_window_set_default_size (GTK_WINDOW (composer),
+ DEFAULT_WIDTH, DEFAULT_HEIGHT);
+
+ gnome_app_construct (GNOME_APP (composer), "e-msg-composer",
+ "Compose a message");
+
+ composer->menubar_gui = glade_xml_new (E_GLADEDIR "/e-msg-composer.glade",
+ "menubar");
+ gnome_app_set_menus (GNOME_APP (composer),
+ GTK_MENU_BAR (glade_xml_get_widget (composer->menubar_gui,
+ "menubar")));
+
+ composer->toolbar_gui = glade_xml_new (E_GLADEDIR "/e-msg-composer.glade",
+ "toolbar");
+ gnome_app_set_toolbar (GNOME_APP (composer),
+ GTK_TOOLBAR (glade_xml_get_widget (composer->toolbar_gui,
+ "toolbar")));
+
+ composer->appbar_gui = glade_xml_new (E_GLADEDIR "/e-msg-composer.glade",
+ "appbar");
+ gnome_app_set_statusbar (GNOME_APP (composer),
+ glade_xml_get_widget (composer->appbar_gui,
+ "appbar"));
+
+ vbox = gtk_vbox_new (FALSE, 0);
+
+ composer->hdrs = e_msg_composer_hdrs_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), composer->hdrs, FALSE, TRUE, 0);
+ gtk_widget_show (composer->hdrs);
+
+ /* GtkText for message body editing, wrapped into a
+ GtkScrolledWindow. */
+
+ composer->text_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy
+ (GTK_SCROLLED_WINDOW (composer->text_scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ composer->text = gtk_text_new (NULL, NULL);
+ gtk_text_set_word_wrap (GTK_TEXT (composer->text), FALSE);
+ gtk_text_set_editable (GTK_TEXT (composer->text), TRUE);
+ gtk_container_add (GTK_CONTAINER (composer->text_scrolled_window),
+ composer->text);
+ gtk_widget_show (composer->text);
+ gtk_box_pack_start (GTK_BOX (vbox), composer->text_scrolled_window,
+ TRUE, TRUE, 0);
+ gtk_widget_show (composer->text_scrolled_window);
+
+ /* Attachment editor, wrapped into a GtkScrolledWindow. We don't
+ show it for now. */
+
+ composer->attachment_scrolled_window = gtk_scrolled_window_new (NULL,
+ NULL);
+ gtk_scrolled_window_set_policy
+ (GTK_SCROLLED_WINDOW (composer->attachment_scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ composer->attachment_bar = e_msg_composer_attachment_bar_new (NULL);
+ GTK_WIDGET_SET_FLAGS (composer->attachment_bar, GTK_CAN_FOCUS);
+ gtk_container_add (GTK_CONTAINER (composer->attachment_scrolled_window),
+ composer->attachment_bar);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ composer->attachment_scrolled_window,
+ FALSE, TRUE, GNOME_PAD_SMALL);
+
+ gnome_app_set_contents (GNOME_APP (composer), vbox);
+ gtk_widget_show (vbox);
+
+ e_msg_composer_show_attachments (composer, FALSE);
+
+ setup_signals (composer);
+}
+
+/**
+ * e_msg_composer_new:
+ *
+ * Create a new message composer widget.
+ *
+ * Return value: A pointer to the newly created widget
+ **/
+GtkWidget *
+e_msg_composer_new (void)
+{
+ GtkWidget *new;
+
+ new = gtk_type_new (e_msg_composer_get_type ());
+ e_msg_composer_construct (E_MSG_COMPOSER (new));
+
+ return new;
+}
+
+
+/**
+ * e_msg_composer_show_attachments:
+ * @composer: A message composer widget
+ * @show: A boolean specifying whether the attachment bar should be shown or
+ * not
+ *
+ * If @show is %FALSE, hide the attachment bar. Otherwise, show it.
+ **/
+void
+e_msg_composer_show_attachments (EMsgComposer *composer,
+ gboolean show)
+{
+ g_return_if_fail (composer != NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+ show_attachments (composer, show);
+}
+
+
+/**
+ * e_msg_composer_get_message:
+ * @composer: A message composer widget
+ *
+ * Retrieve the message edited by the user as a CamelMimeMessage. The
+ * CamelMimeMessage object is created on the fly; subsequent calls to this
+ * function will always create new objects from scratch.
+ *
+ * Return value: A pointer to the new CamelMimeMessage object
+ **/
+CamelMimeMessage *
+e_msg_composer_get_message (EMsgComposer *composer)
+{
+ g_return_val_if_fail (composer != NULL, NULL);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+ return build_message (composer);
+}
diff --git a/composer/e-msg-composer.glade b/composer/e-msg-composer.glade
new file mode 100644
index 0000000000..b747c6f979
--- /dev/null
+++ b/composer/e-msg-composer.glade
@@ -0,0 +1,587 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+ <name>Message Composer</name>
+ <program_name>e-msg-composer</program_name>
+ <directory></directory>
+ <source_directory>src</source_directory>
+ <pixmaps_directory>pixmaps</pixmaps_directory>
+ <language>C</language>
+ <gnome_support>True</gnome_support>
+ <gettext_support>True</gettext_support>
+ <use_widget_names>False</use_widget_names>
+ <output_main_file>True</output_main_file>
+ <output_support_files>True</output_support_files>
+ <output_build_files>True</output_build_files>
+ <backup_source_files>True</backup_source_files>
+ <main_source_file>interface.c</main_source_file>
+ <main_header_file>interface.h</main_header_file>
+ <handler_source_file>callbacks.c</handler_source_file>
+ <handler_header_file>callbacks.h</handler_header_file>
+ <support_source_file>support.c</support_source_file>
+ <support_header_file>support.h</support_header_file>
+ <translatable_strings_file></translatable_strings_file>
+</project>
+
+<widget>
+ <class>GnomeApp</class>
+ <name>app1</name>
+ <title>Message Composer</title>
+ <type>GTK_WINDOW_TOPLEVEL</type>
+ <position>GTK_WIN_POS_NONE</position>
+ <modal>False</modal>
+ <allow_shrink>False</allow_shrink>
+ <allow_grow>True</allow_grow>
+ <auto_shrink>False</auto_shrink>
+ <enable_layout_config>True</enable_layout_config>
+
+ <widget>
+ <class>GnomeDock</class>
+ <child_name>GnomeApp:dock</child_name>
+ <name>dock1</name>
+ <allow_floating>True</allow_floating>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+
+ <widget>
+ <class>GnomeDockItem</class>
+ <name>dockitem1</name>
+ <border_width>2</border_width>
+ <placement>GNOME_DOCK_TOP</placement>
+ <band>0</band>
+ <position>0</position>
+ <offset>0</offset>
+ <locked>False</locked>
+ <exclusive>True</exclusive>
+ <never_floating>False</never_floating>
+ <never_vertical>True</never_vertical>
+ <never_horizontal>False</never_horizontal>
+ <shadow_type>GTK_SHADOW_OUT</shadow_type>
+
+ <widget>
+ <class>GtkMenuBar</class>
+ <name>menubar</name>
+ <shadow_type>GTK_SHADOW_NONE</shadow_type>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>file</name>
+ <stock_item>GNOMEUIINFO_MENU_FILE_TREE</stock_item>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>file_menu</name>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>open</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_open_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:08 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_OPEN_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>save</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_save_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:13 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_SAVE_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>save_as</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_save_as_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:16 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_SAVE_AS_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>save_in_folder</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_save_in_folder_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:20 GMT</last_modification_time>
+ </signal>
+ <label>Save in _Folder...</label>
+ <right_justify>False</right_justify>
+ <stock_icon>GNOME_STOCK_MENU_SAVE_AS</stock_icon>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator8</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>menu_send</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_send_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:29 GMT</last_modification_time>
+ </signal>
+ <label>S_end</label>
+ <right_justify>False</right_justify>
+ <stock_icon>GNOME_STOCK_MENU_MAIL_SND</stock_icon>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>postpone</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_postpone_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:37 GMT</last_modification_time>
+ </signal>
+ <label>_Postpone</label>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator1</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>exit</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_exit_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:25:59 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_EXIT_ITEM</stock_item>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>edit</name>
+ <stock_item>GNOMEUIINFO_MENU_EDIT_TREE</stock_item>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>edit_menu</name>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>undo</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_undo_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:10 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_UNDO_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>redo</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_redo_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:15 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_REDO_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator3</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>cut</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_cut_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:21 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_CUT_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>copy</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_copy_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:26 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_COPY_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>paste</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_paste_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:31 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_PASTE_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator2</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>select_all</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_select_all_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:37 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_SELECT_ALL_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator5</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>find</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_find_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:42 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_FIND_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>find_again</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_find_again_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:47 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_FIND_AGAIN_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>replace</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_replace_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:53 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_REPLACE_ITEM</stock_item>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator6</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>properties</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_properties_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:26:58 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_PROPERTIES_ITEM</stock_item>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>view1</name>
+ <stock_item>GNOMEUIINFO_MENU_VIEW_TREE</stock_item>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>view1_menu</name>
+
+ <widget>
+ <class>GtkCheckMenuItem</class>
+ <name>menu_view_attachments</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_view_attachments_activate</handler>
+ <last_modification_time>Fri, 05 Nov 1999 18:31:16 GMT</last_modification_time>
+ </signal>
+ <label>Attachments</label>
+ <active>False</active>
+ <always_show_toggle>False</always_show_toggle>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>insert</name>
+ <label>_Insert</label>
+ <right_justify>False</right_justify>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>insert_menu</name>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>menu_add_attachment</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_attachment_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:05 GMT</last_modification_time>
+ </signal>
+ <label>Attachment...</label>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>text_from_file</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_text_from_file_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:16 GMT</last_modification_time>
+ </signal>
+ <label>Text from file...</label>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator6</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>hypertext_link</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_hypertext_link_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:21 GMT</last_modification_time>
+ </signal>
+ <label>Hypertext link...</label>
+ <right_justify>False</right_justify>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>settings</name>
+ <stock_item>GNOMEUIINFO_MENU_SETTINGS_TREE</stock_item>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>settings_menu</name>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>preferences</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_preferences_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:37 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_PREFERENCES_ITEM</stock_item>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>tools</name>
+ <label>_Tools</label>
+ <right_justify>False</right_justify>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>tools_menu</name>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>spell_check1</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_spell_check_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:44 GMT</last_modification_time>
+ </signal>
+ <label>_Spell check</label>
+ <right_justify>False</right_justify>
+ <stock_icon>GNOME_STOCK_MENU_SPELLCHECK</stock_icon>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>separator7</name>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>check_names</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_check_names_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:49 GMT</last_modification_time>
+ </signal>
+ <label>Check names</label>
+ <right_justify>False</right_justify>
+ </widget>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>menubar_address_dialog</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_address_book_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:27:53 GMT</last_modification_time>
+ </signal>
+ <label>Address book...</label>
+ <right_justify>False</right_justify>
+ <stock_icon>GNOME_STOCK_MENU_BOOK_RED</stock_icon>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GtkMenuItem</class>
+ <name>help</name>
+ <stock_item>GNOMEUIINFO_MENU_HELP_TREE</stock_item>
+
+ <widget>
+ <class>GtkMenu</class>
+ <name>help_menu</name>
+
+ <widget>
+ <class>GtkPixmapMenuItem</class>
+ <name>about</name>
+ <signal>
+ <name>activate</name>
+ <handler>on_about_activate</handler>
+ <last_modification_time>Wed, 27 Oct 1999 22:28:01 GMT</last_modification_time>
+ </signal>
+ <stock_item>GNOMEUIINFO_MENU_ABOUT_ITEM</stock_item>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GnomeDockItem</class>
+ <name>dockitem2</name>
+ <border_width>1</border_width>
+ <placement>GNOME_DOCK_TOP</placement>
+ <band>1</band>
+ <position>0</position>
+ <offset>0</offset>
+ <locked>False</locked>
+ <exclusive>True</exclusive>
+ <never_floating>False</never_floating>
+ <never_vertical>False</never_vertical>
+ <never_horizontal>False</never_horizontal>
+ <shadow_type>GTK_SHADOW_OUT</shadow_type>
+
+ <widget>
+ <class>GtkToolbar</class>
+ <name>toolbar</name>
+ <border_width>1</border_width>
+ <orientation>GTK_ORIENTATION_HORIZONTAL</orientation>
+ <type>GTK_TOOLBAR_BOTH</type>
+ <space_size>16</space_size>
+ <space_style>GTK_TOOLBAR_SPACE_LINE</space_style>
+ <relief>GTK_RELIEF_NONE</relief>
+ <tooltips>True</tooltips>
+
+ <widget>
+ <class>GtkButton</class>
+ <child_name>Toolbar:button</child_name>
+ <name>toolbar_send</name>
+ <tooltip>Send this message</tooltip>
+ <label>Send
+</label>
+ <stock_pixmap>GNOME_STOCK_PIXMAP_MAIL_SND</stock_pixmap>
+ </widget>
+
+ <widget>
+ <class>GtkToggleButton</class>
+ <child_name>Toolbar:button</child_name>
+ <name>toolbar_view_attachments</name>
+ <tooltip>Show attachments for this message</tooltip>
+ <label>Show
+attachments</label>
+ <active>False</active>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <child_name>Toolbar:button</child_name>
+ <name>toolbar_add_attachment</name>
+ <tooltip>Attach a file</tooltip>
+ <label>Attach
+file</label>
+ </widget>
+
+ <widget>
+ <class>GtkButton</class>
+ <child_name>Toolbar:button</child_name>
+ <name>toolbar_address_dialog</name>
+ <tooltip>Go to addressbook</tooltip>
+ <label>Edit
+addresses</label>
+ <stock_pixmap>GNOME_STOCK_PIXMAP_BOOK_RED</stock_pixmap>
+ </widget>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>Placeholder</class>
+ <child_name>GnomeDock:contents</child_name>
+ </widget>
+ </widget>
+
+ <widget>
+ <class>GnomeAppBar</class>
+ <child_name>GnomeApp:appbar</child_name>
+ <name>appbar</name>
+ <has_progress>True</has_progress>
+ <has_status>True</has_status>
+ <child>
+ <padding>0</padding>
+ <expand>True</expand>
+ <fill>True</fill>
+ </child>
+ </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
new file mode 100644
index 0000000000..3028dedca3
--- /dev/null
+++ b/composer/e-msg-composer.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-msg-composer.h
+ *
+ * Copyright (C) 1999 Helix Code, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifndef ___E_MSG_COMPOSER_H__
+#define ___E_MSG_COMPOSER_H__
+
+#include <gnome.h>
+#include <glade/glade.h>
+
+#include "e-msg-composer-attachment-bar.h"
+#include "e-msg-composer-hdrs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+
+#define E_TYPE_MSG_COMPOSER (e_msg_composer_get_type ())
+#define E_MSG_COMPOSER(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MSG_COMPOSER, EMsgComposer))
+#define E_MSG_COMPOSER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MSG_COMPOSER, EMsgComposerClass))
+#define E_IS_MSG_COMPOSER(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MSG_COMPOSER))
+#define E_IS_MSG_COMPOSER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MSG_COMPOSER))
+
+
+typedef struct _EMsgComposer EMsgComposer;
+typedef struct _EMsgComposerClass EMsgComposerClass;
+
+struct _EMsgComposer {
+ GnomeApp parent;
+
+ GladeXML *menubar_gui;
+ GladeXML *toolbar_gui;
+ GladeXML *appbar_gui;
+
+ GtkWidget *hdrs;
+
+ GtkWidget *text;
+ GtkWidget *text_scrolled_window;
+
+ GtkWidget *attachment_bar;
+ GtkWidget *attachment_scrolled_window;
+
+ GtkWidget *address_dialog;
+
+ gboolean attachment_bar_visible : 1;
+};
+
+struct _EMsgComposerClass {
+ GnomeAppClass parent_class;
+
+ void (* send) (EMsgComposer *composer);
+ void (* postpone) (EMsgComposer *composer);
+};
+
+
+GtkType e_msg_composer_get_type (void);
+void e_msg_composer_construct (EMsgComposer *composer);
+GtkWidget *e_msg_composer_new (void);
+void e_msg_composer_show_attachments (EMsgComposer *composer, gboolean show);
+CamelMimeMessage *e_msg_composer_get_message (EMsgComposer *composer);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ___E_MSG_COMPOSER_H__ */
diff --git a/composer/main.c b/composer/main.c
new file mode 100644
index 0000000000..8fbf01b5e8
--- /dev/null
+++ b/composer/main.c
@@ -0,0 +1,52 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+#include <gnome.h>
+
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-stream-fs.h>
+#include <camel/camel-stream.h>
+
+#include "e-msg-composer.h"
+
+static void
+send_cb (EMsgComposer *composer,
+ gpointer data)
+{
+ CamelMimeMessage *message;
+ CamelStream *stream;
+ gint stdout_dup;
+
+ message = e_msg_composer_get_message (composer);
+
+ stdout_dup = dup (1);
+ stream = camel_stream_fs_new_with_fd (stdout_dup);
+ camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message),
+ stream);
+ camel_stream_close (stream);
+
+ gtk_object_unref (GTK_OBJECT (message));
+
+#if 0
+ gtk_widget_destroy (GTK_WIDGET (composer));
+ gtk_main_quit ();
+#endif
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *composer;
+
+ gnome_init ("test", "0.0", argc, argv);
+ glade_gnome_init ();
+
+ composer = e_msg_composer_new ();
+ gtk_widget_show (composer);
+
+ gtk_signal_connect (GTK_OBJECT (composer), "send",
+ GTK_SIGNAL_FUNC (send_cb), NULL);
+
+ gtk_main ();
+
+ return 0;
+}