From de169b4feeeaf2013aa256ddf70276bacbd6542a Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 10 Feb 2009 02:51:52 +0000 Subject: Rewrite the signature management UI from top to bottom. - Break the UI out of Glade and into small, manageable widgets: ESignatureEditor (moved from mail to widgets/misc) ESignatureManager ESignatureTreeView ESignatureScriptDialog - Move several signature utilities to e-util/e-signature-utils.c so they're accessible from widgets/misc without introducing circular dependences. - Have EMailShellModule listen for new GtkhtmlEditor windows (from which EMsgComposer and ESignatureEditor are derived) and configure the window with spelling and HTML editing user preferences. - Drastically simplifies em-composer-prefs.c. svn path=/branches/kill-bonobo/; revision=37239 --- widgets/misc/Makefile.am | 8 + widgets/misc/e-signature-combo-box.c | 1 + widgets/misc/e-signature-combo-box.h | 1 + widgets/misc/e-signature-editor.c | 503 +++++++++++++++++++++ widgets/misc/e-signature-editor.h | 70 +++ widgets/misc/e-signature-manager.c | 746 +++++++++++++++++++++++++++++++ widgets/misc/e-signature-manager.h | 100 +++++ widgets/misc/e-signature-script-dialog.c | 463 +++++++++++++++++++ widgets/misc/e-signature-script-dialog.h | 76 ++++ widgets/misc/e-signature-tree-view.c | 405 +++++++++++++++++ widgets/misc/e-signature-tree-view.h | 78 ++++ 11 files changed, 2451 insertions(+) create mode 100644 widgets/misc/e-signature-editor.c create mode 100644 widgets/misc/e-signature-editor.h create mode 100644 widgets/misc/e-signature-manager.c create mode 100644 widgets/misc/e-signature-manager.h create mode 100644 widgets/misc/e-signature-script-dialog.c create mode 100644 widgets/misc/e-signature-script-dialog.h create mode 100644 widgets/misc/e-signature-tree-view.c create mode 100644 widgets/misc/e-signature-tree-view.h (limited to 'widgets/misc') diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am index ce702ec9fd..a1d555085b 100644 --- a/widgets/misc/Makefile.am +++ b/widgets/misc/Makefile.am @@ -78,6 +78,10 @@ widgetsinclude_HEADERS = \ e-selection-model-simple.h \ e-selection-model.h \ e-signature-combo-box.h \ + e-signature-editor.h \ + e-signature-manager.h \ + e-signature-script-dialog.h \ + e-signature-tree-view.h \ e-unicode.h \ e-colors.h @@ -125,6 +129,10 @@ libemiscwidgets_la_SOURCES = \ e-selection-model-simple.c \ e-selection-model.c \ e-signature-combo-box.c \ + e-signature-editor.c \ + e-signature-manager.c \ + e-signature-script-dialog.c \ + e-signature-tree-view.c \ e-unicode.c \ e-colors.c diff --git a/widgets/misc/e-signature-combo-box.c b/widgets/misc/e-signature-combo-box.c index 9ebd20a0b2..40a93b7d25 100644 --- a/widgets/misc/e-signature-combo-box.c +++ b/widgets/misc/e-signature-combo-box.c @@ -1,4 +1,5 @@ /* + * e-signature-combo-box.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/widgets/misc/e-signature-combo-box.h b/widgets/misc/e-signature-combo-box.h index ec05c2b5d7..5cdd224e6b 100644 --- a/widgets/misc/e-signature-combo-box.h +++ b/widgets/misc/e-signature-combo-box.h @@ -1,4 +1,5 @@ /* + * e-signature-combo-box.h * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/widgets/misc/e-signature-editor.c b/widgets/misc/e-signature-editor.c new file mode 100644 index 0000000000..c7dcbdaad9 --- /dev/null +++ b/widgets/misc/e-signature-editor.c @@ -0,0 +1,503 @@ +/* + * e-signature-editor.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-signature-editor.h" + +#include +#include + +#include +#include + +#define E_SIGNATURE_EDITOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SIGNATURE_EDITOR, ESignatureEditorPrivate)) + +enum { + PROP_0, + PROP_SIGNATURE +}; + +struct _ESignatureEditorPrivate { + GtkActionGroup *action_group; + ESignature *signature; + GtkWidget *entry; + gchar *original_name; +}; + +static const gchar *ui = +"\n" +" \n" +" \n" +" \n" +" \n" +" " +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +""; + +static gpointer parent_class = NULL; + +static void +handle_error (GError **error) +{ + if (*error != NULL) { + g_warning ("%s", (*error)->message); + g_clear_error (error); + } +} + +static void +action_close_cb (GtkAction *action, + ESignatureEditor *editor) +{ + gboolean something_changed = FALSE; + const gchar *original_name; + const gchar *signature_name; + + original_name = editor->priv->original_name; + signature_name = gtk_entry_get_text (GTK_ENTRY (editor->priv->entry)); + + something_changed |= gtkhtml_editor_has_undo (GTKHTML_EDITOR (editor)); + something_changed |= (strcmp (signature_name, original_name) != 0); + + if (something_changed) { + gint response; + + response = e_error_run ( + GTK_WINDOW (editor), + "mail:ask-signature-changed", NULL); + if (response == GTK_RESPONSE_YES) { + GtkActionGroup *action_group; + + action_group = editor->priv->action_group; + action = gtk_action_group_get_action ( + action_group, "save-and-close"); + gtk_action_activate (action); + return; + } else if (response == GTK_RESPONSE_CANCEL) + return; + } + + gtk_widget_destroy (GTK_WIDGET (editor)); +} + +static void +action_save_and_close_cb (GtkAction *action, + ESignatureEditor *editor) +{ + GtkWidget *entry; + ESignatureList *signature_list; + ESignature *signature; + ESignature *same_name; + const gchar *filename; + gchar *signature_name; + gboolean html; + GError *error = NULL; + + entry = editor->priv->entry; + html = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (editor)); + + if (editor->priv->signature == NULL) { + signature = e_signature_new (); + signature->name = g_strdup (_("Unnamed")); + signature->script = FALSE; + signature->html = html; + + /* FIXME Pass a GError and deal with it. */ + signature->filename = e_create_signature_file (NULL); + } else { + signature = g_object_ref (editor->priv->signature); + signature->html = html; + } + + filename = signature->filename; + gtkhtml_editor_save (GTKHTML_EDITOR (editor), filename, html, &error); + + if (error != NULL) { + e_error_run ( + GTK_WINDOW (editor), + "mail:no-save-signature", + error->message, NULL); + g_clear_error (&error); + return; + } + + signature_list = e_get_signature_list (); + + signature_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + g_strstrip (signature_name); + + /* Make sure the signature name is not blank. */ + if (*signature_name == '\0') { + e_error_run ( + GTK_WINDOW (editor), + "mail:blank-signature", NULL); + gtk_widget_grab_focus (entry); + g_free (signature_name); + return; + } + + /* Don't overwrite an existing signature of the same name. + * XXX ESignatureList misuses const. */ + same_name = (ESignature *) e_signature_list_find ( + signature_list, E_SIGNATURE_FIND_NAME, signature_name); + if (same_name != NULL && strcmp (signature->uid, same_name->uid) != 0) { + e_error_run ( + GTK_WINDOW (editor), + "mail:signature-already-exists", + signature_name, NULL); + gtk_widget_grab_focus (entry); + g_free (signature_name); + return; + } + + g_free (signature->name); + signature->name = signature_name; + + if (editor->priv->signature != NULL) + e_signature_list_change (signature_list, signature); + else + e_signature_list_add (signature_list, signature); + e_signature_list_save (signature_list); + + gtk_widget_destroy (GTK_WIDGET (editor)); +} + +static GtkActionEntry entries[] = { + + { "close", + GTK_STOCK_CLOSE, + N_("_Close"), + "w", + NULL, + G_CALLBACK (action_close_cb) }, + + { "save-and-close", + GTK_STOCK_SAVE, + N_("_Save and Close"), + "Return", + NULL, + G_CALLBACK (action_save_and_close_cb) }, + + { "file-menu", + NULL, + N_("_File"), + NULL, + NULL, + NULL } +}; + +static gboolean +signature_editor_delete_event_cb (ESignatureEditor *editor, + GdkEvent *event) +{ + GtkActionGroup *action_group; + GtkAction *action; + + action_group = editor->priv->action_group; + action = gtk_action_group_get_action (action_group, "close"); + gtk_action_activate (action); + + return TRUE; +} + +static void +signature_editor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SIGNATURE: + e_signature_editor_set_signature ( + E_SIGNATURE_EDITOR (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +signature_editor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SIGNATURE: + g_value_set_object ( + value, e_signature_editor_get_signature ( + E_SIGNATURE_EDITOR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +signature_editor_dispose (GObject *object) +{ + ESignatureEditorPrivate *priv; + + priv = E_SIGNATURE_EDITOR_GET_PRIVATE (object); + + if (priv->action_group != NULL) { + g_object_unref (priv->action_group); + priv->action_group = NULL; + } + + if (priv->signature != NULL) { + g_object_unref (priv->signature); + priv->signature = NULL; + } + + if (priv->entry != NULL) { + g_object_unref (priv->entry); + priv->entry = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +signature_editor_finalize (GObject *object) +{ + ESignatureEditorPrivate *priv; + + priv = E_SIGNATURE_EDITOR_GET_PRIVATE (object); + + g_free (priv->original_name); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +signature_editor_class_init (ESignatureEditorClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ESignatureEditorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = signature_editor_set_property; + object_class->get_property = signature_editor_get_property; + object_class->dispose = signature_editor_dispose; + object_class->finalize = signature_editor_finalize; + + g_object_class_install_property ( + object_class, + PROP_SIGNATURE, + g_param_spec_object ( + "signature", + NULL, + NULL, + E_TYPE_SIGNATURE, + G_PARAM_READWRITE)); +} + +static void +signature_editor_init (ESignatureEditor *editor) +{ + GtkActionGroup *action_group; + GtkUIManager *manager; + GtkWidget *container; + GtkWidget *widget; + GtkWidget *vbox; + GError *error = NULL; + + editor->priv = E_SIGNATURE_EDITOR_GET_PRIVATE (editor); + vbox = GTKHTML_EDITOR (editor)->vbox; + + manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (editor)); + + gtk_ui_manager_add_ui_from_string (manager, ui, -1, &error); + handle_error (&error); + + action_group = gtk_action_group_new ("signature"); + gtk_action_group_set_translation_domain ( + action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions ( + action_group, entries, + G_N_ELEMENTS (entries), editor); + gtk_ui_manager_insert_action_group (manager, action_group, 0); + editor->priv->action_group = g_object_ref (action_group); + + gtk_ui_manager_ensure_update (manager); + + gtk_window_set_title (GTK_WINDOW (editor), _("Edit Signature")); + + widget = gtk_hbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (widget), 6); + gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); + /* Position 2 should be between the main and style toolbars. */ + gtk_box_reorder_child (GTK_BOX (vbox), widget, 2); + gtk_widget_show (widget); + container = widget; + + widget = gtk_entry_new (); + gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 0); + editor->priv->entry = g_object_ref_sink (widget); + gtk_widget_show (widget); + + widget = gtk_label_new_with_mnemonic (_("_Signature Name:")); + gtk_label_set_mnemonic_widget (GTK_LABEL (widget), editor->priv->entry); + gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + g_signal_connect ( + editor, "delete-event", + G_CALLBACK (signature_editor_delete_event_cb), NULL); + + e_signature_editor_set_signature (editor, NULL); +} + +GType +e_signature_editor_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (ESignatureEditorClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) signature_editor_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ESignatureEditor), + 0, /* n_preallocs */ + (GInstanceInitFunc) signature_editor_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + GTKHTML_TYPE_EDITOR, "ESignatureEditor", + &type_info, 0); + } + + return type; +} + +GtkWidget * +e_signature_editor_new (void) +{ + return g_object_new (E_TYPE_SIGNATURE_EDITOR, NULL); +} + +ESignature * +e_signature_editor_get_signature (ESignatureEditor *editor) +{ + g_return_val_if_fail (E_IS_SIGNATURE_EDITOR (editor), NULL); + + return editor->priv->signature; +} + +void +e_signature_editor_set_signature (ESignatureEditor *editor, + ESignature *signature) +{ + const gchar *filename; + const gchar *signature_name; + gchar *contents; + gsize length; + GError *error = NULL; + + g_return_if_fail (E_IS_SIGNATURE_EDITOR (editor)); + + if (signature != NULL) + g_return_if_fail (E_SIGNATURE (signature)); + + if (editor->priv->signature != NULL) { + g_object_unref (editor->priv->signature); + editor->priv->signature = NULL; + } + + if (signature == NULL) + goto exit; + + editor->priv->signature = g_object_ref (signature); + + /* Load signature content. */ + + filename = signature->filename; + + if (signature->html) + g_file_get_contents (filename, &contents, &length, &error); + else { + gchar *data; + + data = e_read_signature_file (signature, FALSE, &error); + if (data != NULL) + contents = g_strdup_printf ("
\n%s", data);
+		else
+			contents = NULL;
+		length = -1;
+		g_free (data);
+	}
+
+	if (error == NULL) {
+		gtkhtml_editor_set_html_mode (
+			GTKHTML_EDITOR (editor), signature->html);
+		gtkhtml_editor_set_text_html (
+			GTKHTML_EDITOR (editor), contents, length);
+		g_free (contents);
+	} else {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+
+exit:
+	if (signature != NULL)
+		signature_name = signature->name;
+	else
+		signature_name = _("Unnamed");
+
+	/* Set the entry text before we grab focus. */
+	g_free (editor->priv->original_name);
+	editor->priv->original_name = g_strdup (signature_name);
+	gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), signature_name);
+
+	/* Set the focus appropriately.  If this is a new signature, draw
+	 * the user's attention to the signature name entry.  Otherwise go
+	 * straight to the editing area. */
+	if (signature == NULL)
+		gtk_widget_grab_focus (editor->priv->entry);
+	else {
+		GtkHTML *html;
+
+		html = gtkhtml_editor_get_html (GTKHTML_EDITOR (editor));
+		gtk_widget_grab_focus (GTK_WIDGET (html));
+	}
+
+	g_object_notify (G_OBJECT (editor), "signature");
+}
diff --git a/widgets/misc/e-signature-editor.h b/widgets/misc/e-signature-editor.h
new file mode 100644
index 0000000000..9d6c37ac87
--- /dev/null
+++ b/widgets/misc/e-signature-editor.h
@@ -0,0 +1,70 @@
+/*
+ * e-signature-editor.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_EDITOR_H
+#define E_SIGNATURE_EDITOR_H
+
+#include 
+#include 
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE_EDITOR \
+	(e_signature_editor_get_type ())
+#define E_SIGNATURE_EDITOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNATURE_EDITOR, ESignatureEditor))
+#define E_SIGNATURE_EDITOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNATURE_EDITOR, ESignatureEditorClass))
+#define E_IS_SIGNATURE_EDITOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNATURE_EDITOR))
+#define E_IS_SIGNATURE_EDITOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNATURE_EDITOR))
+#define E_SIGNATURE_EDITOR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNATURE_EDITOR, ESignatureEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignatureEditor ESignatureEditor;
+typedef struct _ESignatureEditorClass ESignatureEditorClass;
+typedef struct _ESignatureEditorPrivate ESignatureEditorPrivate;
+
+struct _ESignatureEditor {
+	GtkhtmlEditor parent;
+	ESignatureEditorPrivate *priv;
+};
+
+struct _ESignatureEditorClass {
+	GtkhtmlEditorClass parent_class;
+};
+
+GType		e_signature_editor_get_type	 (void);
+GtkWidget *	e_signature_editor_new		 (void);
+ESignature *	e_signature_editor_get_signature (ESignatureEditor *editor);
+void		e_signature_editor_set_signature (ESignatureEditor *editor,
+						  ESignature *signature);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_EDITOR_H */
diff --git a/widgets/misc/e-signature-manager.c b/widgets/misc/e-signature-manager.c
new file mode 100644
index 0000000000..0c145e9821
--- /dev/null
+++ b/widgets/misc/e-signature-manager.c
@@ -0,0 +1,746 @@
+/*
+ * e-signature-manager.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-signature-manager.h"
+
+#include 
+#include 
+#include 
+#include "e-util/e-binding.h"
+#include "e-signature-tree-view.h"
+#include "e-signature-script-dialog.h"
+
+#define E_SIGNATURE_MANAGER_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SIGNATURE_MANAGER, ESignatureManagerPrivate))
+
+struct _ESignatureManagerPrivate {
+	ESignatureList *signature_list;
+
+	GtkWidget *tree_view;
+	GtkWidget *add_button;
+	GtkWidget *add_script_button;
+	GtkWidget *edit_button;
+	GtkWidget *remove_button;
+
+	guint allow_scripts : 1;
+	guint prefer_html   : 1;
+};
+
+enum {
+	PROP_0,
+	PROP_ALLOW_SCRIPTS,
+	PROP_PREFER_HTML,
+	PROP_SIGNATURE_LIST
+};
+
+enum {
+	ADD_SIGNATURE,
+	ADD_SIGNATURE_SCRIPT,
+	EDITOR_CREATED,
+	EDIT_SIGNATURE,
+	REMOVE_SIGNATURE,
+	LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+static void
+signature_manager_emit_editor_created (ESignatureManager *manager,
+                                       GtkWidget *editor)
+{
+	g_return_if_fail (E_IS_SIGNATURE_EDITOR (editor));
+
+	g_signal_emit (manager, signals[EDITOR_CREATED], 0, editor);
+}
+
+static gboolean
+signature_manager_key_press_event_cb (ESignatureManager *manager,
+                                      GdkEventKey *event)
+{
+	if (event->keyval == GDK_Delete) {
+		e_signature_manager_remove_signature (manager);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+signature_manager_run_script_dialog (ESignatureManager *manager,
+                                     ESignature *signature,
+                                     const gchar *title)
+{
+	GtkWidget *dialog;
+	GFile *script_file;
+	const gchar *script_name;
+	gboolean success = FALSE;
+	gpointer parent;
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (manager));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	dialog = e_signature_script_dialog_new (parent);
+	gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+	if (signature->filename != NULL && signature->name != NULL) {
+
+		script_file = g_file_new_for_path (signature->filename);
+		script_name = signature->name;
+
+		e_signature_script_dialog_set_script_file (
+			E_SIGNATURE_SCRIPT_DIALOG (dialog), script_file);
+		e_signature_script_dialog_set_script_name (
+			E_SIGNATURE_SCRIPT_DIALOG (dialog), script_name);
+
+		g_object_unref (script_file);
+	}
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+		goto exit;
+
+	script_file = e_signature_script_dialog_get_script_file (
+		E_SIGNATURE_SCRIPT_DIALOG (dialog));
+	script_name = e_signature_script_dialog_get_script_name (
+		E_SIGNATURE_SCRIPT_DIALOG (dialog));
+
+	g_free (signature->filename);
+	signature->filename = g_file_get_path (script_file);
+
+	g_free (signature->name);
+	signature->name = g_strdup (script_name);
+
+	g_object_unref (script_file);
+
+	success = TRUE;
+
+exit:
+	gtk_widget_destroy (dialog);
+
+	return success;
+}
+
+static void
+signature_manager_selection_changed_cb (ESignatureManager *manager,
+                                        GtkTreeSelection *selection)
+{
+	GtkWidget *edit_button;
+	GtkWidget *remove_button;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	edit_button = manager->priv->edit_button;
+	remove_button = manager->priv->remove_button;
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gtk_widget_set_sensitive (edit_button, TRUE);
+		gtk_widget_set_sensitive (remove_button, TRUE);
+	} else {
+		gtk_widget_set_sensitive (edit_button, FALSE);
+		gtk_widget_set_sensitive (remove_button, FALSE);
+	}
+}
+
+static void
+signature_manager_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ALLOW_SCRIPTS:
+			e_signature_manager_set_allow_scripts (
+				E_SIGNATURE_MANAGER (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_PREFER_HTML:
+			e_signature_manager_set_prefer_html (
+				E_SIGNATURE_MANAGER (object),
+				g_value_get_boolean (value));
+			return;
+
+		case PROP_SIGNATURE_LIST:
+			e_signature_manager_set_signature_list (
+				E_SIGNATURE_MANAGER (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_manager_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ALLOW_SCRIPTS:
+			g_value_set_boolean (
+				value,
+				e_signature_manager_get_allow_scripts (
+				E_SIGNATURE_MANAGER (object)));
+			return;
+
+		case PROP_PREFER_HTML:
+			g_value_set_boolean (
+				value,
+				e_signature_manager_get_prefer_html (
+				E_SIGNATURE_MANAGER (object)));
+			return;
+
+		case PROP_SIGNATURE_LIST:
+			g_value_set_object (
+				value,
+				e_signature_manager_get_signature_list (
+				E_SIGNATURE_MANAGER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_manager_dispose (GObject *object)
+{
+	ESignatureManagerPrivate *priv;
+
+	priv = E_SIGNATURE_MANAGER_GET_PRIVATE (object);
+
+	if (priv->signature_list != NULL) {
+		g_object_unref (priv->signature_list);
+		priv->signature_list = NULL;
+	}
+
+	if (priv->tree_view != NULL) {
+		g_object_unref (priv->tree_view);
+		priv->tree_view = NULL;
+	}
+
+	if (priv->add_button != NULL) {
+		g_object_unref (priv->add_button);
+		priv->add_button = NULL;
+	}
+
+	if (priv->add_script_button != NULL) {
+		g_object_unref (priv->add_script_button);
+		priv->add_script_button = NULL;
+	}
+
+	if (priv->edit_button != NULL) {
+		g_object_unref (priv->edit_button);
+		priv->edit_button = NULL;
+	}
+
+	if (priv->remove_button != NULL) {
+		g_object_unref (priv->remove_button);
+		priv->remove_button = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+signature_manager_add_signature (ESignatureManager *manager)
+{
+	ESignatureTreeView *tree_view;
+	GtkWidget *editor;
+
+	tree_view = e_signature_manager_get_tree_view (manager);
+
+	editor = e_signature_editor_new ();
+	gtkhtml_editor_set_html_mode (
+		GTKHTML_EDITOR (editor), manager->priv->prefer_html);
+	signature_manager_emit_editor_created (manager, editor);
+
+	gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+}
+
+static void
+signature_manager_add_signature_script (ESignatureManager *manager)
+{
+	ESignatureTreeView *tree_view;
+	ESignatureList *signature_list;
+	ESignature *signature;
+	const gchar *title;
+
+	title = _("Add Signature Script");
+	tree_view = e_signature_manager_get_tree_view (manager);
+	signature_list = e_signature_manager_get_signature_list (manager);
+
+	signature = e_signature_new ();
+	signature->script = TRUE;
+	signature->html = TRUE;
+
+	if (signature_manager_run_script_dialog (manager, signature, title))
+		e_signature_list_add (signature_list, signature);
+
+	e_signature_list_save (signature_list);
+	g_object_unref (signature);
+
+	gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+}
+
+static void
+signature_manager_editor_created (ESignatureManager *manager,
+                                  ESignatureEditor *editor)
+{
+	GtkWindowPosition position;
+	gpointer parent;
+
+	position = GTK_WIN_POS_CENTER_ON_PARENT;
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (manager));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	gtk_window_set_transient_for (GTK_WINDOW (editor), parent);
+	gtk_window_set_position (GTK_WINDOW (editor), position);
+	gtk_widget_show (GTK_WIDGET (editor));
+}
+
+static void
+signature_manager_edit_signature (ESignatureManager *manager)
+{
+	ESignatureTreeView *tree_view;
+	ESignatureList *signature_list;
+	ESignature *signature;
+	GtkWidget *editor;
+	const gchar *title;
+	gchar *filename;
+
+	tree_view = e_signature_manager_get_tree_view (manager);
+	signature = e_signature_tree_view_get_selected (tree_view);
+	signature_list = e_signature_manager_get_signature_list (manager);
+
+	if (signature == NULL)
+		return;
+
+	if (signature->script)
+		goto script;
+
+	filename = signature->filename;
+	if (filename == NULL || *filename == '\0') {
+		g_free (filename);
+		filename = g_strdup (_("Unnamed"));
+		signature->filename = filename;
+	}
+
+	editor = e_signature_editor_new ();
+	e_signature_editor_set_signature (
+		E_SIGNATURE_EDITOR (editor), signature);
+	signature_manager_emit_editor_created (manager, editor);
+
+	goto exit;
+
+script:
+	title = _("Edit Signature Script");
+
+	if (signature_manager_run_script_dialog (manager, signature, title))
+		e_signature_list_change (signature_list, signature);
+
+	e_signature_list_save (signature_list);
+
+exit:
+	gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+
+	g_object_unref (signature);
+}
+
+static void
+signature_manager_remove_signature (ESignatureManager *manager)
+{
+	ESignatureTreeView *tree_view;
+	ESignatureList *signature_list;
+	ESignature *signature;
+
+	tree_view = e_signature_manager_get_tree_view (manager);
+	signature = e_signature_tree_view_get_selected (tree_view);
+	signature_list = e_signature_tree_view_get_signature_list (tree_view);
+
+	if (signature == NULL)
+		return;
+
+	if (signature->filename != NULL && !signature->script)
+		g_unlink (signature->filename);
+
+	e_signature_list_remove (signature_list, signature);
+	e_signature_list_save (signature_list);
+
+	gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+}
+
+static void
+signature_manager_class_init (ESignatureManagerClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (ESignatureManagerPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = signature_manager_set_property;
+	object_class->get_property = signature_manager_get_property;
+	object_class->dispose = signature_manager_dispose;
+
+	class->add_signature = signature_manager_add_signature;
+	class->add_signature_script = signature_manager_add_signature_script;
+	class->editor_created = signature_manager_editor_created;
+	class->edit_signature = signature_manager_edit_signature;
+	class->remove_signature = signature_manager_remove_signature;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ALLOW_SCRIPTS,
+		g_param_spec_boolean (
+			"allow-scripts",
+			"Allow Scripts",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PREFER_HTML,
+		g_param_spec_boolean (
+			"prefer-html",
+			"Prefer HTML",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNATURE_LIST,
+		g_param_spec_object (
+			"signature-list",
+			"Signature List",
+			NULL,
+			E_TYPE_SIGNATURE_LIST,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	signals[ADD_SIGNATURE] = g_signal_new (
+		"add-signature",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (ESignatureManagerClass, add_signature),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[ADD_SIGNATURE_SCRIPT] = g_signal_new (
+		"add-signature-script",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (ESignatureManagerClass, add_signature_script),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[EDITOR_CREATED] = g_signal_new (
+		"editor-created",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (ESignatureManagerClass, editor_created),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		E_TYPE_SIGNATURE_EDITOR);
+
+	signals[EDIT_SIGNATURE] = g_signal_new (
+		"edit-signature",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (ESignatureManagerClass, edit_signature),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[REMOVE_SIGNATURE] = g_signal_new (
+		"remove-signature",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (ESignatureManagerClass, remove_signature),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+static void
+signature_manager_init (ESignatureManager *manager)
+{
+	GtkTreeSelection *selection;
+	GtkWidget *container;
+	GtkWidget *widget;
+
+	manager->priv = E_SIGNATURE_MANAGER_GET_PRIVATE (manager);
+
+	gtk_table_resize (GTK_TABLE (manager), 1, 2);
+	gtk_table_set_col_spacings (GTK_TABLE (manager), 6);
+	gtk_table_set_row_spacings (GTK_TABLE (manager), 12);
+
+	container = GTK_WIDGET (manager);
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_table_attach (
+		GTK_TABLE (container), widget, 0, 1, 0, 1,
+		GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_signature_tree_view_new ();
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	manager->priv->tree_view = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	e_mutual_binding_new (
+		G_OBJECT (manager), "signature-list",
+		G_OBJECT (widget), "signature-list");
+
+	g_signal_connect_swapped (
+		widget, "key-press-event",
+		G_CALLBACK (signature_manager_key_press_event_cb),
+		manager);
+
+	g_signal_connect_swapped (
+		widget, "row-activated",
+		G_CALLBACK (e_signature_manager_edit_signature),
+		manager);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+
+	g_signal_connect_swapped (
+		selection, "changed",
+		G_CALLBACK (signature_manager_selection_changed_cb),
+		manager);
+
+	container = GTK_WIDGET (manager);
+
+	widget = gtk_vbutton_box_new ();
+	gtk_button_box_set_layout (
+		GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
+	gtk_box_set_spacing (GTK_BOX (widget), 6);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 0, 2, 0, GTK_FILL, 0, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_button_new_from_stock (GTK_STOCK_ADD);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->add_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_signature_manager_add_signature),
+		manager);
+
+	widget = gtk_button_new_with_mnemonic (_("Add _Script"));
+	gtk_button_set_image (
+		GTK_BUTTON (widget), gtk_image_new_from_stock (
+		GTK_STOCK_EXECUTE, GTK_ICON_SIZE_BUTTON));
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->add_script_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	e_binding_new (
+		G_OBJECT (manager), "allow-scripts",
+		G_OBJECT (widget), "sensitive");
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_signature_manager_add_signature_script),
+		manager);
+
+	widget = gtk_button_new_from_stock (GTK_STOCK_EDIT);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->edit_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_signature_manager_edit_signature),
+		manager);
+
+	widget = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->remove_button = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_signature_manager_remove_signature),
+		manager);
+}
+
+GType
+e_signature_manager_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (ESignatureManagerClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) signature_manager_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_init */
+			sizeof (ESignatureManager),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) signature_manager_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_TABLE, "ESignatureManager", &type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_signature_manager_new (ESignatureList *signature_list)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_LIST (signature_list), NULL);
+
+	return g_object_new (
+		E_TYPE_SIGNATURE_MANAGER,
+		"signature-list", signature_list, NULL);
+}
+
+void
+e_signature_manager_add_signature (ESignatureManager *manager)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	g_signal_emit (manager, signals[ADD_SIGNATURE], 0);
+}
+
+void
+e_signature_manager_add_signature_script (ESignatureManager *manager)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	g_signal_emit (manager, signals[ADD_SIGNATURE_SCRIPT], 0);
+}
+
+void
+e_signature_manager_edit_signature (ESignatureManager *manager)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	g_signal_emit (manager, signals[EDIT_SIGNATURE], 0);
+}
+
+void
+e_signature_manager_remove_signature (ESignatureManager *manager)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	g_signal_emit (manager, signals[REMOVE_SIGNATURE], 0);
+}
+
+gboolean
+e_signature_manager_get_allow_scripts (ESignatureManager *manager)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_MANAGER (manager), FALSE);
+
+	return manager->priv->allow_scripts;
+}
+
+void
+e_signature_manager_set_allow_scripts (ESignatureManager *manager,
+                                       gboolean allow_scripts)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	manager->priv->allow_scripts = allow_scripts;
+
+	g_object_notify (G_OBJECT (manager), "allow-scripts");
+}
+
+gboolean
+e_signature_manager_get_prefer_html (ESignatureManager *manager)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_MANAGER (manager), FALSE);
+
+	return manager->priv->prefer_html;
+}
+
+void
+e_signature_manager_set_prefer_html (ESignatureManager *manager,
+                                     gboolean prefer_html)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	manager->priv->prefer_html = prefer_html;
+
+	g_object_notify (G_OBJECT (manager), "prefer-html");
+}
+
+ESignatureList *
+e_signature_manager_get_signature_list (ESignatureManager *manager)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_MANAGER (manager), NULL);
+
+	return manager->priv->signature_list;
+}
+
+void
+e_signature_manager_set_signature_list (ESignatureManager *manager,
+                                        ESignatureList *signature_list)
+{
+	g_return_if_fail (E_IS_SIGNATURE_MANAGER (manager));
+
+	if (signature_list != NULL) {
+		g_return_if_fail (E_IS_SIGNATURE_LIST (signature_list));
+		g_object_ref (signature_list);
+	}
+
+	if (manager->priv->signature_list != NULL)
+		g_object_unref (manager->priv->signature_list);
+
+	manager->priv->signature_list = signature_list;
+
+	g_object_notify (G_OBJECT (manager), "signature-list");
+}
+
+ESignatureTreeView *
+e_signature_manager_get_tree_view (ESignatureManager *manager)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_MANAGER (manager), NULL);
+
+	return E_SIGNATURE_TREE_VIEW (manager->priv->tree_view);
+}
diff --git a/widgets/misc/e-signature-manager.h b/widgets/misc/e-signature-manager.h
new file mode 100644
index 0000000000..6c182badab
--- /dev/null
+++ b/widgets/misc/e-signature-manager.h
@@ -0,0 +1,100 @@
+/*
+ * e-signature-manager.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_MANAGER_H
+#define E_SIGNATURE_MANAGER_H
+
+#include 
+#include 
+#include 
+#include 
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE_MANAGER \
+	(e_signature_manager_get_type ())
+#define E_SIGNATURE_MANAGER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNATURE_MANAGER, ESignatureManager))
+#define E_SIGNATURE_MANAGER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNATURE_MANAGER, ESignatureManagerClass))
+#define E_IS_SIGNATURE_MANAGER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNATURE_MANAGER))
+#define E_IS_SIGNATURE_MANAGER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNATURE_MANAGER))
+#define E_SIGNATURE_MANAGER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNATURE_MANAGER, ESignatureManagerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignatureManager ESignatureManager;
+typedef struct _ESignatureManagerClass ESignatureManagerClass;
+typedef struct _ESignatureManagerPrivate ESignatureManagerPrivate;
+
+struct _ESignatureManager {
+	GtkTable parent;
+	ESignatureManagerPrivate *priv;
+};
+
+struct _ESignatureManagerClass {
+	GtkTableClass parent_class;
+
+	void		(*add_signature)	(ESignatureManager *manager);
+	void		(*add_signature_script)	(ESignatureManager *manager);
+	void		(*editor_created)	(ESignatureManager *manager,
+						 ESignatureEditor *editor);
+	void		(*edit_signature)	(ESignatureManager *manager);
+	void		(*remove_signature)	(ESignatureManager *manager);
+};
+
+GType		e_signature_manager_get_type	(void);
+GtkWidget *	e_signature_manager_new		(ESignatureList *signature_list);
+void		e_signature_manager_add_signature
+						(ESignatureManager *manager);
+void		e_signature_manager_add_signature_script
+						(ESignatureManager *manager);
+void		e_signature_manager_edit_signature
+						(ESignatureManager *manager);
+void		e_signature_manager_remove_signature
+						(ESignatureManager *manager);
+gboolean	e_signature_manager_get_allow_scripts
+						(ESignatureManager *manager);
+void		e_signature_manager_set_allow_scripts
+						(ESignatureManager *manager,
+						 gboolean allow_scripts);
+gboolean	e_signature_manager_get_prefer_html
+						(ESignatureManager *manager);
+void		e_signature_manager_set_prefer_html
+						(ESignatureManager *manager,
+						 gboolean prefer_html);
+ESignatureList *e_signature_manager_get_signature_list
+						(ESignatureManager *manager);
+void		e_signature_manager_set_signature_list
+						(ESignatureManager *manager,
+						 ESignatureList *signature_list);
+ESignatureTreeView *
+		e_signature_manager_get_tree_view
+						(ESignatureManager *manager);
+
+#endif /* E_SIGNATURE_MANAGER_H */
diff --git a/widgets/misc/e-signature-script-dialog.c b/widgets/misc/e-signature-script-dialog.c
new file mode 100644
index 0000000000..06e021b046
--- /dev/null
+++ b/widgets/misc/e-signature-script-dialog.c
@@ -0,0 +1,463 @@
+/*
+ * e-signature-script-dialog.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-signature-script-dialog.h"
+
+#include 
+#include "e-util/e-binding.h"
+
+#define E_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SIGNATURE_SCRIPT_DIALOG, ESignatureScriptDialogPrivate))
+
+struct _ESignatureScriptDialogPrivate {
+	GtkWidget *entry;
+	GtkWidget *file_chooser;
+	GtkWidget *alert;
+};
+
+enum {
+	PROP_0,
+	PROP_SCRIPT_FILE,
+	PROP_SCRIPT_NAME
+};
+
+static gpointer parent_class;
+
+static gboolean
+signature_script_dialog_filter_cb (const GtkFileFilterInfo *filter_info)
+{
+	const gchar *filename = filter_info->filename;
+
+	return g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE);
+}
+
+static void
+signature_script_dialog_update_status (ESignatureScriptDialog *dialog)
+{
+	GFile *script_file;
+	const gchar *script_name;
+	gboolean show_alert;
+	gboolean sensitive;
+
+	script_file = e_signature_script_dialog_get_script_file (dialog);
+	script_name = e_signature_script_dialog_get_script_name (dialog);
+
+	sensitive = (script_name != NULL && *script_name != '\0');
+
+	if (script_file != NULL) {
+		gboolean executable;
+		gchar *filename;
+
+		filename = g_file_get_path (script_file);
+		executable = g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE);
+		g_free (filename);
+
+		show_alert = !executable;
+		sensitive &= executable;
+
+		g_object_unref (script_file);
+	} else {
+		sensitive = FALSE;
+		show_alert = FALSE;
+	}
+
+	if (show_alert)
+		gtk_widget_show (dialog->priv->alert);
+	else
+		gtk_widget_hide (dialog->priv->alert);
+
+	gtk_dialog_set_response_sensitive (
+		GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive);
+}
+
+static void
+signature_script_dialog_set_property (GObject *object,
+                                      guint property_id,
+                                      const GValue *value,
+                                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SCRIPT_FILE:
+			e_signature_script_dialog_set_script_file (
+				E_SIGNATURE_SCRIPT_DIALOG (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_SCRIPT_NAME:
+			e_signature_script_dialog_set_script_name (
+				E_SIGNATURE_SCRIPT_DIALOG (object),
+				g_value_get_string (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_script_dialog_get_property (GObject *object,
+                                      guint property_id,
+                                      GValue *value,
+                                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SCRIPT_FILE:
+			g_value_set_object (
+				value,
+				e_signature_script_dialog_get_script_file (
+				E_SIGNATURE_SCRIPT_DIALOG (object)));
+			return;
+
+		case PROP_SCRIPT_NAME:
+			g_value_set_string (
+				value,
+				e_signature_script_dialog_get_script_name (
+				E_SIGNATURE_SCRIPT_DIALOG (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_script_dialog_dispose (GObject *object)
+{
+	ESignatureScriptDialogPrivate *priv;
+
+	priv = E_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE (object);
+
+	if (priv->entry != NULL) {
+		g_object_unref (priv->entry);
+		priv->entry = NULL;
+	}
+
+	if (priv->file_chooser != NULL) {
+		g_object_unref (priv->file_chooser);
+		priv->file_chooser = NULL;
+	}
+
+	if (priv->alert != NULL) {
+		g_object_unref (priv->alert);
+		priv->alert = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+signature_script_dialog_map (GtkWidget *widget)
+{
+	GtkWidget *action_area;
+	GtkWidget *content_area;
+
+	/* Chain up to parent's map() method. */
+	GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+	/* XXX Override GtkDialog's broken style property defaults. */
+	action_area = gtk_dialog_get_action_area (GTK_DIALOG (widget));
+	content_area = gtk_dialog_get_content_area (GTK_DIALOG (widget));
+
+	gtk_box_set_spacing (GTK_BOX (content_area), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
+	gtk_container_set_border_width (GTK_CONTAINER (content_area), 12);
+}
+
+static void
+signature_script_dialog_class_init (ESignatureScriptDialogClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (ESignatureScriptDialogPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = signature_script_dialog_set_property;
+	object_class->get_property = signature_script_dialog_get_property;
+	object_class->dispose = signature_script_dialog_dispose;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->map = signature_script_dialog_map;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SCRIPT_FILE,
+		g_param_spec_object (
+			"script-file",
+			"Script File",
+			NULL,
+			G_TYPE_FILE,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SCRIPT_NAME,
+		g_param_spec_string (
+			"script-name",
+			"Script Name",
+			NULL,
+			_("Unnamed"),
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+}
+
+static void
+signature_script_dialog_init (ESignatureScriptDialog *dialog)
+{
+	GtkFileFilter *filter;
+	GtkWidget *content_area;
+	GtkWidget *container;
+	GtkWidget *widget;
+	gchar *markup;
+
+	dialog->priv = E_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE (dialog);
+
+	content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+	gtk_dialog_add_button (
+		GTK_DIALOG (dialog),
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+	gtk_dialog_add_button (
+		GTK_DIALOG (dialog),
+		GTK_STOCK_SAVE, GTK_RESPONSE_OK);
+
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	container = content_area;
+
+	widget = gtk_table_new (4, 2, FALSE);
+	gtk_table_set_col_spacings (GTK_TABLE (widget), 6);
+	gtk_table_set_row_spacings (GTK_TABLE (widget), 6);
+	gtk_table_set_row_spacing (GTK_TABLE (widget), 0, 12);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_image_new_from_stock (
+		GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 0, 1, 0, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new (_(
+		"The output of this script will be used as your\n"
+		"signature. The name you specify will be used\n"
+		"for display purposes only."));
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_entry_new ();
+	gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	dialog->priv->entry = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	widget = gtk_label_new_with_mnemonic (_("_Name:"));
+	gtk_label_set_mnemonic_widget (
+		GTK_LABEL (widget), dialog->priv->entry);
+	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_file_chooser_button_new (
+		NULL, GTK_FILE_CHOOSER_ACTION_OPEN);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	dialog->priv->file_chooser = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	/* Restrict file selection to executable files. */
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_add_custom (
+		filter, GTK_FILE_FILTER_FILENAME,
+		(GtkFileFilterFunc) signature_script_dialog_filter_cb,
+		NULL, NULL);
+	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (widget), filter);
+
+	/* XXX ESignature stores a filename instead of a URI,
+	 *     so we have to restrict it to local files only. */
+	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
+
+	widget = gtk_label_new_with_mnemonic (_("S_cript:"));
+	gtk_label_set_mnemonic_widget (
+		GTK_LABEL (widget), dialog->priv->file_chooser);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	/* This is just a placeholder. */
+	widget = gtk_label_new (NULL);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		0, 1, 3, 4, GTK_FILL, 0, 0, 0);
+	gtk_widget_show (widget);
+
+	widget = gtk_hbox_new (FALSE, 6);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 3, 4, 0, 0, 0, 0);
+	dialog->priv->alert = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_image_new_from_stock (
+		GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_MENU);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	markup = g_markup_printf_escaped (
+		"%s",
+		_("Script file must be executable."));
+	widget = gtk_label_new (markup);
+	gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+	g_free (markup);
+
+	g_signal_connect (
+		dialog, "notify::script-file",
+		G_CALLBACK (signature_script_dialog_update_status), NULL);
+
+	g_signal_connect (
+		dialog, "notify::script-name",
+		G_CALLBACK (signature_script_dialog_update_status), NULL);
+
+	g_signal_connect_swapped (
+		dialog->priv->entry, "changed",
+		G_CALLBACK (signature_script_dialog_update_status), dialog);
+
+	g_signal_connect_swapped (
+		dialog->priv->file_chooser, "file-set",
+		G_CALLBACK (signature_script_dialog_update_status), dialog);
+
+	signature_script_dialog_update_status (dialog);
+}
+
+GType
+e_signature_script_dialog_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (ESignatureScriptDialogClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) signature_script_dialog_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (ESignatureScriptDialog),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) signature_script_dialog_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_DIALOG, "ESignatureScriptDialog",
+			&type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_signature_script_dialog_new (GtkWindow *parent)
+{
+	return g_object_new (
+		E_TYPE_SIGNATURE_SCRIPT_DIALOG,
+		"transient-for", parent, NULL);
+}
+
+GFile *
+e_signature_script_dialog_get_script_file (ESignatureScriptDialog *dialog)
+{
+	GtkFileChooser *file_chooser;
+
+	g_return_val_if_fail (E_IS_SIGNATURE_SCRIPT_DIALOG (dialog), NULL);
+
+	file_chooser = GTK_FILE_CHOOSER (dialog->priv->file_chooser);
+
+	return gtk_file_chooser_get_file (file_chooser);
+}
+
+void
+e_signature_script_dialog_set_script_file (ESignatureScriptDialog *dialog,
+                                           GFile *script_file)
+{
+	GtkFileChooser *file_chooser;
+	GError *error = NULL;
+
+	g_return_if_fail (E_IS_SIGNATURE_SCRIPT_DIALOG (dialog));
+	g_return_if_fail (G_IS_FILE (script_file));
+
+	file_chooser = GTK_FILE_CHOOSER (dialog->priv->file_chooser);
+
+	if (gtk_file_chooser_set_file (file_chooser, script_file, &error))
+		g_object_notify (G_OBJECT (dialog), "script-file");
+	else {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+}
+
+const gchar *
+e_signature_script_dialog_get_script_name (ESignatureScriptDialog *dialog)
+{
+	GtkEntry *entry;
+
+	g_return_val_if_fail (E_IS_SIGNATURE_SCRIPT_DIALOG (dialog), NULL);
+
+	entry = GTK_ENTRY (dialog->priv->entry);
+
+	return gtk_entry_get_text (entry);
+}
+
+void
+e_signature_script_dialog_set_script_name (ESignatureScriptDialog *dialog,
+                                           const gchar *script_name)
+{
+	GtkEntry *entry;
+
+	g_return_if_fail (E_IS_SIGNATURE_SCRIPT_DIALOG (dialog));
+
+	if (script_name == NULL)
+		script_name = "";
+
+	entry = GTK_ENTRY (dialog->priv->entry);
+	gtk_entry_set_text (entry, script_name);
+
+	g_object_notify (G_OBJECT (dialog), "script-name");
+}
diff --git a/widgets/misc/e-signature-script-dialog.h b/widgets/misc/e-signature-script-dialog.h
new file mode 100644
index 0000000000..4cd7f05632
--- /dev/null
+++ b/widgets/misc/e-signature-script-dialog.h
@@ -0,0 +1,76 @@
+/*
+ * e-signature-script-dialog.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_SCRIPT_DIALOG_H
+#define E_SIGNATURE_SCRIPT_DIALOG_H
+
+#include 
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE_SCRIPT_DIALOG \
+	(e_signature_script_dialog_get_type ())
+#define E_SIGNATURE_SCRIPT_DIALOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNATURE_SCRIPT_DIALOG, ESignatureScriptDialog))
+#define E_SIGNATURE_SCRIPT_DIALOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNATURE_SCRIPT_DIALOG, ESignatureScriptDialogClass))
+#define E_IS_SIGNATURE_SCRIPT_DIALOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNATURE_SCRIPT_DIALOG))
+#define E_IS_SIGNATURE_SCRIPT_DIALOG_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNATURE_SCRIPT_DIALOG))
+#define E_SIGNATURE_SCRIPT_DIALOG_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNATURE_SCRIPT_DIALOG, ESignatureScriptDialogClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignatureScriptDialog ESignatureScriptDialog;
+typedef struct _ESignatureScriptDialogClass ESignatureScriptDialogClass;
+typedef struct _ESignatureScriptDialogPrivate ESignatureScriptDialogPrivate;
+
+struct _ESignatureScriptDialog {
+	GtkDialog parent;
+	ESignatureScriptDialogPrivate *priv;
+};
+
+struct _ESignatureScriptDialogClass {
+	GtkDialogClass parent_class;
+};
+
+GType		e_signature_script_dialog_get_type	(void);
+GtkWidget *	e_signature_script_dialog_new		(GtkWindow *parent);
+GFile *		e_signature_script_dialog_get_script_file
+					(ESignatureScriptDialog *dialog);
+void		e_signature_script_dialog_set_script_file
+					(ESignatureScriptDialog *dialog,
+					 GFile *script_file);
+const gchar *	e_signature_script_dialog_get_script_name
+					(ESignatureScriptDialog *dialog);
+void		e_signature_script_dialog_set_script_name
+					(ESignatureScriptDialog *dialog,
+					 const gchar *script_name);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_SCRIPT_DIALOG_H */
diff --git a/widgets/misc/e-signature-tree-view.c b/widgets/misc/e-signature-tree-view.c
new file mode 100644
index 0000000000..ef1cc36b14
--- /dev/null
+++ b/widgets/misc/e-signature-tree-view.c
@@ -0,0 +1,405 @@
+/*
+ * e-signature-tree-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-signature-tree-view.h"
+
+#define E_SIGNATURE_TREE_VIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_SIGNATURE_TREE_VIEW, ESignatureTreeViewPrivate))
+
+enum {
+	COLUMN_STRING,
+	COLUMN_SIGNATURE
+};
+
+enum {
+	PROP_0,
+	PROP_SIGNATURE_LIST
+};
+
+enum {
+	REFRESHED,
+	LAST_SIGNAL
+};
+
+struct _ESignatureTreeViewPrivate {
+	ESignatureList *signature_list;
+	GHashTable *index;
+};
+
+static gpointer parent_class;
+static guint signal_ids[LAST_SIGNAL];
+
+static void
+signature_tree_view_refresh_cb (ESignatureList *signature_list,
+                                ESignature *unused,
+                                ESignatureTreeView *tree_view)
+{
+	GtkListStore *store;
+	GtkTreeModel *model;
+	GtkTreeIter tree_iter;
+	EIterator *signature_iter;
+	ESignature *signature;
+	GHashTable *index;
+	GList *list = NULL;
+	GList *iter;
+
+	store = gtk_list_store_new (2, G_TYPE_STRING, E_TYPE_SIGNATURE);
+	model = GTK_TREE_MODEL (store);
+	index = tree_view->priv->index;
+
+	g_hash_table_remove_all (index);
+
+	if (signature_list == NULL)
+		goto skip;
+
+	/* Build a list of ESignatures to display. */
+	signature_iter = e_list_get_iterator (E_LIST (signature_list));
+	while (e_iterator_is_valid (signature_iter)) {
+
+		/* XXX EIterator misuses const. */
+		signature = (ESignature *) e_iterator_get (signature_iter);
+		list = g_list_prepend (list, signature);
+		e_iterator_next (signature_iter);
+	}
+	g_object_unref (signature_iter);
+
+	list = g_list_reverse (list);
+
+	/* Populate the list store and index. */
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GtkTreeRowReference *reference;
+		GtkTreePath *path;
+
+		signature = iter->data;
+
+		/* Skip autogenerated signatures. */
+		if (signature->autogen)
+			continue;
+
+		gtk_list_store_append (store, &tree_iter);
+		gtk_list_store_set (
+			store, &tree_iter,
+			COLUMN_STRING, signature->name,
+			COLUMN_SIGNATURE, signature, -1);
+
+		path = gtk_tree_model_get_path (model, &tree_iter);
+		reference = gtk_tree_row_reference_new (model, path);
+		g_hash_table_insert (index, signature, reference);
+		gtk_tree_path_free (path);
+	}
+
+skip:
+	/* Restore the previously selected signature. */
+	signature = e_signature_tree_view_get_selected (tree_view);
+	if (signature != NULL)
+		g_object_ref (signature);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), model);
+	e_signature_tree_view_set_selected (tree_view, signature);
+	if (signature != NULL)
+		g_object_unref (signature);
+
+	g_signal_emit (tree_view, signal_ids[REFRESHED], 0);
+}
+
+static GObject *
+signature_tree_view_constructor (GType type,
+                                 guint n_construct_properties,
+                                 GObjectConstructParam *construct_properties)
+{
+	GObject *object;
+	GtkTreeView *tree_view;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+
+	/* Chain up to parent's constructor() method. */
+	object = G_OBJECT_CLASS (parent_class)->constructor (
+		type, n_construct_properties, construct_properties);
+
+	tree_view = GTK_TREE_VIEW (object);
+	gtk_tree_view_set_headers_visible (tree_view, FALSE);
+
+	column = gtk_tree_view_column_new ();
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_add_attribute (
+		column, renderer, "text", COLUMN_STRING);
+	gtk_tree_view_append_column (tree_view, column);
+
+	return object;
+}
+
+static void
+signature_tree_view_set_property (GObject *object,
+                                  guint property_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SIGNATURE_LIST:
+			e_signature_tree_view_set_signature_list (
+				E_SIGNATURE_TREE_VIEW (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_tree_view_get_property (GObject *object,
+                                  guint property_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_SIGNATURE_LIST:
+			g_value_set_object (
+				value,
+				e_signature_tree_view_get_signature_list (
+				E_SIGNATURE_TREE_VIEW (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+signature_tree_view_dispose (GObject *object)
+{
+	ESignatureTreeViewPrivate *priv;
+
+	priv = E_SIGNATURE_TREE_VIEW_GET_PRIVATE (object);
+
+	if (priv->signature_list != NULL) {
+		g_signal_handlers_disconnect_by_func (
+			priv->signature_list,
+			signature_tree_view_refresh_cb, object);
+		g_object_unref (priv->signature_list);
+		priv->signature_list = NULL;
+	}
+
+	g_hash_table_remove_all (priv->index);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+signature_tree_view_finalize (GObject *object)
+{
+	ESignatureTreeViewPrivate *priv;
+
+	priv = E_SIGNATURE_TREE_VIEW_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->index);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+signature_tree_view_class_init (ESignatureTreeViewClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (ESignatureTreeViewPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->constructor = signature_tree_view_constructor;
+	object_class->set_property = signature_tree_view_set_property;
+	object_class->get_property = signature_tree_view_get_property;
+	object_class->dispose = signature_tree_view_dispose;
+	object_class->finalize = signature_tree_view_finalize;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SIGNATURE_LIST,
+		g_param_spec_object (
+			"signature-list",
+			"Signature List",
+			NULL,
+			E_TYPE_SIGNATURE_LIST,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
+
+	signal_ids[REFRESHED] = g_signal_new (
+		"refreshed",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		0, NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+static void
+signature_tree_view_init (ESignatureTreeView *tree_view)
+{
+	GHashTable *index;
+
+	/* Reverse-lookup index */
+	index = g_hash_table_new_full (
+		g_direct_hash, g_direct_equal,
+		(GDestroyNotify) g_object_unref,
+		(GDestroyNotify) gtk_tree_row_reference_free);
+
+	tree_view->priv = E_SIGNATURE_TREE_VIEW_GET_PRIVATE (tree_view);
+	tree_view->priv->index = index;
+}
+
+GType
+e_signature_tree_view_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (ESignatureTreeViewClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) signature_tree_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (ESignatureTreeView),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) signature_tree_view_init,
+			NULL  /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_TREE_VIEW, "ESignatureTreeView",
+			&type_info, 0);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_signature_tree_view_new (void)
+{
+	return g_object_new (E_TYPE_SIGNATURE_TREE_VIEW, NULL);
+}
+
+ESignatureList *
+e_signature_tree_view_get_signature_list (ESignatureTreeView *tree_view)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_TREE_VIEW (tree_view), NULL);
+
+	return tree_view->priv->signature_list;
+}
+
+void
+e_signature_tree_view_set_signature_list (ESignatureTreeView *tree_view,
+                                          ESignatureList *signature_list)
+{
+	ESignatureTreeViewPrivate *priv;
+
+	g_return_if_fail (E_IS_SIGNATURE_TREE_VIEW (tree_view));
+
+	if (signature_list != NULL)
+		g_return_if_fail (E_IS_SIGNATURE_LIST (signature_list));
+
+	priv = E_SIGNATURE_TREE_VIEW_GET_PRIVATE (tree_view);
+
+	if (priv->signature_list != NULL) {
+		g_signal_handlers_disconnect_by_func (
+			priv->signature_list,
+			signature_tree_view_refresh_cb, tree_view);
+		g_object_unref (priv->signature_list);
+		priv->signature_list = NULL;
+	}
+
+	if (signature_list != NULL) {
+		priv->signature_list = g_object_ref (signature_list);
+
+		/* Listen for changes to the signature list. */
+		g_signal_connect (
+			priv->signature_list, "signature-added",
+			G_CALLBACK (signature_tree_view_refresh_cb),
+			tree_view);
+		g_signal_connect (
+			priv->signature_list, "signature-changed",
+			G_CALLBACK (signature_tree_view_refresh_cb),
+			tree_view);
+		g_signal_connect (
+			priv->signature_list, "signature-removed",
+			G_CALLBACK (signature_tree_view_refresh_cb),
+			tree_view);
+	}
+
+	signature_tree_view_refresh_cb (signature_list, NULL, tree_view);
+
+	g_object_notify (G_OBJECT (tree_view), "signature-list");
+}
+
+ESignature *
+e_signature_tree_view_get_selected (ESignatureTreeView *tree_view)
+{
+	ESignature *signature;
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	g_return_val_if_fail (E_IS_SIGNATURE_TREE_VIEW (tree_view), NULL);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+		return NULL;
+
+	gtk_tree_model_get (model, &iter, COLUMN_SIGNATURE, &signature, -1);
+
+	return signature;
+}
+
+gboolean
+e_signature_tree_view_set_selected (ESignatureTreeView *tree_view,
+                                    ESignature *signature)
+{
+	GtkTreeRowReference *reference;
+	GtkTreeSelection *selection;
+	GtkTreePath *path;
+
+	g_return_val_if_fail (E_IS_SIGNATURE_TREE_VIEW (tree_view), FALSE);
+
+	if (signature != NULL)
+		g_return_val_if_fail (E_IS_SIGNATURE (signature), FALSE);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+	/* NULL means clear the selection. */
+	if (signature == NULL) {
+		gtk_tree_selection_unselect_all (selection);
+		return TRUE;
+	}
+
+	/* Lookup the tree row reference for the signature. */
+	reference = g_hash_table_lookup (tree_view->priv->index, signature);
+	if (reference == NULL)
+		return FALSE;
+
+	/* Select the referenced path. */
+	path = gtk_tree_row_reference_get_path (reference);
+	gtk_tree_selection_select_path (selection, path);
+	gtk_tree_path_free (path);
+
+	return TRUE;
+}
diff --git a/widgets/misc/e-signature-tree-view.h b/widgets/misc/e-signature-tree-view.h
new file mode 100644
index 0000000000..3afe569136
--- /dev/null
+++ b/widgets/misc/e-signature-tree-view.h
@@ -0,0 +1,78 @@
+/*
+ * e-signature-tree-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see   
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SIGNATURE_TREE_VIEW_H
+#define E_SIGNATURE_TREE_VIEW_H
+
+#include 
+#include 
+#include 
+
+/* Standard GObject macros */
+#define E_TYPE_SIGNATURE_TREE_VIEW \
+	(e_signature_tree_view_get_type ())
+#define E_SIGNATURE_TREE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SIGNATURE_TREE_VIEW, ESignatureTreeView))
+#define E_SIGNATURE_TREE_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SIGNATURE_TREE_VIEW, ESignatureTreeViewClass))
+#define E_IS_SIGNATURE_TREE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SIGNATURE_TREE_VIEW))
+#define E_IS_SIGNATURE_TREE_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SIGNATURE_TREE_VIEW))
+#define E_SIGNATURE_TREE_VIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SIGNATURE_TREE_VIEW, ESignatureTreeViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESignatureTreeView ESignatureTreeView;
+typedef struct _ESignatureTreeViewClass ESignatureTreeViewClass;
+typedef struct _ESignatureTreeViewPrivate ESignatureTreeViewPrivate;
+
+struct _ESignatureTreeView {
+	GtkTreeView parent;
+	ESignatureTreeViewPrivate *priv;
+};
+
+struct _ESignatureTreeViewClass {
+	GtkTreeViewClass parent_class;
+};
+
+GType		e_signature_tree_view_get_type	(void);
+GtkWidget *	e_signature_tree_view_new	(void);
+ESignatureList *e_signature_tree_view_get_signature_list
+					(ESignatureTreeView *tree_view);
+void		e_signature_tree_view_set_signature_list
+					(ESignatureTreeView *tree_view,
+					 ESignatureList *signature_list);
+ESignature *	e_signature_tree_view_get_selected
+					(ESignatureTreeView *tree_view);
+gboolean	e_signature_tree_view_set_selected
+					(ESignatureTreeView *tree_view,
+					 ESignature *signature);
+
+G_END_DECLS
+
+#endif /* E_SIGNATURE_TREE_VIEW_H */
-- 
cgit v1.2.3