[evolution/kill-bonobo: 58/58] Merge branch 'master' into kill-bonobo



commit 1351c8e4fb443a9705bb1225c3c574c05a36f8ca
Merge: 624f481... 42e75c9...
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Jul 1 10:47:10 2009 -0400

    Merge branch 'master' into kill-bonobo

 NEWS                                            |   48 +
 addressbook/gui/contact-editor/Makefile.am      |    4 +
 addressbook/gui/contact-editor/test-editor.c    |   23 +-
 addressbook/gui/contact-list-editor/Makefile.am |    4 +
 calendar/gui/e-day-view-main-item.c             |   19 +-
 calendar/gui/e-day-view.c                       |    2 -
 calendar/gui/gnome-cal.c                        |   20 +-
 calendar/gui/tag-calendar.c                     |   14 +-
 composer/e-composer-header.c                    |   11 +-
 composer/e-msg-composer.h                       |    2 +-
 configure.ac                                    |    7 +-
 em-format/em-format.c                           |   68 +
 filter/rule-editor.c                            |    4 +-
 help/cs/figures/evo_adv_search_a.png            |  Bin 0 -> 45652 bytes
 help/cs/figures/evo_cal_advsearch.png           |  Bin 0 -> 33530 bytes
 help/cs/figures/evo_contacteditor_a.png         |  Bin 0 -> 66489 bytes
 help/cs/figures/evo_edit_rule_a.png             |  Bin 0 -> 36591 bytes
 help/cs/figures/evo_edit_search.png             |  Bin 0 -> 30429 bytes
 help/cs/figures/evo_flag_follow_up_a.png        |  Bin 0 -> 36029 bytes
 help/cs/figures/evo_googlecontacts.png          |  Bin 0 -> 32531 bytes
 help/cs/figures/evo_gwreceiveedit_a.png         |  Bin 0 -> 46054 bytes
 help/cs/figures/evo_gwreceiveoptedit_a.png      |  Bin 0 -> 55437 bytes
 help/cs/figures/evo_gwstatustrack.png           |  Bin 0 -> 48756 bytes
 help/cs/figures/evo_identityedit_a.png          |  Bin 0 -> 51481 bytes
 help/cs/figures/evo_imapheader_a.png            |  Bin 0 -> 56395 bytes
 help/cs/figures/evo_junk_a.png                  |  Bin 0 -> 127789 bytes
 help/cs/figures/evo_message_filters_a.png       |  Bin 0 -> 27880 bytes
 help/cs/figures/evo_rule_a.png                  |  Bin 0 -> 58395 bytes
 help/cs/figures/evo_select_add_folder.png       |  Bin 0 -> 27657 bytes
 help/cs/figures/evo_select_folder.png           |  Bin 0 -> 28730 bytes
 help/cs/figures/evo_send_option_a.png           |  Bin 0 -> 34360 bytes
 help/cs/figures/evo_sendstatus_a.png            |  Bin 0 -> 49744 bytes
 help/cs/figures/evo_shd_memo_a.png              |  Bin 0 -> 31919 bytes
 help/cs/figures/exchg-identity.png              |  Bin 0 -> 53312 bytes
 help/cs/figures/exchng-identity.png             |  Bin 0 -> 46026 bytes
 help/cs/figures/exchng-rec-mail.png             |  Bin 0 -> 49293 bytes
 help/cs/figures/exchng-rec-option.png           |  Bin 0 -> 69585 bytes
 help/cs/figures/exchng-settings.png             |  Bin 0 -> 53388 bytes
 help/cs/figures/filter-new-fig.png              |  Bin 0 -> 36299 bytes
 help/cs/figures/google_cal_view.png             |  Bin 0 -> 37394 bytes
 help/cs/figures/mailer_preferences.png          |  Bin 0 -> 114131 bytes
 help/cs/figures/network_pref.png                |  Bin 0 -> 73921 bytes
 help/cs/figures/quick_add_a.png                 |  Bin 0 -> 20224 bytes
 help/cs/figures/quick_reference.png             |  Bin 0 -> 86166 bytes
 help/de/de.po                                   |  350 +--
 help/de/figures/account_editor_a.png            |  Bin 0 -> 73799 bytes
 help/de/figures/evo_flag_follow_up_a.png        |  Bin 0 -> 35188 bytes
 help/de/figures/evo_newmail.png                 |  Bin 0 -> 7182 bytes
 help/de/figures/evo_select_folder.png           |  Bin 0 -> 28954 bytes
 mail/e-mail-attachment-bar.h                    |    2 +-
 mail/e-mail-reader.c                            |    4 +-
 mail/em-account-editor.c                        |    4 +-
 mail/em-composer-utils.c                        |   24 +-
 mail/em-composer-utils.h                        |    2 +-
 mail/em-folder-tree.c                           |    3 -
 mail/em-vfolder-editor.c                        |    3 +-
 mail/mail-send-recv.c                           |   11 +-
 modules/mail/e-mail-shell-view-private.c        |   50 +
 plugins/audio-inline/Makefile.am                |    7 +
 plugins/backup-restore/backup-restore.c         |    8 +-
 plugins/backup-restore/backup.c                 |   76 +-
 plugins/bbdb/Makefile.am                        |    7 +
 plugins/external-editor/Makefile.am             |    8 +
 plugins/google-account-setup/google-source.c    |  130 +-
 plugins/groupwise-features/Makefile.am          |    4 +
 plugins/imap-features/Makefile.am               |    4 +
 plugins/ipod-sync/Makefile.am                   |    1 +
 plugins/itip-formatter/itip-formatter.c         |   28 +-
 plugins/mail-notification/Makefile.am           |    7 +
 plugins/mono/mono-plugin.c                      |    2 +-
 plugins/pst-import/Makefile.am                  |    9 +
 plugins/publish-calendar/publish-calendar.c     |    4 +-
 plugins/tnef-attachments/Makefile.am            |    8 +
 plugins/vcard-inline/Makefile.am                |    7 +
 po/bn_IN.po                                     |  252 ++-
 po/de.po                                        | 3827 ++++++++++++-----------
 po/es.po                                        |  561 ++--
 po/et.po                                        | 3719 +++++++++++------------
 po/he.po                                        | 2136 +++++++-------
 po/or.po                                        | 1019 ++++---
 po/ta.po                                        | 1849 ++++++------
 widgets/misc/Makefile.am                        |    4 +
 widgets/misc/e-calendar-item.c                  |   38 +-
 widgets/misc/e-calendar-item.h                  |   10 +-
 widgets/misc/test-calendar.c                    |    4 +-
 widgets/text/Makefile.am                        |    4 +
 86 files changed, 7644 insertions(+), 6768 deletions(-)
---
diff --cc configure.ac
index 5654991,85d8e72..4975ff1
--- a/configure.ac
+++ b/configure.ac
@@@ -52,9 -49,8 +52,9 @@@ m4_define([gconf_minimum_version], [2.0
  m4_define([libglade_minimum_version], [2.0.0])          # XXX Just a Guess
  m4_define([libgnomecanvas_minimum_version], [2.0.0])    # XXX Just a Guess
  m4_define([libgnomeui_minimum_version], [2.0.0])        # XXX Just a Guess
- m4_define([libxml_minimum_version], [2.0.0])            # XXX Just a Guess
+ m4_define([libxml_minimum_version], [2.7.3])
  m4_define([shared_mime_info_minimum_version], [0.22])
 +m4_define([unique_minimum_version], [1.0])              # XXX Just a Guess
  
  # Optional Packages
  #
diff --cc em-format/em-format.c
index 585b611,0000000..9540067
mode 100644,000000..100644
--- a/em-format/em-format.c
+++ b/em-format/em-format.c
@@@ -1,1949 -1,0 +1,2017 @@@
 +/*
 + * 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 <http://www.gnu.org/licenses/>
 + *
 + *
 + * Authors:
 + *		Michael Zucchi <notzed ximian com>
 + *      Jeffrey Stedfast <fejj ximian com>
 + *
 + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 + *
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <string.h>
 +
 +#include <glib/gi18n.h>
 +#include <gio/gio.h>
 +
 +#include <libedataserver/e-msgport.h>
 +#include <camel/camel-stream.h>
 +#include <camel/camel-stream-mem.h>
 +#include <camel/camel-multipart.h>
 +#include <camel/camel-multipart-encrypted.h>
 +#include <camel/camel-multipart-signed.h>
 +#include <camel/camel-medium.h>
 +#include <camel/camel-mime-message.h>
 +#include <camel/camel-gpg-context.h>
 +#include <camel/camel-smime-context.h>
 +#include <camel/camel-string-utils.h>
 +#include <camel/camel-stream-filter.h>
 +#include <camel/camel-stream-null.h>
 +#include <camel/camel-stream-mem.h>
 +#include <camel/camel-mime-filter-charset.h>
 +#include <camel/camel-mime-filter-windows.h>
 +#include <camel/camel-mime-filter-pgp.h>
 +
 +#include "em-format.h"
 +#include "e-util/e-util.h"
 +#include "shell/e-shell.h"
 +#include "shell/e-shell-settings.h"
 +
 +#define d(x)
 +
 +/* Used to cache various data/info for redraws
 +   The validity stuff could be cached at a higher level but this is easier
 +   This absolutely relies on the partid being _globally unique_
 +   This is still kind of yucky, we should maintian a full tree of all this data,
 +   along with/as part of the puri tree */
 +struct _EMFormatCache {
 +	CamelCipherValidity *valid; /* validity copy */
 +	CamelMimePart *secured;	/* encrypted subpart */
 +
 +	guint state:2;		/* inline state */
 +
 +	gchar partid[1];
 +};
 +
 +#define INLINE_UNSET (0)
 +#define INLINE_ON (1)
 +#define INLINE_OFF (2)
 +
 +static void emf_builtin_init(EMFormatClass *);
 +
 +static const EMFormatHandler *emf_find_handler(EMFormat *emf, const gchar *mime_type);
 +static void emf_format_clone(EMFormat *emf, CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, EMFormat *emfsource);
 +static void emf_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid);
 +static gboolean emf_busy(EMFormat *emf);
 +enum {
 +	EMF_COMPLETE,
 +	EMF_LAST_SIGNAL
 +};
 +
 +static gpointer parent_class;
 +static guint signals[EMF_LAST_SIGNAL];
 +
 +static void
 +emf_free_cache(struct _EMFormatCache *efc)
 +{
 +	if (efc->valid)
 +		camel_cipher_validity_free(efc->valid);
 +	if (efc->secured)
 +		camel_object_unref(efc->secured);
 +	g_free(efc);
 +}
 +
 +static struct _EMFormatCache *
 +emf_insert_cache(EMFormat *emf, const gchar *partid)
 +{
 +	struct _EMFormatCache *new;
 +
 +	new = g_malloc0(sizeof(*new)+strlen(partid));
 +	strcpy(new->partid, partid);
 +	g_hash_table_insert(emf->inline_table, new->partid, new);
 +
 +	return new;
 +}
 +
 +static void
 +emf_finalize (GObject *object)
 +{
 +	EMFormat *emf = EM_FORMAT (object);
 +
 +	if (emf->session)
 +		camel_object_unref (emf->session);
 +
 +	g_hash_table_destroy (emf->inline_table);
 +
 +	em_format_clear_headers(emf);
 +	camel_cipher_validity_free(emf->valid);
 +	g_free(emf->charset);
 +	g_free (emf->default_charset);
 +	g_string_free(emf->part_id, TRUE);
 +
 +	/* FIXME: check pending jobs */
 +
 +	/* Chain up to parent's finalize() method. */
 +	G_OBJECT_CLASS (parent_class)->finalize (object);
 +}
 +
 +static void
 +emf_base_init (EMFormatClass *class)
 +{
 +	class->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
 +	emf_builtin_init (class);
 +}
 +
 +static void
 +emf_class_init (EMFormatClass *class)
 +{
 +	GObjectClass *object_class;
 +
 +	parent_class = g_type_class_peek_parent (class);
 +
 +	object_class = G_OBJECT_CLASS (class);
 +	object_class->finalize = emf_finalize;
 +
 +	class->find_handler = emf_find_handler;
 +	class->format_clone = emf_format_clone;
 +	class->format_secure = emf_format_secure;
 +	class->busy = emf_busy;
 +
 +	signals[EMF_COMPLETE] = g_signal_new (
 +		"complete",
 +		G_OBJECT_CLASS_TYPE (class),
 +		G_SIGNAL_RUN_LAST,
 +		G_STRUCT_OFFSET (EMFormatClass, complete),
 +		NULL, NULL,
 +		g_cclosure_marshal_VOID__VOID,
 +		G_TYPE_NONE, 0);
 +
 +	class->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
 +	emf_builtin_init (class);
 +}
 +
 +static void
 +emf_init (EMFormat *emf)
 +{
 +	EShell *shell;
 +	EShellSettings *shell_settings;
 +
 +	emf->inline_table = g_hash_table_new_full (
 +		g_str_hash, g_str_equal,
 +		(GDestroyNotify) NULL,
 +		(GDestroyNotify) emf_free_cache);
 +	emf->composer = FALSE;
 +	emf->print = FALSE;
 +	e_dlist_init(&emf->header_list);
 +	em_format_default_headers(emf);
 +	emf->part_id = g_string_new("");
 +
 +	shell = e_shell_get_default ();
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	emf->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
 +	g_return_if_fail (emf->session != NULL);
 +
 +	camel_object_ref (emf->session);
 +}
 +
 +GType
 +em_format_get_type (void)
 +{
 +	static GType type = 0;
 +
 +	if (G_UNLIKELY (type == 0)) {
 +		static const GTypeInfo type_info = {
 +			sizeof (EMFormatClass),
 +			(GBaseInitFunc) emf_base_init,
 +			(GBaseFinalizeFunc) NULL,
 +			(GClassInitFunc) emf_class_init,
 +			(GClassFinalizeFunc) NULL,
 +			NULL,  /* class_data */
 +			sizeof (EMFormat),
 +			0,     /* n_preallocs */
 +			(GInstanceInitFunc) emf_init,
 +			NULL   /* value_table */
 +		};
 +
 +		type = g_type_register_static (
 +			G_TYPE_OBJECT, "EMFormat", &type_info, 0);
 +	}
 +
 +	return type;
 +}
 +
 +/**
 + * em_format_class_add_handler:
 + * @emfc: EMFormatClass
 + * @info: Callback information.
 + *
 + * Add a mime type handler to this class.  This is only used by
 + * implementing classes.  The @info.old pointer will automatically be
 + * setup to point to the old hanlder if one was already set.  This can
 + * be used for overrides a fallback.
 + *
 + * When a mime type described by @info is encountered, the callback will
 + * be invoked.  Note that @info may be extended by sub-classes if
 + * they require additional context information.
 + *
 + * Use a mime type of "foo/ *" to insert a fallback handler for type "foo".
 + **/
 +void
 +em_format_class_add_handler(EMFormatClass *emfc, EMFormatHandler *info)
 +{
 +	d(printf("adding format handler to '%s' '%s'\n",	g_type_name_from_class((GTypeClass *)emfc), info->mime_type));
 +	info->old = g_hash_table_lookup(emfc->type_handlers, info->mime_type);
 +	g_hash_table_insert(emfc->type_handlers, (gpointer) info->mime_type, info);
 +}
 +
 +struct _class_handlers {
 +	EMFormatClass *old;
 +	EMFormatClass *new;
 +};
 +static void
 +merge_missing (gpointer key, gpointer value, gpointer userdata)
 +{
 +	struct _class_handlers *classes = (struct _class_handlers *) userdata;
 +	EMFormatHandler *info, *oldinfo;
 +
 +	oldinfo = (EMFormatHandler *) value;
 +	info = g_hash_table_lookup (classes->new->type_handlers, key);
 +	if (!info) {
 +		/* Might be from a plugin */
 +		g_hash_table_insert (classes->new->type_handlers, key, value);
 +	}
 +
 +}
 +
 +void
 +em_format_merge_handler(EMFormat *new, EMFormat *old)
 +{
 +	EMFormatClass *oldc = (EMFormatClass *)G_OBJECT_GET_CLASS(old);
 +	EMFormatClass *newc = (EMFormatClass *)G_OBJECT_GET_CLASS(new);
 +	struct _class_handlers fclasses;
 +
 +	fclasses.old = oldc;
 +	fclasses.new = newc;
 +
 +	g_hash_table_foreach (oldc->type_handlers, merge_missing, &fclasses);
 +
 +}
 +
 +/**
 + * em_format_class_remove_handler:
 + * @emfc:
 + * @info:
 + *
 + * Remove a handler.  @info must be a value which was previously
 + * added.
 + **/
 +void
 +em_format_class_remove_handler(EMFormatClass *emfc, EMFormatHandler *info)
 +{
 +	EMFormatHandler *current;
 +
 +	/* TODO: thread issues? */
 +
 +	current = g_hash_table_lookup(emfc->type_handlers, info->mime_type);
 +	if (current == info) {
 +		current = info->old;
 +		if (current)
 +			g_hash_table_insert(emfc->type_handlers, (gpointer) current->mime_type, current);
 +		else
 +			g_hash_table_remove(emfc->type_handlers, info->mime_type);
 +	} else {
 +		while (current && current->old != info)
 +			current = current->old;
 +		g_return_if_fail(current != NULL);
 +		current->old = info->old;
 +	}
 +}
 +
 +const EMFormatHandler *
 +em_format_find_handler (EMFormat *emf,
 +                        const gchar *mime_type)
 +{
 +	EMFormatClass *class;
 +
 +	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
 +	g_return_val_if_fail (mime_type != NULL, NULL);
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_val_if_fail (class->find_handler != NULL, NULL);
 +	return class->find_handler (emf, mime_type);
 +}
 +
 +/**
 + * em_format_find_handler:
 + * @emf:
 + * @mime_type:
 + *
 + * Find a format handler by @mime_type.
 + *
 + * Return value: NULL if no handler is available.
 + **/
 +static const EMFormatHandler *
 +emf_find_handler(EMFormat *emf, const gchar *mime_type)
 +{
 +	EMFormatClass *emfc = (EMFormatClass *)G_OBJECT_GET_CLASS(emf);
 +
 +	return g_hash_table_lookup(emfc->type_handlers, mime_type);
 +}
 +
 +/**
 + * em_format_fallback_handler:
 + * @emf:
 + * @mime_type:
 + *
 + * Try to find a format handler based on the major type of the @mime_type.
 + *
 + * The subtype is replaced with "*" and a lookup performed.
 + *
 + * Return value:
 + **/
 +const EMFormatHandler *
 +em_format_fallback_handler(EMFormat *emf, const gchar *mime_type)
 +{
 +	gchar *mime, *s;
 +
 +	s = strchr(mime_type, '/');
 +	if (s == NULL)
 +		mime = (gchar *)mime_type;
 +	else {
 +		gsize len = (s-mime_type)+1;
 +
 +		mime = alloca(len+2);
 +		strncpy(mime, mime_type, len);
 +		strcpy(mime+len, "*");
 +	}
 +
 +	return em_format_find_handler(emf, mime);
 +}
 +
 +/**
 + * em_format_add_puri:
 + * @emf:
 + * @size:
 + * @cid: Override the autogenerated content id.
 + * @part:
 + * @func:
 + *
 + * Add a pending-uri handler.  When formatting parts that reference
 + * other parts, a pending-uri (PURI) can be used to track the reference.
 + *
 + * @size is used to allocate the structure, so that it can be directly
 + * subclassed by implementors.
 + *
 + * @cid can be used to override the key used to retreive the PURI, if NULL,
 + * then the content-location and the content-id of the @part are stored
 + * as lookup keys for the part.
 + *
 + * FIXME: This may need a free callback.
 + *
 + * Return value: A new PURI, with a referenced copy of @part, and the cid
 + * always set.  The uri will be set if one is available.  Clashes
 + * are resolved by forgetting the old PURI in the global index.
 + **/
 +EMFormatPURI *
 +em_format_add_puri(EMFormat *emf, gsize size, const gchar *cid, CamelMimePart *part, EMFormatPURIFunc func)
 +{
 +	EMFormatPURI *puri;
 +	const gchar *tmp;
 +
 +	d(printf("adding puri for part: %s\n", emf->part_id->str));
 +
 +	if (size < sizeof(*puri)) {
 +		g_warning (
 +			"size (%" G_GSIZE_FORMAT
 +			") less than size of puri\n", size);
 +		size = sizeof (*puri);
 +	}
 +
 +	puri = g_malloc0(size);
 +
 +	puri->format = emf;
 +	puri->func = func;
 +	puri->use_count = 0;
 +	puri->cid = g_strdup(cid);
 +	puri->part_id = g_strdup(emf->part_id->str);
 +
 +	if (part) {
 +		camel_object_ref(part);
 +		puri->part = part;
 +	}
 +
 +	if (part != NULL && cid == NULL) {
 +		tmp = camel_mime_part_get_content_id(part);
 +		if (tmp)
 +			puri->cid = g_strdup_printf("cid:%s", tmp);
 +		else
 +			puri->cid = g_strdup_printf("em-no-cid:%s", emf->part_id->str);
 +
 +		d(printf("built cid '%s'\n", puri->cid));
 +
 +		/* not quite same as old behaviour, it also put in the relative uri and a fallback for no parent uri */
 +		tmp = camel_mime_part_get_content_location(part);
 +		puri->uri = NULL;
 +		if (tmp == NULL) {
 +			/* no location, don't set a uri at all, html parts do this themselves */
 +		} else {
 +			if (strchr(tmp, ':') == NULL && emf->base != NULL) {
 +				CamelURL *uri;
 +
 +				uri = camel_url_new_with_base(emf->base, tmp);
 +				puri->uri = camel_url_to_string(uri, 0);
 +				camel_url_free(uri);
 +			} else {
 +				puri->uri = g_strdup(tmp);
 +			}
 +		}
 +	}
 +
 +	g_return_val_if_fail (puri->cid != NULL, NULL);
 +	g_return_val_if_fail (emf->pending_uri_level != NULL, NULL);
 +	g_return_val_if_fail (emf->pending_uri_table != NULL, NULL);
 +
 +	e_dlist_addtail(&emf->pending_uri_level->uri_list, (EDListNode *)puri);
 +
 +	if (puri->uri)
 +		g_hash_table_insert(emf->pending_uri_table, puri->uri, puri);
 +	g_hash_table_insert(emf->pending_uri_table, puri->cid, puri);
 +
 +	return puri;
 +}
 +
 +/**
 + * em_format_push_level:
 + * @emf:
 + *
 + * This is used to build a heirarchy of visible PURI objects based on
 + * the structure of the message.  Used by multipart/alternative formatter.
 + *
 + * FIXME: This could probably also take a uri so it can automaticall update
 + * the base location.
 + **/
 +void
 +em_format_push_level(EMFormat *emf)
 +{
 +	struct _EMFormatPURITree *purilist;
 +
 +	d(printf("em_format_push_level\n"));
 +	purilist = g_malloc0(sizeof(*purilist));
 +	e_dlist_init(&purilist->children);
 +	e_dlist_init(&purilist->uri_list);
 +	purilist->parent = emf->pending_uri_level;
 +	if (emf->pending_uri_tree == NULL) {
 +		emf->pending_uri_tree = purilist;
 +	} else {
 +		e_dlist_addtail(&emf->pending_uri_level->children, (EDListNode *)purilist);
 +	}
 +	emf->pending_uri_level = purilist;
 +}
 +
 +/**
 + * em_format_pull_level:
 + * @emf:
 + *
 + * Drop a level of visibility back to the parent.  Note that
 + * no PURI values are actually freed.
 + **/
 +void
 +em_format_pull_level(EMFormat *emf)
 +{
 +	d(printf("em_format_pull_level\n"));
 +	emf->pending_uri_level = emf->pending_uri_level->parent;
 +}
 +
 +/**
 + * em_format_find_visible_puri:
 + * @emf:
 + * @uri:
 + *
 + * Search for a PURI based on the visibility defined by :push_level()
 + * and :pull_level().
 + *
 + * Return value:
 + **/
 +EMFormatPURI *
 +em_format_find_visible_puri(EMFormat *emf, const gchar *uri)
 +{
 +	EMFormatPURI *pw;
 +	struct _EMFormatPURITree *ptree;
 +
 +	d(printf("checking for visible uri '%s'\n", uri));
 +
 +	ptree = emf->pending_uri_level;
 +	while (ptree) {
 +		pw = (EMFormatPURI *)ptree->uri_list.head;
 +		while (pw->next) {
 +			d(printf(" pw->uri = '%s' pw->cid = '%s\n", pw->uri?pw->uri:"", pw->cid));
 +			if ((pw->uri && !strcmp(pw->uri, uri)) || !strcmp(pw->cid, uri))
 +				return pw;
 +			pw = pw->next;
 +		}
 +		ptree = ptree->parent;
 +	}
 +
 +	return NULL;
 +}
 +
 +/**
 + * em_format_find_puri:
 + * @emf:
 + * @uri:
 + *
 + * Search for a PURI based on a uri.  Both the content-id
 + * and content-location are checked.
 + *
 + * Return value:
 + **/
 +EMFormatPURI *
 +
 +em_format_find_puri(EMFormat *emf, const gchar *uri)
 +{
 +	return g_hash_table_lookup(emf->pending_uri_table, uri);
 +}
 +
 +static void
 +emf_clear_puri_node(struct _EMFormatPURITree *node)
 +{
 +	{
 +		EMFormatPURI *pw, *pn;
 +
 +		/* clear puri's at this level */
 +		pw = (EMFormatPURI *)node->uri_list.head;
 +		pn = pw->next;
 +		while (pn) {
 +			d(printf ("freeing pw %p format:%p\n", pw, pw->format));
 +			if (pw->free)
 +				pw->free(pw);
 +			g_free(pw->uri);
 +			g_free(pw->cid);
 +			g_free(pw->part_id);
 +			if (pw->part)
 +				camel_object_unref(pw->part);
 +			g_free(pw);
 +			pw = pn;
 +			pn = pn->next;
 +		}
 +	}
 +
 +	{
 +		struct _EMFormatPURITree *cw, *cn;
 +
 +		/* clear child nodes */
 +		cw = (struct _EMFormatPURITree *)node->children.head;
 +		cn = cw->next;
 +		while (cn) {
 +			emf_clear_puri_node(cw);
 +			cw = cn;
 +			cn = cn->next;
 +		}
 +	}
 +
 +	g_free(node);
 +}
 +
 +/**
 + * em_format_clear_puri_tree:
 + * @emf:
 + *
 + * For use by implementors to clear out the message structure
 + * data.
 + **/
 +void
 +em_format_clear_puri_tree(EMFormat *emf)
 +{
 +	d(printf("clearing pending uri's\n"));
 +
 +	if (emf->pending_uri_table) {
 +		g_hash_table_destroy(emf->pending_uri_table);
 +		emf_clear_puri_node(emf->pending_uri_tree);
 +		emf->pending_uri_level = NULL;
 +		emf->pending_uri_tree = NULL;
 +	}
 +	emf->pending_uri_table = g_hash_table_new(g_str_hash, g_str_equal);
 +	em_format_push_level(emf);
 +}
 +
 +/* use mime_type == NULL  to force showing as application/octet-stream */
 +void
 +em_format_part_as(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const gchar *mime_type)
 +{
 +	const EMFormatHandler *handle = NULL;
 +	const gchar *snoop_save = emf->snoop_mime_type, *tmp;
 +	CamelURL *base_save = emf->base, *base = NULL;
 +	gchar *basestr = NULL;
 +
 +	d(printf("format_part_as()\n"));
 +
 +	emf->snoop_mime_type = NULL;
 +
 +	/* RFC 2110, we keep track of content-base, and absolute content-location headers
 +	   This is actually only required for html, but, *shrug* */
 +	tmp = camel_medium_get_header((CamelMedium *)part, "Content-Base");
 +	if (tmp == NULL) {
 +		tmp = camel_mime_part_get_content_location(part);
 +		if (tmp && strchr(tmp, ':') == NULL)
 +			tmp = NULL;
 +	} else {
 +		tmp = basestr = camel_header_location_decode(tmp);
 +	}
 +	d(printf("content-base is '%s'\n", tmp?tmp:"<unset>"));
 +	if (tmp
 +	    && (base = camel_url_new(tmp, NULL))) {
 +		emf->base = base;
 +		d(printf("Setting content base '%s'\n", tmp));
 +	}
 +	g_free(basestr);
 +
 +	if (mime_type != NULL) {
 +		if (g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) {
 +			emf->snoop_mime_type = mime_type = em_format_snoop_type(part);
 +			if (mime_type == NULL)
 +				mime_type = "application/octet-stream";
 +		}
 +
 +		handle = em_format_find_handler(emf, mime_type);
 +		if (handle == NULL)
 +			handle = em_format_fallback_handler(emf, mime_type);
 +
 +		if (handle != NULL
 +		    && !em_format_is_attachment(emf, part)) {
 +			d(printf("running handler for type '%s'\n", mime_type));
 +			handle->handler(emf, stream, part, handle);
 +			goto finish;
 +		}
 +		d(printf("this type is an attachment? '%s'\n", mime_type));
 +	} else {
 +		mime_type = "application/octet-stream";
 +	}
 +
 +	((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_attachment(emf, stream, part, mime_type, handle);
 +finish:
 +	emf->base = base_save;
 +	emf->snoop_mime_type = snoop_save;
 +
 +	if (base)
 +		camel_url_free(base);
 +}
 +
 +void
 +em_format_part(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
 +{
 +	gchar *mime_type;
 +	CamelDataWrapper *dw;
 +
 +	dw = camel_medium_get_content_object((CamelMedium *)part);
 +	mime_type = camel_data_wrapper_get_mime_type(dw);
 +	if (mime_type) {
 +		camel_strdown(mime_type);
 +		em_format_part_as(emf, stream, part, mime_type);
 +		g_free(mime_type);
 +	} else
 +		em_format_part_as(emf, stream, part, "text/plain");
 +}
 +
 +static void
 +emf_clone_inlines(gpointer key, gpointer val, gpointer data)
 +{
 +	struct _EMFormatCache *emfc = val, *new;
 +
 +	new = emf_insert_cache((EMFormat *)data, emfc->partid);
 +	new->state = emfc->state;
 +	if (emfc->valid)
 +		new->valid = camel_cipher_validity_clone(emfc->valid);
 +	if (emfc->secured)
 +		camel_object_ref((new->secured = emfc->secured));
 +}
 +
 +static void
 +emf_format_clone(EMFormat *emf, CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, EMFormat *emfsource)
 +{
 +	em_format_clear_puri_tree(emf);
 +
 +	if (emf != emfsource) {
 +		g_hash_table_remove_all(emf->inline_table);
 +		if (emfsource) {
 +			struct _EMFormatHeader *h;
 +
 +			/* We clone the current state here */
 +			g_hash_table_foreach(emfsource->inline_table, emf_clone_inlines, emf);
 +			emf->mode = emfsource->mode;
 +			g_free(emf->charset);
 +			emf->charset = g_strdup(emfsource->charset);
 +			g_free (emf->default_charset);
 +			emf->default_charset = g_strdup (emfsource->default_charset);
 +
 +			em_format_clear_headers(emf);
 +			for (h = (struct _EMFormatHeader *)emfsource->header_list.head; h->next; h = h->next)
 +				em_format_add_header(emf, h->name, h->flags);
 +		}
 +	}
 +
 +	/* what a mess */
 +	if (folder != emf->folder) {
 +		if (emf->folder)
 +			camel_object_unref(emf->folder);
 +		if (folder)
 +			camel_object_ref(folder);
 +		emf->folder = folder;
 +	}
 +
 +	if (uid != emf->uid) {
 +		g_free(emf->uid);
 +		emf->uid = g_strdup(uid);
 +	}
 +
 +	if (msg != emf->message) {
 +		if (emf->message)
 +			camel_object_unref(emf->message);
 +		if (msg)
 +			camel_object_ref(msg);
 +		emf->message = msg;
 +	}
 +
 +	g_string_truncate(emf->part_id, 0);
 +	if (folder != NULL)
 +		/* TODO build some string based on the folder name/location? */
 +		g_string_append_printf(emf->part_id, ".%p", (gpointer) folder);
 +	if (uid != NULL)
 +		g_string_append_printf(emf->part_id, ".%s", uid);
 +}
 +
 +static void
 +emf_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid)
 +{
 +	CamelCipherValidity *save = emf->valid_parent;
 +	gint len;
 +
 +	/* Note that this also requires support from higher up in the class chain
 +	    - validity needs to be cleared when you start output
 +	    - also needs to be cleared (but saved) whenever you start a new message. */
 +
 +	if (emf->valid == NULL) {
 +		emf->valid = valid;
 +	} else {
 +		camel_dlist_addtail(&emf->valid_parent->children, (CamelDListNode *)valid);
 +		camel_cipher_validity_envelope(emf->valid_parent, valid);
 +	}
 +
 +	emf->valid_parent = valid;
 +
 +	len = emf->part_id->len;
 +	g_string_append_printf(emf->part_id, ".secured");
 +	em_format_part(emf, stream, part);
 +	g_string_truncate(emf->part_id, len);
 +
 +	emf->valid_parent = save;
 +}
 +
 +static gboolean
 +emf_busy(EMFormat *emf)
 +{
 +	return FALSE;
 +}
 +
 +/**
 + * em_format_format_clone:
 + * @emf: an #EMFormat
 + * @folder: a #CamelFolder or %NULL
 + * @uid: Message UID or %NULL
 + * @msg: a #CamelMimeMessage or %NULL
 + * @emfsource: Used as a basis for user-altered layout, e.g. inline viewed
 + * attachments.
 + *
 + * Format a message @msg.  If @emfsource is non NULL, then the status of
 + * inlined expansion and so forth is copied direction from @emfsource.
 + *
 + * By passing the same value for @emf and @emfsource, you can perform
 + * a display refresh, or it can be used to generate an identical layout,
 + * e.g. to print what the user has shown inline.
 + **/
 +void
 +em_format_format_clone (EMFormat *emf,
 +                        CamelFolder *folder,
 +                        const gchar *uid,
 +                        CamelMimeMessage *message,
 +                        EMFormat *source)
 +{
 +	EMFormatClass *class;
 +
 +	g_return_if_fail (EM_IS_FORMAT (emf));
 +	g_return_if_fail (folder == NULL || CAMEL_IS_FOLDER (folder));
 +	g_return_if_fail (message == NULL || CAMEL_IS_MIME_MESSAGE (message));
 +	g_return_if_fail (source == NULL || EM_IS_FORMAT (source));
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_if_fail (class->format_clone != NULL);
 +	class->format_clone (emf, folder, uid, message, source);
 +}
 +
 +void
 +em_format_format (EMFormat *emf,
 +                  CamelFolder *folder,
 +                  const gchar *uid,
 +                  CamelMimeMessage *message)
 +{
 +	/* em_format_format_clone() will check the arguments. */
 +	em_format_format_clone (emf, folder, uid, message, NULL);
 +}
 +
 +void
 +em_format_redraw (EMFormat *emf)
 +{
 +	g_return_if_fail (EM_IS_FORMAT (emf));
 +
 +	em_format_format_clone (
 +		emf, emf->folder, emf->uid, emf->message, emf);
 +}
 +
 +/**
 + * em_format_set_mode:
 + * @emf:
 + * @type:
 + *
 + * Set display mode, EM_FORMAT_SOURCE, EM_FORMAT_ALLHEADERS, or
 + * EM_FORMAT_NORMAL.
 + **/
 +void
 +em_format_set_mode(EMFormat *emf, em_format_mode_t type)
 +{
 +	if (emf->mode == type)
 +		return;
 +
 +	emf->mode = type;
 +
 +	/* force redraw if type changed afterwards */
 +	if (emf->message)
 +		em_format_redraw(emf);
 +}
 +
 +/**
 + * em_format_set_charset:
 + * @emf:
 + * @charset:
 + *
 + * set override charset on formatter.  message will be redisplayed if
 + * required.
 + **/
 +void
 +em_format_set_charset(EMFormat *emf, const gchar *charset)
 +{
 +	if ((emf->charset && charset && g_ascii_strcasecmp(emf->charset, charset) == 0)
 +	    || (emf->charset == NULL && charset == NULL)
 +	    || (emf->charset == charset))
 +		return;
 +
 +	g_free(emf->charset);
 +	emf->charset = g_strdup(charset);
 +
 +	if (emf->message)
 +		em_format_redraw(emf);
 +}
 +
 +/**
 + * em_format_set_default_charset:
 + * @emf:
 + * @charset:
 + *
 + * Set the fallback, default system charset to use when no other charsets
 + * are present.  Message will be redisplayed if required (and sometimes redisplayed
 + * when it isn't).
 + **/
 +void
 +em_format_set_default_charset(EMFormat *emf, const gchar *charset)
 +{
 +	if ((emf->default_charset && charset && g_ascii_strcasecmp(emf->default_charset, charset) == 0)
 +	    || (emf->default_charset == NULL && charset == NULL)
 +	    || (emf->default_charset == charset))
 +		return;
 +
 +	g_free(emf->default_charset);
 +	emf->default_charset = g_strdup(charset);
 +
 +	if (emf->message && emf->charset == NULL)
 +		em_format_redraw(emf);
 +}
 +
 +/**
 + * em_format_clear_headers:
 + * @emf:
 + *
 + * Clear the list of headers to be displayed.  This will force all headers to
 + * be shown.
 + **/
 +void
 +em_format_clear_headers(EMFormat *emf)
 +{
 +	EMFormatHeader *eh;
 +
 +	while ((eh = (EMFormatHeader *)e_dlist_remhead(&emf->header_list)))
 +		g_free(eh);
 +}
 +
 +/* note: also copied in em-mailer-prefs.c */
 +static const struct {
 +	const gchar *name;
 +	guint32 flags;
 +} default_headers[] = {
 +	{ N_("From"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Reply-To"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("To"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Cc"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Bcc"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Subject"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Date"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Newsgroups"), EM_FORMAT_HEADER_BOLD },
 +	{ N_("Face"), 0 },
 +};
 +
 +/**
 + * em_format_default_headers:
 + * @emf:
 + *
 + * Set the headers to show to the default list.
 + *
 + * From, Reply-To, To, Cc, Bcc, Subject and Date.
 + **/
 +void
 +em_format_default_headers(EMFormat *emf)
 +{
 +	gint i;
 +
 +	em_format_clear_headers(emf);
 +	for (i=0; i<sizeof(default_headers)/sizeof(default_headers[0]); i++)
 +		em_format_add_header(emf, default_headers[i].name, default_headers[i].flags);
 +}
 +
 +/**
 + * em_format_add_header:
 + * @emf:
 + * @name: The name of the header, as it will appear during output.
 + * @flags: EM_FORMAT_HEAD_* defines to control display attributes.
 + *
 + * Add a specific header to show.  If any headers are set, they will
 + * be displayed in the order set by this function.  Certain known
 + * headers included in this list will be shown using special
 + * formatting routines.
 + **/
 +void em_format_add_header(EMFormat *emf, const gchar *name, guint32 flags)
 +{
 +	EMFormatHeader *h;
 +
 +	h = g_malloc(sizeof(*h) + strlen(name));
 +	h->flags = flags;
 +	strcpy(h->name, name);
 +	e_dlist_addtail(&emf->header_list, (EDListNode *)h);
 +}
 +
 +/**
 + * em_format_is_attachment:
 + * @emf:
 + * @part: Part to check.
 + *
 + * Returns true if the part is an attachment.
 + *
 + * A part is not considered an attachment if it is a
 + * multipart, or a text part with no filename.  It is used
 + * to determine if an attachment header should be displayed for
 + * the part.
 + *
 + * Content-Disposition is not checked.
 + *
 + * Return value: TRUE/FALSE
 + **/
 +gint em_format_is_attachment(EMFormat *emf, CamelMimePart *part)
 +{
 +	/*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
 +	CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
 +
 +	if (!dw)
 +		return 0;
 +
 +	/*printf("checking is attachment %s/%s\n", ct->type, ct->subtype);*/
 +	return !(camel_content_type_is (dw->mime_type, "multipart", "*")
 +		 || camel_content_type_is(dw->mime_type, "application", "x-pkcs7-mime")
 +		 || camel_content_type_is(dw->mime_type, "application", "pkcs7-mime")
 +		 || camel_content_type_is(dw->mime_type, "application", "x-inlinepgp-signed")
 +		 || camel_content_type_is(dw->mime_type, "application", "x-inlinepgp-encrypted")
 +		 || camel_content_type_is(dw->mime_type, "x-evolution", "evolution-rss-feed")
 +		 || (camel_content_type_is (dw->mime_type, "text", "*")
 +		     && camel_mime_part_get_filename(part) == NULL));
 +}
 +
 +/**
 + * em_format_is_inline:
 + * @emf:
 + * @part:
 + * @partid: format->part_id part id of this part.
 + * @handle: handler for this part
 + *
 + * Returns true if the part should be displayed inline.  Any part with
 + * a Content-Disposition of inline, or if the @handle has a default
 + * inline set, will be shown inline.
 + *
 + * :set_inline() called on the same part will override any calculated
 + * value.
 + *
 + * Return value:
 + **/
 +gint em_format_is_inline(EMFormat *emf, const gchar *partid, CamelMimePart *part, const EMFormatHandler *handle)
 +{
 +	struct _EMFormatCache *emfc;
 +	const gchar *tmp;
 +
 +	if (handle == NULL)
 +		return FALSE;
 +
 +	emfc = g_hash_table_lookup(emf->inline_table, partid);
 +	if (emfc && emfc->state != INLINE_UNSET)
 +		return emfc->state & 1;
 +
 +	/* some types need to override the disposition, e.g. application/x-pkcs7-mime */
 +	if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION)
 +		return TRUE;
 +
 +	tmp = camel_mime_part_get_disposition(part);
 +	if (tmp)
 +		return g_ascii_strcasecmp(tmp, "inline") == 0;
 +
 +	/* otherwise, use the default for this handler type */
 +	return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0;
 +}
 +
 +/**
 + * em_format_set_inline:
 + * @emf:
 + * @partid: id of part
 + * @state:
 + *
 + * Force the attachment @part to be expanded or hidden explictly to match
 + * @state.  This is used only to record the change for a redraw or
 + * cloned layout render and does not force a redraw.
 + **/
 +void em_format_set_inline(EMFormat *emf, const gchar *partid, gint state)
 +{
 +	struct _EMFormatCache *emfc;
 +
 +	emfc = g_hash_table_lookup(emf->inline_table, partid);
 +	if (emfc == NULL) {
 +		emfc = emf_insert_cache(emf, partid);
 +	} else if (emfc->state != INLINE_UNSET && (emfc->state & 1) == state)
 +		return;
 +
 +	emfc->state = state?INLINE_ON:INLINE_OFF;
 +
 +	if (emf->message)
 +		em_format_redraw(emf);
 +}
 +
 +void
 +em_format_format_attachment (EMFormat *emf,
 +                             CamelStream *stream,
 +                             CamelMimePart *mime_part,
 +                             const gchar *mime_type,
 +                             const struct _EMFormatHandler *info)
 +{
 +	EMFormatClass *class;
 +
 +	g_return_if_fail (EM_IS_FORMAT (emf));
 +	g_return_if_fail (CAMEL_IS_STREAM (stream));
 +	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
 +	g_return_if_fail (mime_type != NULL);
 +	g_return_if_fail (info != NULL);
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_if_fail (class->format_attachment != NULL);
 +	class->format_attachment (emf, stream, mime_part, mime_type, info);
 +}
 +
 +void
 +em_format_format_error (EMFormat *emf,
 +                        CamelStream *stream,
 +                        const gchar *format,
 +                        ...)
 +{
 +	EMFormatClass *class;
 +	gchar *errmsg;
 +	va_list ap;
 +
 +	g_return_if_fail (EM_IS_FORMAT (emf));
 +	g_return_if_fail (CAMEL_IS_STREAM (stream));
 +	g_return_if_fail (format != NULL);
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_if_fail (class->format_error != NULL);
 +
 +	va_start (ap, format);
 +	errmsg = g_strdup_vprintf (format, ap);
 +	class->format_error (emf, stream, errmsg);
 +	g_free (errmsg);
 +	va_end (ap);
 +}
 +
 +void
 +em_format_format_secure (EMFormat *emf,
 +                         CamelStream *stream,
 +                         CamelMimePart *mime_part,
 +                         CamelCipherValidity *valid)
 +{
 +	EMFormatClass *class;
 +
 +	g_return_if_fail (EM_IS_FORMAT (emf));
 +	g_return_if_fail (CAMEL_IS_STREAM (stream));
 +	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
 +	g_return_if_fail (valid != NULL);
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_if_fail (class->format_secure != NULL);
 +	class->format_secure (emf, stream, mime_part, valid);
 +
 +	if (emf->valid_parent == NULL && emf->valid != NULL) {
 +		camel_cipher_validity_free (emf->valid);
 +		emf->valid = NULL;
 +	}
 +}
 +
 +void
 +em_format_format_source (EMFormat *emf,
 +                         CamelStream *stream,
 +                         CamelMimePart *mime_part)
 +{
 +	EMFormatClass *class;
 +
 +	g_return_if_fail (EM_IS_FORMAT (emf));
 +	g_return_if_fail (CAMEL_IS_STREAM (stream));
 +	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_if_fail (class->format_source != NULL);
 +	class->format_source (emf, stream, mime_part);
 +}
 +
 +gboolean
 +em_format_busy (EMFormat *emf)
 +{
 +	EMFormatClass *class;
 +
 +	g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
 +
 +	class = EM_FORMAT_GET_CLASS (emf);
 +	g_return_val_if_fail (class->busy != NULL, FALSE);
 +	return class->busy (emf);
 +}
 +
 +/* should this be virtual? */
 +void
 +em_format_format_content(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
 +{
 +	CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
 +
 +	if (camel_content_type_is (dw->mime_type, "text", "*"))
 +		em_format_format_text(emf, stream, (CamelDataWrapper *)part);
 +	else
 +		camel_data_wrapper_decode_to_stream(dw, stream);
 +}
 +
 +/**
 + * em_format_format_content:
 + * @emf:
 + * @stream: Where to write the converted text
 + * @part: Part whose container is to be formatted
 + *
 + * Decode/output a part's content to @stream.
 + **/
 +void
 +em_format_format_text(EMFormat *emf, CamelStream *stream, CamelDataWrapper *dw)
 +{
 +	CamelStreamFilter *filter_stream;
 +	CamelMimeFilterCharset *filter;
 +	const gchar *charset = NULL;
 +	CamelMimeFilterWindows *windows = NULL;
 +	CamelStream *mem_stream = NULL;
 +	gsize size;
 +	gsize max;
 +	GConfClient *gconf;
 +
 +	if (emf->charset) {
 +		charset = emf->charset;
 +	} else if (dw->mime_type
 +		   && (charset = camel_content_type_param (dw->mime_type, "charset"))
 +		   && g_ascii_strncasecmp(charset, "iso-8859-", 9) == 0) {
 +		CamelStream *null;
 +
 +		/* Since a few Windows mailers like to claim they sent
 +		 * out iso-8859-# encoded text when they really sent
 +		 * out windows-cp125#, do some simple sanity checking
 +		 * before we move on... */
 +
 +		null = camel_stream_null_new();
 +		filter_stream = camel_stream_filter_new_with_stream(null);
 +		camel_object_unref(null);
 +
 +		windows = (CamelMimeFilterWindows *)camel_mime_filter_windows_new(charset);
 +		camel_stream_filter_add(filter_stream, (CamelMimeFilter *)windows);
 +
 +		camel_data_wrapper_decode_to_stream(dw, (CamelStream *)filter_stream);
 +		camel_stream_flush((CamelStream *)filter_stream);
 +		camel_object_unref(filter_stream);
 +
 +		charset = camel_mime_filter_windows_real_charset (windows);
 +	} else if (charset == NULL) {
 +		charset = emf->default_charset;
 +	}
 +
 +	mem_stream = (CamelStream *)camel_stream_mem_new ();
 +	filter_stream = camel_stream_filter_new_with_stream(mem_stream);
 +
 +	if ((filter = camel_mime_filter_charset_new_convert(charset, "UTF-8"))) {
 +		camel_stream_filter_add(filter_stream, (CamelMimeFilter *) filter);
 +		camel_object_unref(filter);
 +	}
 +
 +	max = -1;
 +
 +	gconf = gconf_client_get_default ();
 +	if (gconf_client_get_bool (gconf, "/apps/evolution/mail/display/force_message_limit", NULL)) {
 +		max = gconf_client_get_int (gconf, "/apps/evolution/mail/display/message_text_part_limit", NULL);
 +		if (max == 0)
 +			max = -1;
 +	}
 +	g_object_unref (gconf);
 +
 +	size = camel_data_wrapper_decode_to_stream(emf->mode == EM_FORMAT_SOURCE ? (CamelDataWrapper *)dw: camel_medium_get_content_object((CamelMedium *)dw), (CamelStream *)filter_stream);
 +	camel_stream_flush((CamelStream *)filter_stream);
 +	camel_object_unref(filter_stream);
 +	camel_stream_reset (mem_stream);
 +
 +	if (max == -1 || size == -1 || size < (max * 1024) || emf->composer) {
 +		camel_stream_write_to_stream(mem_stream, (CamelStream *)stream);
 +		camel_stream_flush((CamelStream *)stream);
 +	} else {
 +		((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_optional(emf, stream, (CamelMimePart *)dw, mem_stream);
 +	}
 +
 +	if (windows)
 +		camel_object_unref(windows);
 +}
 +
 +/**
 + * em_format_describe_part:
 + * @part:
 + * @mimetype:
 + *
 + * Generate a simple textual description of a part, @mime_type represents the
 + * the content.
 + *
 + * Return value:
 + **/
 +gchar *
 +em_format_describe_part(CamelMimePart *part, const gchar *mime_type)
 +{
 +	GString *stext;
 +	const gchar *filename, *description;
 +	gchar *content_type, *desc;
 +
 +	stext = g_string_new("");
 +	content_type = g_content_type_from_mime_type (mime_type);
 +	desc = g_content_type_get_description (content_type ? content_type : mime_type);
 +	g_free (content_type);
 +	g_string_append_printf (stext, _("%s attachment"), desc ? desc : mime_type);
 +	g_free (desc);
 +	if ((filename = camel_mime_part_get_filename (part)))
 +		g_string_append_printf(stext, " (%s)", filename);
 +	if ((description = camel_mime_part_get_description(part)) &&
 +		(*description != 0) &&
 +		!(filename && (strcmp(filename, description) == 0)))
 +		g_string_append_printf(stext, ", \"%s\"", description);
 +
 +	return g_string_free (stext, FALSE);
 +}
 +
 +/* ********************************************************************** */
 +
 +#ifdef ENABLE_SMIME
 +static void
 +emf_application_xpkcs7mime(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelCipherContext *context;
 +	CamelException *ex;
 +	CamelMimePart *opart;
 +	CamelCipherValidity *valid;
 +	struct _EMFormatCache *emfc;
 +
 +	/* should this perhaps run off a key of ".secured" ? */
 +	emfc = g_hash_table_lookup(emf->inline_table, emf->part_id->str);
 +	if (emfc && emfc->valid) {
 +		em_format_format_secure(emf, stream, emfc->secured, camel_cipher_validity_clone(emfc->valid));
 +		return;
 +	}
 +
 +	ex = camel_exception_new();
 +
 +	context = camel_smime_context_new(emf->session);
 +
 +	opart = camel_mime_part_new();
 +	valid = camel_cipher_decrypt(context, part, opart, ex);
 +	if (valid == NULL) {
 +		em_format_format_error(emf, stream, "%s", ex->desc?ex->desc:_("Could not parse S/MIME message: Unknown error"));
 +		em_format_part_as(emf, stream, part, NULL);
 +	} else {
 +		if (emfc == NULL)
 +			emfc = emf_insert_cache(emf, emf->part_id->str);
 +
 +		emfc->valid = camel_cipher_validity_clone(valid);
 +		camel_object_ref((emfc->secured = opart));
 +
 +		em_format_format_secure(emf, stream, opart, valid);
 +	}
 +
 +	camel_object_unref(opart);
 +	camel_object_unref(context);
 +	camel_exception_free(ex);
 +}
 +#endif
 +
 +/* RFC 1740 */
 +static void
 +emf_multipart_appledouble(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
 +	CamelMimePart *mime_part;
 +	gint len;
 +
 +	if (!CAMEL_IS_MULTIPART(mp)) {
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	mime_part = camel_multipart_get_part(mp, 1);
 +	if (mime_part) {
 +		/* try the data fork for something useful, doubtful but who knows */
 +		len = emf->part_id->len;
 +		g_string_append_printf(emf->part_id, ".appledouble.1");
 +		em_format_part(emf, stream, mime_part);
 +		g_string_truncate(emf->part_id, len);
 +	} else
 +		em_format_format_source(emf, stream, part);
 +
 +}
 +
 +/* RFC ??? */
 +static void
 +emf_multipart_mixed(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
 +	gint i, nparts, len;
 +
 +	if (!CAMEL_IS_MULTIPART(mp)) {
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	len = emf->part_id->len;
 +	nparts = camel_multipart_get_number(mp);
 +	for (i = 0; i < nparts; i++) {
 +		part = camel_multipart_get_part(mp, i);
 +		g_string_append_printf(emf->part_id, ".mixed.%d", i);
 +		em_format_part(emf, stream, part);
 +		g_string_truncate(emf->part_id, len);
 +	}
 +}
 +
 +/* RFC 1740 */
 +static void
 +emf_multipart_alternative(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
 +	gint i, nparts, bestid = 0;
 +	CamelMimePart *best = NULL;
 +
 +	if (!CAMEL_IS_MULTIPART(mp)) {
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	/* as per rfc, find the last part we know how to display */
 +	nparts = camel_multipart_get_number(mp);
 +	for (i = 0; i < nparts; i++) {
 +		CamelContentType *type;
 +		gchar *mime_type;
 +
 +		/* is it correct to use the passed in *part here? */
 +		part = camel_multipart_get_part(mp, i);
 +
 +		if (!part)
 +			continue;
 +
 +		type = camel_mime_part_get_content_type (part);
 +		mime_type = camel_content_type_simple (type);
 +
 +		camel_strdown (mime_type);
 +
 +		/*if (want_plain && !strcmp (mime_type, "text/plain"))
 +		  return part;*/
 +
 +		if (em_format_find_handler(emf, mime_type)
 +		    || (best == NULL && em_format_fallback_handler(emf, mime_type))) {
 +			best = part;
 +			bestid = i;
 +		}
 +
 +		g_free(mime_type);
 +	}
 +
 +	if (best) {
 +		gint len = emf->part_id->len;
 +
 +		g_string_append_printf(emf->part_id, ".alternative.%d", bestid);
 +		em_format_part(emf, stream, best);
 +		g_string_truncate(emf->part_id, len);
 +	} else
 +		emf_multipart_mixed(emf, stream, part, info);
 +}
 +
 +static void
 +emf_multipart_encrypted(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelCipherContext *context;
 +	CamelException *ex;
 +	const gchar *protocol;
 +	CamelMimePart *opart;
 +	CamelCipherValidity *valid;
 +	CamelMultipartEncrypted *mpe;
 +	struct _EMFormatCache *emfc;
 +
 +	/* should this perhaps run off a key of ".secured" ? */
 +	emfc = g_hash_table_lookup(emf->inline_table, emf->part_id->str);
 +	if (emfc && emfc->valid) {
 +		em_format_format_secure(emf, stream, emfc->secured, camel_cipher_validity_clone(emfc->valid));
 +		return;
 +	}
 +
 +	mpe = (CamelMultipartEncrypted*)camel_medium_get_content_object((CamelMedium *)part);
 +	if (!CAMEL_IS_MULTIPART_ENCRYPTED(mpe)) {
 +		em_format_format_error(emf, stream, _("Could not parse MIME message. Displaying as source."));
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	/* Currently we only handle RFC2015-style PGP encryption. */
 +	protocol = camel_content_type_param(((CamelDataWrapper *)mpe)->mime_type, "protocol");
 +	if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) {
 +		em_format_format_error(emf, stream, _("Unsupported encryption type for multipart/encrypted"));
 +		em_format_part_as(emf, stream, part, "multipart/mixed");
 +		return;
 +	}
 +
 +	ex = camel_exception_new();
 +	context = camel_gpg_context_new(emf->session);
 +	opart = camel_mime_part_new();
 +	valid = camel_cipher_decrypt(context, part, opart, ex);
 +	if (valid == NULL) {
 +		em_format_format_error(emf, stream, ex->desc?_("Could not parse PGP/MIME message"):_("Could not parse PGP/MIME message: Unknown error"));
 +		if (ex->desc)
 +			em_format_format_error(emf, stream, "%s", ex->desc);
 +		em_format_part_as(emf, stream, part, "multipart/mixed");
 +	} else {
 +		if (emfc == NULL)
 +			emfc = emf_insert_cache(emf, emf->part_id->str);
 +
 +		emfc->valid = camel_cipher_validity_clone(valid);
 +		camel_object_ref((emfc->secured = opart));
 +
 +		em_format_format_secure(emf, stream, opart, valid);
 +	}
 +
 +	/* TODO: Make sure when we finalize this part, it is zero'd out */
 +	camel_object_unref(opart);
 +	camel_object_unref(context);
 +	camel_exception_free(ex);
 +}
 +
 +static void
 +emf_write_related(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
 +{
 +	em_format_format_content(emf, stream, puri->part);
 +	camel_stream_close(stream);
 +}
 +
 +/* RFC 2387 */
 +static void
 +emf_multipart_related(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
 +	CamelMimePart *body_part, *display_part = NULL;
 +	CamelContentType *content_type;
 +	const gchar *start;
 +	gint i, nparts, partidlen, displayid = 0;
 +	gchar *oldpartid;
 +	struct _EMFormatPURITree *ptree;
 +	EMFormatPURI *puri, *purin;
 +
 +	if (!CAMEL_IS_MULTIPART(mp)) {
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	/* FIXME: put this stuff in a shared function */
 +	nparts = camel_multipart_get_number(mp);
 +	content_type = camel_mime_part_get_content_type(part);
 +	start = camel_content_type_param (content_type, "start");
 +	if (start && strlen(start)>2) {
 +		gint len;
 +		const gchar *cid;
 +
 +		/* strip <>'s */
 +		len = strlen (start) - 2;
 +		start++;
 +
 +		for (i=0; i<nparts; i++) {
 +			body_part = camel_multipart_get_part(mp, i);
 +			cid = camel_mime_part_get_content_id(body_part);
 +
 +			if (cid && !strncmp(cid, start, len) && strlen(cid) == len) {
 +				display_part = body_part;
 +				displayid = i;
 +				break;
 +			}
 +		}
 +	} else {
 +		display_part = camel_multipart_get_part(mp, 0);
 +	}
 +
 +	if (display_part == NULL) {
 +		emf_multipart_mixed(emf, stream, part, info);
 +		return;
 +	}
 +
 +	em_format_push_level(emf);
 +
 +	oldpartid = g_strdup(emf->part_id->str);
 +	partidlen = emf->part_id->len;
 +
 +	/* queue up the parts for possible inclusion */
 +	for (i = 0; i < nparts; i++) {
 +		body_part = camel_multipart_get_part(mp, i);
 +		if (body_part != display_part) {
 +			/* set the partid since add_puri uses it */
 +			g_string_append_printf(emf->part_id, ".related.%d", i);
 +			puri = em_format_add_puri(emf, sizeof(EMFormatPURI), NULL, body_part, emf_write_related);
 +			g_string_truncate(emf->part_id, partidlen);
 +			d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid));
 +		}
 +	}
 +
 +	g_string_append_printf(emf->part_id, ".related.%d", displayid);
 +	em_format_part(emf, stream, display_part);
 +	g_string_truncate(emf->part_id, partidlen);
 +	camel_stream_flush(stream);
 +
 +	ptree = emf->pending_uri_level;
 +	puri = (EMFormatPURI *)ptree->uri_list.head;
 +	purin = puri->next;
 +	while (purin) {
 +		if (puri->use_count == 0) {
 +			d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count));
 +			if (puri->func == emf_write_related) {
 +				g_string_printf(emf->part_id, "%s", puri->part_id);
 +				em_format_part(emf, stream, puri->part);
 +			} else {
 +				d(printf("unreferenced uri generated by format code: %s\n", puri->uri?puri->uri:puri->cid));
 +			}
 +		}
 +		puri = purin;
 +		purin = purin->next;
 +	}
 +
 +	g_string_printf(emf->part_id, "%s", oldpartid);
 +	g_free(oldpartid);
 +
 +	em_format_pull_level(emf);
 +}
 +
 +static void
 +emf_multipart_signed(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelMimePart *cpart;
 +	CamelMultipartSigned *mps;
 +	CamelCipherContext *cipher = NULL;
 +	struct _EMFormatCache *emfc;
 +
 +	/* should this perhaps run off a key of ".secured" ? */
 +	emfc = g_hash_table_lookup(emf->inline_table, emf->part_id->str);
 +	if (emfc && emfc->valid) {
 +		em_format_format_secure(emf, stream, emfc->secured, camel_cipher_validity_clone(emfc->valid));
 +		return;
 +	}
 +
 +	mps = (CamelMultipartSigned *)camel_medium_get_content_object((CamelMedium *)part);
 +	if (!CAMEL_IS_MULTIPART_SIGNED(mps)
 +	    || (cpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
 +		em_format_format_error(emf, stream, _("Could not parse MIME message. Displaying as source."));
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	/* FIXME: Should be done via a plugin interface */
 +	/* FIXME: duplicated in em-format-html-display.c */
 +	if (mps->protocol) {
 +#ifdef ENABLE_SMIME
 +		if (g_ascii_strcasecmp("application/x-pkcs7-signature", mps->protocol) == 0
 +		    || g_ascii_strcasecmp("application/pkcs7-signature", mps->protocol) == 0)
 +			cipher = camel_smime_context_new(emf->session);
 +		else
 +#endif
 +			if (g_ascii_strcasecmp("application/pgp-signature", mps->protocol) == 0)
 +				cipher = camel_gpg_context_new(emf->session);
 +	}
 +
 +	if (cipher == NULL) {
 +		em_format_format_error(emf, stream, _("Unsupported signature format"));
 +		em_format_part_as(emf, stream, part, "multipart/mixed");
 +	} else {
 +		CamelException *ex = camel_exception_new();
 +		CamelCipherValidity *valid;
 +
 +		valid = camel_cipher_verify(cipher, part, ex);
 +		if (valid == NULL) {
 +			em_format_format_error(emf, stream, ex->desc?_("Error verifying signature"):_("Unknown error verifying signature"));
 +			if (ex->desc)
 +				em_format_format_error(emf, stream, "%s", ex->desc);
 +			em_format_part_as(emf, stream, part, "multipart/mixed");
 +		} else {
 +			if (emfc == NULL)
 +				emfc = emf_insert_cache(emf, emf->part_id->str);
 +
 +			emfc->valid = camel_cipher_validity_clone(valid);
 +			camel_object_ref((emfc->secured = cpart));
 +
 +			em_format_format_secure(emf, stream, cpart, valid);
 +		}
 +
 +		camel_exception_free(ex);
 +		camel_object_unref(cipher);
 +	}
 +}
 +
++/* RFC 4155 */
++static void
++emf_application_mbox (EMFormat *emf,
++                      CamelStream *stream,
++                      CamelMimePart *mime_part,
++                      const EMFormatHandler *info)
++{
++	const EMFormatHandler *handle;
++	CamelMimeParser *parser;
++	CamelStream *mem_stream;
++	camel_mime_parser_state_t state;
++
++	/* Extract messages from the application/mbox part and
++	 * render them as a flat list of messages. */
++
++	/* XXX If the mbox has multiple messages, maybe render them
++	 *     as a multipart/digest so each message can be expanded
++	 *     or collapsed individually.
++	 *
++	 *     See attachment_handler_mail_x_uid_list() for example. */
++
++	/* XXX This is based on em_utils_read_messages_from_stream().
++	 *     Perhaps refactor that function to return an array of
++	 *     messages instead of assuming we want to append them
++	 *     to a folder? */
++
++	handle = em_format_find_handler (emf, "x-evolution/message/rfc822");
++	g_return_if_fail (handle != NULL);
++
++	parser = camel_mime_parser_new ();
++	camel_mime_parser_scan_from (parser, TRUE);
++
++	mem_stream = camel_stream_mem_new ();
++	camel_data_wrapper_decode_to_stream (
++		CAMEL_DATA_WRAPPER (mime_part), mem_stream);
++	camel_seekable_stream_seek (
++		CAMEL_SEEKABLE_STREAM (mem_stream), 0, CAMEL_STREAM_SET);
++	camel_mime_parser_init_with_stream (parser, mem_stream);
++	camel_object_unref (mem_stream);
++
++	/* Extract messages from the mbox. */
++	state = camel_mime_parser_step (parser, NULL, NULL);
++	while (state == CAMEL_MIME_PARSER_STATE_FROM) {
++		CamelMimeMessage *message;
++
++		message = camel_mime_message_new ();
++		mime_part = CAMEL_MIME_PART (message);
++
++		if (camel_mime_part_construct_from_parser (mime_part, parser) == -1) {
++			camel_object_unref (message);
++			break;
++		}
++
++		/* Render the message. */
++		handle->handler (emf, stream, mime_part, handle);
++
++		camel_object_unref (message);
++
++		/* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
++		state = camel_mime_parser_step (parser, NULL, NULL);
++
++		state = camel_mime_parser_step (parser, NULL, NULL);
++	}
++
++	camel_object_unref (parser);
++}
++
 +static void
 +emf_message_rfc822(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
 +	const EMFormatHandler *handle;
 +	gint len;
 +
 +	if (!CAMEL_IS_MIME_MESSAGE(dw)) {
 +		em_format_format_source(emf, stream, part);
 +		return;
 +	}
 +
 +	len = emf->part_id->len;
 +	g_string_append_printf(emf->part_id, ".rfc822");
 +
 +	handle = em_format_find_handler(emf, "x-evolution/message/rfc822");
 +	if (handle)
 +		handle->handler(emf, stream, (CamelMimePart *)dw, handle);
 +
 +	g_string_truncate(emf->part_id, len);
 +}
 +
 +static void
 +emf_message_deliverystatus(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
 +{
 +	em_format_format_text(emf, stream, (CamelDataWrapper *)part);
 +}
 +
 +static void
 +emf_inlinepgp_signed(EMFormat *emf, CamelStream *stream, CamelMimePart *ipart, EMFormatHandler *info)
 +{
 +	CamelStreamFilter *filtered_stream;
 +	CamelMimeFilterPgp *pgp_filter;
 +	CamelContentType *content_type;
 +	CamelCipherContext *cipher;
 +	CamelCipherValidity *valid;
 +	CamelDataWrapper *dw;
 +	CamelMimePart *opart;
 +	CamelStream *ostream;
 +	CamelException *ex;
 +	gchar *type;
 +
 +	if (!ipart) {
 +		em_format_format_error(emf, stream, _("Unknown error verifying signature"));
 +		return;
 +	}
 +
 +	ex = camel_exception_new();
 +	cipher = camel_gpg_context_new(emf->session);
 +	/* Verify the signature of the message */
 +	valid = camel_cipher_verify(cipher, ipart, ex);
 +	if (!valid) {
 +		em_format_format_error(emf, stream, ex->desc?_("Error verifying signature"):_("Unknown error verifying signature"));
 +		if (ex->desc)
 +			em_format_format_error(emf, stream, "%s", ex->desc);
 +		em_format_format_source(emf, stream, ipart);
 +		/* I think this will loop: em_format_part_as(emf, stream, part, "text/plain"); */
 +		camel_exception_free(ex);
 +		camel_object_unref(cipher);
 +		return;
 +	}
 +
 +	/* Setup output stream */
 +	ostream = camel_stream_mem_new();
 +	filtered_stream = camel_stream_filter_new_with_stream(ostream);
 +
 +	/* Add PGP header / footer filter */
 +	pgp_filter = (CamelMimeFilterPgp *)camel_mime_filter_pgp_new();
 +	camel_stream_filter_add(filtered_stream, (CamelMimeFilter *)pgp_filter);
 +	camel_object_unref(pgp_filter);
 +
 +	/* Pass through the filters that have been setup */
 +	dw = camel_medium_get_content_object((CamelMedium *)ipart);
 +	camel_data_wrapper_decode_to_stream(dw, (CamelStream *)filtered_stream);
 +	camel_stream_flush((CamelStream *)filtered_stream);
 +	camel_object_unref(filtered_stream);
 +
 +	/* Create a new text/plain MIME part containing the signed content preserving the original part's Content-Type params */
 +	content_type = camel_mime_part_get_content_type (ipart);
 +	type = camel_content_type_format (content_type);
 +	content_type = camel_content_type_decode (type);
 +	g_free (type);
 +
 +	g_free (content_type->type);
 +	content_type->type = g_strdup ("text");
 +	g_free (content_type->subtype);
 +	content_type->subtype = g_strdup ("plain");
 +	type = camel_content_type_format (content_type);
 +	camel_content_type_unref (content_type);
 +
 +	dw = camel_data_wrapper_new ();
 +	camel_data_wrapper_construct_from_stream (dw, ostream);
 +	camel_data_wrapper_set_mime_type (dw, type);
 +	g_free (type);
 +
 +	opart = camel_mime_part_new ();
 +	camel_medium_set_content_object ((CamelMedium *) opart, dw);
 +	camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) opart, dw->mime_type);
 +
 +	/* Pass it off to the real formatter */
 +	em_format_format_secure(emf, stream, opart, valid);
 +
 +	/* Clean Up */
 +	camel_object_unref(dw);
 +	camel_object_unref(opart);
 +	camel_object_unref(ostream);
 +	camel_object_unref(cipher);
 +	camel_exception_free(ex);
 +}
 +
 +static void
 +emf_inlinepgp_encrypted(EMFormat *emf, CamelStream *stream, CamelMimePart *ipart, EMFormatHandler *info)
 +{
 +	CamelCipherContext *cipher;
 +	CamelCipherValidity *valid;
 +	CamelException *ex;
 +	CamelMimePart *opart;
 +	CamelDataWrapper *dw;
 +	gchar *mime_type;
 +
 +	cipher = camel_gpg_context_new(emf->session);
 +	ex = camel_exception_new();
 +	opart = camel_mime_part_new();
 +	/* Decrypt the message */
 +	valid = camel_cipher_decrypt (cipher, ipart, opart, ex);
 +	if (!valid) {
 +		em_format_format_error(emf, stream, ex->desc?_("Could not parse PGP message"):_("Could not parse PGP message: Unknown error"));
 +		if (ex->desc)
 +			em_format_format_error(emf, stream, "%s", ex->desc);
 +		em_format_format_source(emf, stream, ipart);
 +		/* I think this will loop: em_format_part_as(emf, stream, part, "text/plain"); */
 +		camel_exception_free(ex);
 +		camel_object_unref(cipher);
 +		camel_object_unref(opart);
 +		return;
 +	}
 +
 +	dw = camel_medium_get_content_object ((CamelMedium *)opart);
 +	mime_type = camel_data_wrapper_get_mime_type (dw);
 +
 +	/* this ensures to show the 'opart' as inlined, if possible */
 +	if (mime_type && g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) {
 +		const gchar *snoop = em_format_snoop_type (opart);
 +
 +		if (snoop)
 +			camel_data_wrapper_set_mime_type (dw, snoop);
 +	}
 +
 +	g_free (mime_type);
 +
 +	/* Pass it off to the real formatter */
 +	em_format_format_secure(emf, stream, opart, valid);
 +
 +	/* Clean Up */
 +	camel_object_unref(opart);
 +	camel_object_unref (cipher);
 +	camel_exception_free (ex);
 +}
 +
 +static EMFormatHandler type_builtin_table[] = {
 +#ifdef ENABLE_SMIME
 +	{ (gchar *) "application/x-pkcs7-mime", (EMFormatFunc)emf_application_xpkcs7mime, EM_FORMAT_HANDLER_INLINE_DISPOSITION },
 +#endif
++	{ (gchar *) "application/mbox", emf_application_mbox, EM_FORMAT_HANDLER_INLINE },
 +	{ (gchar *) "multipart/alternative", emf_multipart_alternative },
 +	{ (gchar *) "multipart/appledouble", emf_multipart_appledouble },
 +	{ (gchar *) "multipart/encrypted", emf_multipart_encrypted },
 +	{ (gchar *) "multipart/mixed", emf_multipart_mixed },
 +	{ (gchar *) "multipart/signed", emf_multipart_signed },
 +	{ (gchar *) "multipart/related", emf_multipart_related },
 +	{ (gchar *) "multipart/*", emf_multipart_mixed },
 +	{ (gchar *) "message/rfc822", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
 +	{ (gchar *) "message/news", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
 +	{ (gchar *) "message/delivery-status", emf_message_deliverystatus },
 +	{ (gchar *) "message/*", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
 +
 +	/* Insert brokenly-named parts here */
 +#ifdef ENABLE_SMIME
 +	{ (gchar *) "application/pkcs7-mime", (EMFormatFunc)emf_application_xpkcs7mime, EM_FORMAT_HANDLER_INLINE_DISPOSITION },
 +#endif
 +
 +	/* internal types */
 +	{ (gchar *) "application/x-inlinepgp-signed", (EMFormatFunc)emf_inlinepgp_signed },
 +	{ (gchar *) "application/x-inlinepgp-encrypted", (EMFormatFunc)emf_inlinepgp_encrypted },
 +};
 +
 +static void
 +emf_builtin_init(EMFormatClass *class)
 +{
 +	gint i;
 +
 +	for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
 +		g_hash_table_insert(class->type_handlers, type_builtin_table[i].mime_type, &type_builtin_table[i]);
 +}
 +
 +/**
 + * em_format_snoop_type:
 + * @part:
 + *
 + * Tries to snoop the mime type of a part.
 + *
 + * Return value: NULL if unknown (more likely application/octet-stream).
 + **/
 +const gchar *
 +em_format_snoop_type (CamelMimePart *part)
 +{
 +	/* cache is here only to be able still return const gchar * */
 +	static GHashTable *types_cache = NULL;
 +
 +	const gchar *filename;
 +	gchar *name_type = NULL, *magic_type = NULL, *res, *tmp;
 +	CamelDataWrapper *dw;
 +
 +	filename = camel_mime_part_get_filename (part);
 +	if (filename != NULL)
 +		name_type = e_util_guess_mime_type (filename, FALSE);
 +
 +	dw = camel_medium_get_content_object((CamelMedium *)part);
 +	if (!camel_data_wrapper_is_offline(dw)) {
 +		CamelStreamMem *mem = (CamelStreamMem *)camel_stream_mem_new();
 +
 +		if (camel_data_wrapper_decode_to_stream(dw, (CamelStream *)mem) > 0) {
 +			gchar *ct = g_content_type_guess (filename, mem->buffer->data, mem->buffer->len, NULL);
 +
 +			if (ct)
 +				magic_type = g_content_type_get_mime_type (ct);
 +
 +			g_free (ct);
 +		}
 +		camel_object_unref(mem);
 +	}
 +
 +	d(printf("snooped part, magic_type '%s' name_type '%s'\n", magic_type, name_type));
 +
 +	/* If gvfs doesn't recognize the data by magic, but it
 +	 * contains English words, it will call it text/plain. If the
 +	 * filename-based check came up with something different, use
 +	 * that instead and if it returns "application/octet-stream"
 +	 * try to do better with the filename check.
 +	 */
 +
 +	if (magic_type) {
 +		if (name_type
 +		    && (!strcmp(magic_type, "text/plain")
 +			|| !strcmp(magic_type, "application/octet-stream")))
 +			res = name_type;
 +		else
 +			res = magic_type;
 +	} else
 +		res = name_type;
 +
 +
 +	if (res != name_type)
 +		g_free (name_type);
 +
 +	if (res != magic_type)
 +		g_free (magic_type);
 +
 +	if (!types_cache)
 +		types_cache = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) NULL);
 +
 +	if (res) {
 +		tmp = g_hash_table_lookup (types_cache, res);
 +		if (tmp) {
 +			g_free (res);
 +			res = tmp;
 +		} else {
 +			g_hash_table_insert (types_cache, res, res);
 +		}
 +	}
 +
 +	return res;
 +
 +	/* We used to load parts to check their type, we dont anymore,
 +	   see bug #11778 for some discussion */
 +}
diff --cc mail/e-mail-reader.c
index 346344c,0000000..7ffc873
mode 100644,000000..100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@@ -1,2742 -1,0 +1,2744 @@@
 +/*
 + * e-mail-reader.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 <http://www.gnu.org/licenses/>
 + *
 + *
 + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 + *
 + */
 +
 +#include "e-mail-reader.h"
 +
 +#include <glib/gi18n.h>
 +#include <gdk/gdkkeysyms.h>
 +#include <gtkhtml/gtkhtml.h>
 +#include <gtkhtml/gtkhtml-stream.h>
 +
 +#ifdef HAVE_XFREE
 +#include <X11/XF86keysym.h>
 +#endif
 +
 +#include "e-util/e-util.h"
 +#include "e-util/e-binding.h"
 +#include "e-util/gconf-bridge.h"
 +#include "shell/e-shell.h"
 +#include "widgets/misc/e-charset-picker.h"
 +#include "widgets/misc/e-popup-action.h"
 +
 +#include "mail/e-mail-browser.h"
 +#include "mail/e-mail-display.h"
 +#include "mail/e-mail-reader-utils.h"
 +#include "mail/em-composer-utils.h"
 +#include "mail/em-event.h"
 +#include "mail/em-folder-selector.h"
 +#include "mail/em-folder-tree.h"
 +#include "mail/em-utils.h"
 +#include "mail/mail-autofilter.h"
 +#include "mail/mail-config.h"
 +#include "mail/mail-ops.h"
 +#include "mail/mail-vfolder.h"
 +
 +enum {
 +	CHANGED,
 +	FOLDER_LOADED,
 +	SHOW_SEARCH_BAR,
 +	LAST_SIGNAL
 +};
 +
 +/* Remembers the previously selected folder when transferring messages. */
 +static gchar *default_xfer_messages_uri;
 +
 +static guint signals[LAST_SIGNAL];
 +
 +static void
 +action_mail_add_sender_cb (GtkAction *action,
 +                           EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelMessageInfo *info;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +	const gchar *address;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	if (uids->len != 1)
 +		goto exit;
 +
 +	info = camel_folder_get_message_info (folder, uids->pdata[0]);
 +	if (info == NULL)
 +		goto exit;
 +
 +	address = camel_message_info_from (info);
 +	if (address == NULL || *address == '\0')
 +		goto exit;
 +
 +	em_utils_add_address (window, address);
 +
 +exit:
 +	em_utils_uids_free (uids);
 +}
 +
 +static void
 +action_mail_charset_cb (GtkRadioAction *action,
 +                        GtkRadioAction *current,
 +                        EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	const gchar *charset;
 +
 +	if (action != current)
 +		return;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	charset = g_object_get_data (G_OBJECT (action), "charset");
 +
 +	/* Charset for "Default" action will be NULL. */
 +	em_format_set_charset (EM_FORMAT (html_display), charset);
 +}
 +
 +static void
 +action_mail_check_for_junk_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GPtrArray *uids;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	mail_filter_junk (folder, uids);
 +}
 +
 +static void
 +action_mail_clipboard_copy_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	GtkHTML *html;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	html = EM_FORMAT_HTML (html_display)->html;
 +
 +	gtk_html_copy (html);
 +}
 +
 +static void
 +action_mail_copy_cb (GtkAction *action,
 +                     EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWidget *folder_tree;
 +	GtkWidget *dialog;
 +	GPtrArray *selected;
 +	const gchar *uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder_tree = em_folder_tree_new ();
 +	selected = message_list_get_selected (message_list);
 +
 +	folder = message_list->folder;
 +
 +	em_folder_tree_set_excluded (
 +		EM_FOLDER_TREE (folder_tree),
 +		EMFT_EXCLUDE_NOSELECT | EMFT_EXCLUDE_VIRTUAL |
 +		EMFT_EXCLUDE_VTRASH);
 +
 +	dialog = em_folder_selector_new (
 +		EM_FOLDER_TREE (folder_tree),
 +		EM_FOLDER_SELECTOR_CAN_CREATE,
 +		_("Select Folder"), NULL, _("C_opy"));
 +
 +	if (default_xfer_messages_uri != NULL)
 +		em_folder_selector_set_selected (
 +			EM_FOLDER_SELECTOR (dialog),
 +			default_xfer_messages_uri);
 +
 +	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
 +		goto exit;
 +
 +	uri = em_folder_selector_get_selected_uri (
 +		EM_FOLDER_SELECTOR (dialog));
 +
 +	g_free (default_xfer_messages_uri);
 +	default_xfer_messages_uri = g_strdup (uri);
 +
 +	if (uri != NULL) {
 +		mail_transfer_messages (
 +			folder, selected, FALSE, uri, 0, NULL, NULL);
 +		selected = NULL;
 +	}
 +
 +exit:
 +	if (selected != NULL)
 +		em_utils_uids_free (selected);
 +
 +	gtk_widget_destroy (dialog);
 +}
 +
 +static void
 +action_mail_delete_cb (GtkAction *action,
 +                       EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED;
 +	guint32 set  = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED;
 +
 +	if (!e_mail_reader_confirm_delete (reader))
 +		return;
 +
 +	/* FIXME Verify all selected messages are deletable.
 +	 *       But handle it by disabling this action. */
 +
 +	if (e_mail_reader_mark_selected (reader, mask, set) == 1)
 +		e_mail_reader_select_next_message (reader, FALSE);
 +}
 +
 +static void
 +action_mail_filter_on_mailing_list_cb (GtkAction *action,
 +                                       EMailReader *reader)
 +{
 +	e_mail_reader_create_filter_from_selected (reader, AUTO_MLIST);
 +}
 +
 +static void
 +action_mail_filter_on_recipients_cb (GtkAction *action,
 +                                     EMailReader *reader)
 +{
 +	e_mail_reader_create_filter_from_selected (reader, AUTO_TO);
 +}
 +
 +static void
 +action_mail_filter_on_sender_cb (GtkAction *action,
 +                                 EMailReader *reader)
 +{
 +	e_mail_reader_create_filter_from_selected (reader, AUTO_FROM);
 +}
 +
 +static void
 +action_mail_filter_on_subject_cb (GtkAction *action,
 +                                  EMailReader *reader)
 +{
 +	e_mail_reader_create_filter_from_selected (reader, AUTO_SUBJECT);
 +}
 +
 +static void
 +action_mail_filters_apply_cb (GtkAction *action,
 +                              EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GPtrArray *uids;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	mail_filter_on_demand (folder, uids);
 +}
 +
 +static void
 +action_mail_find_cb (GtkAction *action,
 +                     EMailReader *reader)
 +{
 +	e_mail_reader_show_search_bar (reader);
 +}
 +
 +static void
 +action_mail_flag_clear_cb (GtkAction *action,
 +                           EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_flag_for_followup_clear (window, folder, uids);
 +
 +	em_format_redraw (EM_FORMAT (html_display));
 +}
 +
 +static void
 +action_mail_flag_completed_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_flag_for_followup_completed (window, folder, uids);
 +
 +	em_format_redraw (EM_FORMAT (html_display));
 +}
 +
 +static void
 +action_mail_flag_for_followup_cb (GtkAction *action,
 +                                  EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_flag_for_followup (reader, folder, uids);
 +}
 +
 +static void
 +action_mail_forward_cb (GtkAction *action,
 +                        EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +	const gchar *folder_uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	folder_uri = message_list->folder_uri;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_forward_messages (folder, uids, folder_uri);
 +}
 +
 +static void
 +action_mail_forward_attached_cb (GtkAction *action,
 +                                 EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +	const gchar *folder_uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	folder_uri = message_list->folder_uri;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_forward_attached (folder, uids, folder_uri);
 +}
 +
 +static void
 +action_mail_forward_inline_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +	const gchar *folder_uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	folder_uri = message_list->folder_uri;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_forward_inline (folder, uids, folder_uri);
 +}
 +
 +static void
 +action_mail_forward_quoted_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +	const gchar *folder_uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	folder_uri = message_list->folder_uri;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_forward_quoted (folder, uids, folder_uri);
 +}
 +
 +static void
 +action_mail_load_images_cb (GtkAction *action,
 +                            EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +
 +	em_format_html_load_images (EM_FORMAT_HTML (html_display));
 +}
 +
 +static void
 +action_mail_mark_important_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_DELETED;
 +	guint32 set  = CAMEL_MESSAGE_FLAGGED;
 +
 +	e_mail_reader_mark_selected (reader, mask, set);
 +}
 +
 +static void
 +action_mail_mark_junk_cb (GtkAction *action,
 +                          EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_JUNK |
 +		CAMEL_MESSAGE_NOTJUNK | CAMEL_MESSAGE_JUNK_LEARN;
 +	guint32 set  = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_JUNK |
 +		CAMEL_MESSAGE_JUNK_LEARN;
 +
 +	if (e_mail_reader_mark_selected (reader, mask, set) == 1)
 +		e_mail_reader_select_next_message (reader, TRUE);
 +}
 +
 +static void
 +action_mail_mark_notjunk_cb (GtkAction *action,
 +                             EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_JUNK | CAMEL_MESSAGE_NOTJUNK |
 +		CAMEL_MESSAGE_JUNK_LEARN;
 +	guint32 set  = CAMEL_MESSAGE_NOTJUNK | CAMEL_MESSAGE_JUNK_LEARN;
 +
 +	if (e_mail_reader_mark_selected (reader, mask, set) == 1)
 +		e_mail_reader_select_next_message (reader, TRUE);
 +}
 +
 +static void
 +action_mail_mark_read_cb (GtkAction *action,
 +                          EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_SEEN;
 +	guint32 set  = CAMEL_MESSAGE_SEEN;
 +
 +	e_mail_reader_mark_selected (reader, mask, set);
 +}
 +
 +static void
 +action_mail_mark_unimportant_cb (GtkAction *action,
 +                                 EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_FLAGGED;
 +	guint32 set  = 0;
 +
 +	e_mail_reader_mark_selected (reader, mask, set);
 +}
 +
 +static void
 +action_mail_mark_unread_cb (GtkAction *action,
 +                            EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	guint32 mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED;
 +	guint32 set  = 0;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	e_mail_reader_mark_selected (reader, mask, set);
 +
 +	if (message_list->seen_id != 0) {
 +		g_source_remove (message_list->seen_id);
 +		message_list->seen_id = 0;
 +	}
 +}
 +
 +static void
 +action_mail_message_edit_cb (GtkAction *action,
 +                             EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +
 +	window = e_mail_reader_get_window (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_edit_messages (folder, uids, FALSE);
 +}
 +
 +static void
 +action_mail_message_new_cb (GtkAction *action,
 +                            EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	GtkWindow *window;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	em_utils_compose_new_message (message_list->folder_uri);
 +}
 +
 +static void
 +action_mail_message_open_cb (GtkAction *action,
 +                             EMailReader *reader)
 +{
 +	e_mail_reader_open_selected (reader);
 +}
 +
 +static void
 +action_mail_move_cb (GtkAction *action,
 +                     EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWidget *folder_tree;
 +	GtkWidget *dialog;
 +	GPtrArray *selected;
 +	const gchar *uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder_tree = em_folder_tree_new ();
 +	selected = message_list_get_selected (message_list);
 +
 +	folder = message_list->folder;
 +
 +	em_folder_tree_set_excluded (
 +		EM_FOLDER_TREE (folder_tree),
 +		EMFT_EXCLUDE_NOSELECT | EMFT_EXCLUDE_VIRTUAL |
 +		EMFT_EXCLUDE_VTRASH);
 +
 +	dialog = em_folder_selector_new (
 +		EM_FOLDER_TREE (folder_tree),
 +		EM_FOLDER_SELECTOR_CAN_CREATE,
 +		_("Select Folder"), NULL, _("_Move"));
 +
 +	if (default_xfer_messages_uri != NULL)
 +		em_folder_selector_set_selected (
 +			EM_FOLDER_SELECTOR (dialog),
 +			default_xfer_messages_uri);
 +
 +	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
 +		goto exit;
 +
 +	uri = em_folder_selector_get_selected_uri (
 +		EM_FOLDER_SELECTOR (dialog));
 +
 +	g_free (default_xfer_messages_uri);
 +	default_xfer_messages_uri = g_strdup (uri);
 +
 +	if (uri != NULL) {
 +		mail_transfer_messages (
 +			folder, selected, TRUE, uri, 0, NULL, NULL);
 +		selected = NULL;
 +	}
 +
 +exit:
 +	if (selected != NULL)
 +		em_utils_uids_free (selected);
 +
 +	gtk_widget_destroy (dialog);
 +}
 +
 +static void
 +action_mail_next_cb (GtkAction *action,
 +                     EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	MessageListSelectDirection direction;
 +	guint32 flags, mask;
 +
 +	direction = MESSAGE_LIST_SELECT_NEXT;
 +	flags = 0;
 +	mask  = 0;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select (message_list, direction, flags, mask);
 +}
 +
 +static void
 +action_mail_next_important_cb (GtkAction *action,
 +                               EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	MessageListSelectDirection direction;
 +	guint32 flags, mask;
 +
 +	direction = MESSAGE_LIST_SELECT_NEXT | MESSAGE_LIST_SELECT_WRAP;
 +	flags = CAMEL_MESSAGE_FLAGGED;
 +	mask  = CAMEL_MESSAGE_FLAGGED;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select (message_list, direction, flags, mask);
 +}
 +
 +static void
 +action_mail_next_thread_cb (GtkAction *action,
 +                            EMailReader *reader)
 +{
 +	MessageList *message_list;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select_next_thread (message_list);
 +}
 +
 +static void
 +action_mail_next_unread_cb (GtkAction *action,
 +                            EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	MessageListSelectDirection direction;
 +	guint32 flags, mask;
 +
 +	direction = MESSAGE_LIST_SELECT_NEXT | MESSAGE_LIST_SELECT_WRAP;
 +	flags = 0;
 +	mask  = CAMEL_MESSAGE_SEEN;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select (message_list, direction, flags, mask);
 +}
 +
 +static void
 +action_mail_previous_cb (GtkAction *action,
 +                         EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	MessageListSelectDirection direction;
 +	guint32 flags, mask;
 +
 +	direction = MESSAGE_LIST_SELECT_PREVIOUS;
 +	flags = 0;
 +	mask  = 0;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select (message_list, direction, flags, mask);
 +}
 +
 +static void
 +action_mail_previous_important_cb (GtkAction *action,
 +                                   EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	MessageListSelectDirection direction;
 +	guint32 flags, mask;
 +
 +	direction = MESSAGE_LIST_SELECT_PREVIOUS | MESSAGE_LIST_SELECT_WRAP;
 +	flags = CAMEL_MESSAGE_FLAGGED;
 +	mask  = CAMEL_MESSAGE_FLAGGED;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select (message_list, direction, flags, mask);
 +}
 +
 +static void
 +action_mail_previous_unread_cb (GtkAction *action,
 +                                EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	MessageListSelectDirection direction;
 +	guint32 flags, mask;
 +
 +	direction = MESSAGE_LIST_SELECT_PREVIOUS | MESSAGE_LIST_SELECT_WRAP;
 +	flags = 0;
 +	mask  = CAMEL_MESSAGE_SEEN;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select (message_list, direction, flags, mask);
 +}
 +
 +static void
 +action_mail_print_cb (GtkAction *action,
 +                      EMailReader *reader)
 +{
 +	GtkPrintOperationAction print_action;
 +
 +	print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG;
 +	e_mail_reader_print (reader, print_action);
 +}
 +
 +static void
 +action_mail_print_preview_cb (GtkAction *action,
 +                              EMailReader *reader)
 +{
 +	GtkPrintOperationAction print_action;
 +
 +	print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW;
 +	e_mail_reader_print (reader, print_action);
 +}
 +
 +static void
 +action_mail_redirect_cb (GtkAction *action,
 +                         EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	const gchar *uid;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	uid = message_list->cursor_uid;
 +	g_return_if_fail (uid != NULL);
 +
 +	em_utils_redirect_message_by_uid (folder, uid);
 +}
 +
 +static void
 +action_mail_reply_all_cb (GtkAction *action,
 +                          EMailReader *reader)
 +{
 +	e_mail_reader_reply_to_message (reader, REPLY_MODE_ALL);
 +}
 +
 +static void
 +action_mail_reply_list_cb (GtkAction *action,
 +                           EMailReader *reader)
 +{
 +	e_mail_reader_reply_to_message (reader, REPLY_MODE_LIST);
 +}
 +
 +static void
 +action_mail_reply_sender_cb (GtkAction *action,
 +                             EMailReader *reader)
 +{
 +	e_mail_reader_reply_to_message (reader, REPLY_MODE_SENDER);
 +}
 +
 +static void
 +action_mail_save_as_cb (GtkAction *action,
 +                        EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWindow *window;
 +	GPtrArray *uids;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	window = e_mail_reader_get_window (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	em_utils_save_messages (window, folder, uids);
 +}
 +
 +static void
 +action_mail_search_folder_from_mailing_list_cb (GtkAction *action,
 +                                                EMailReader *reader)
 +{
 +	e_mail_reader_create_vfolder_from_selected (reader, AUTO_MLIST);
 +}
 +
 +static void
 +action_mail_search_folder_from_recipients_cb (GtkAction *action,
 +                                              EMailReader *reader)
 +{
 +	e_mail_reader_create_vfolder_from_selected (reader, AUTO_TO);
 +}
 +
 +static void
 +action_mail_search_folder_from_sender_cb (GtkAction *action,
 +                                          EMailReader *reader)
 +{
 +	e_mail_reader_create_vfolder_from_selected (reader, AUTO_FROM);
 +}
 +
 +static void
 +action_mail_search_folder_from_subject_cb (GtkAction *action,
 +                                           EMailReader *reader)
 +{
 +	e_mail_reader_create_vfolder_from_selected (reader, AUTO_SUBJECT);
 +}
 +
 +static void
 +action_mail_select_all_cb (GtkAction *action,
 +                           EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	GtkHTML *html;
 +	const gchar *action_name;
 +	gboolean selection_active;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	html = EM_FORMAT_HTML (html_display)->html;
 +
 +	gtk_html_select_all (html);
 +
 +	action_name = "mail-clipboard-copy";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	selection_active = gtk_html_command (html, "is-selection-active");
 +	gtk_action_set_sensitive (action, selection_active);
 +}
 +
 +static void
 +action_mail_show_all_headers_cb (GtkToggleAction *action,
 +                                 EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	em_format_mode_t mode;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +
 +	if (gtk_toggle_action_get_active (action))
 +		mode = EM_FORMAT_ALLHEADERS;
 +	else
 +		mode = EM_FORMAT_NORMAL;
 +
 +	em_format_set_mode (EM_FORMAT (html_display), mode);
 +}
 +
 +static void
 +action_mail_show_source_cb (GtkAction *action,
 +                            EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	EShellBackend *shell_backend;
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GtkWidget *browser;
 +	GPtrArray *uids;
 +	const gchar *folder_uri;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	shell_backend = e_mail_reader_get_shell_backend (reader);
 +
 +	folder = message_list->folder;
 +	folder_uri = message_list->folder_uri;
 +	uids = message_list_get_selected (message_list);
 +	g_return_if_fail (uids->len > 0);
 +
 +	browser = e_mail_browser_new (shell_backend);
 +	reader = E_MAIL_READER (browser);
 +	html_display = e_mail_reader_get_html_display (reader);
 +	em_format_set_mode (EM_FORMAT (html_display), EM_FORMAT_SOURCE);
 +	e_mail_reader_set_folder (reader, folder, folder_uri);
 +	e_mail_reader_set_message (reader, uids->pdata[0], FALSE);
 +	gtk_widget_show (browser);
 +
 +	message_list_free_uids (message_list, uids);
 +}
 +
 +static void
 +action_mail_toggle_important_cb (GtkAction *action,
 +                                 EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	GPtrArray *uids;
 +	guint ii;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder = message_list->folder;
 +	uids = message_list_get_selected (message_list);
 +
 +	camel_folder_freeze (folder);
 +
 +	for (ii = 0; ii < uids->len; ii++) {
 +		guint32 flags;
 +
 +		flags = camel_folder_get_message_flags (
 +			folder, uids->pdata[ii]);
 +		flags ^= CAMEL_MESSAGE_FLAGGED;
 +		if (flags & CAMEL_MESSAGE_FLAGGED)
 +			flags &= ~CAMEL_MESSAGE_DELETED;
 +		camel_folder_set_message_flags (
 +			folder, uids->pdata[ii], CAMEL_MESSAGE_FLAGGED |
 +			CAMEL_MESSAGE_DELETED, flags);
 +	}
 +
 +	camel_folder_thaw (folder);
 +
 +	message_list_free_uids (message_list, uids);
 +}
 +
 +static void
 +action_mail_undelete_cb (GtkAction *action,
 +                         EMailReader *reader)
 +{
 +	guint32 mask = CAMEL_MESSAGE_DELETED;
 +	guint32 set  = 0;
 +
 +	e_mail_reader_mark_selected (reader, mask, set);
 +}
 +
 +static void
 +action_mail_zoom_100_cb (GtkAction *action,
 +                         EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	GtkHTML *html;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	html = EM_FORMAT_HTML (html_display)->html;
 +
 +	gtk_html_zoom_reset (html);
 +}
 +
 +static void
 +action_mail_zoom_in_cb (GtkAction *action,
 +                        EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	GtkHTML *html;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	html = EM_FORMAT_HTML (html_display)->html;
 +
 +	gtk_html_zoom_out (html);
 +}
 +
 +static void
 +action_mail_zoom_out_cb (GtkAction *action,
 +                         EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	GtkHTML *html;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	html = EM_FORMAT_HTML (html_display)->html;
 +
 +	gtk_html_zoom_out (html);
 +}
 +
 +static void
 +action_search_folder_recipient_cb (GtkAction *action,
 +                                   EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	EMailDisplay *display;
 +	CamelURL *curl;
 +	const gchar *uri;
 +
 +	/* This action is defined in EMailDisplay. */
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	display = E_MAIL_DISPLAY (EM_FORMAT_HTML (html_display)->html);
 +
 +	uri = e_mail_display_get_selected_uri (display);
 +	g_return_if_fail (uri != NULL);
 +
 +	curl = camel_url_new (uri, NULL);
 +	g_return_if_fail (curl != NULL);
 +
 +	if (curl->path != NULL && *curl->path != '\0') {
 +		CamelInternetAddress *inet_addr;
 +		const gchar *folder_uri;
 +
 +		/* Ensure vfolder is running. */
 +		vfolder_load_storage ();
 +
 +		folder_uri = message_list->folder_uri;
 +
 +		inet_addr = camel_internet_address_new ();
 +		camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
 +		vfolder_gui_add_from_address (inet_addr, AUTO_TO, folder_uri);
 +		camel_object_unref (inet_addr);
 +	}
 +
 +	camel_url_free (curl);
 +}
 +
 +static void
 +action_search_folder_sender_cb (GtkAction *action,
 +                                EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	EMailDisplay *display;
 +	CamelURL *curl;
 +	const gchar *uri;
 +
 +	/* This action is defined in EMailDisplay. */
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	display = E_MAIL_DISPLAY (EM_FORMAT_HTML (html_display)->html);
 +
 +	uri = e_mail_display_get_selected_uri (display);
 +	g_return_if_fail (uri != NULL);
 +
 +	curl = camel_url_new (uri, NULL);
 +	g_return_if_fail (curl != NULL);
 +
 +	if (curl->path != NULL && *curl->path != '\0') {
 +		CamelInternetAddress *inet_addr;
 +		const gchar *folder_uri;
 +
 +		/* Ensure vfolder is running. */
 +		vfolder_load_storage ();
 +
 +		folder_uri = message_list->folder_uri;
 +
 +		inet_addr = camel_internet_address_new ();
 +		camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
 +		vfolder_gui_add_from_address (inet_addr, AUTO_FROM, folder_uri);
 +		camel_object_unref (inet_addr);
 +	}
 +
 +	camel_url_free (curl);
 +}
 +
 +static GtkActionEntry mail_reader_entries[] = {
 +
 +	{ "mail-add-sender",
 +	  NULL,
 +	  N_("A_dd Sender to Address Book"),
 +	  NULL,
 +	  N_("Add sender to address book"),
 +	  G_CALLBACK (action_mail_add_sender_cb) },
 +
 +	{ "mail-check-for-junk",
 +	  "mail-mark-junk",
 +	  N_("Check for _Junk"),
 +	  NULL,
 +	  N_("Filter the selected messages for junk status"),
 +	  G_CALLBACK (action_mail_check_for_junk_cb) },
 +
 +	{ "mail-clipboard-copy",
 +	  GTK_STOCK_COPY,
 +	  NULL,
 +	  NULL,
 +	  N_("Copy selected messages to the clipboard"),
 +	  G_CALLBACK (action_mail_clipboard_copy_cb) },
 +
 +	{ "mail-copy",
 +	  "mail-copy",
 +	  N_("_Copy to Folder..."),
 +	  "<Shift><Control>y",
 +	  N_("Copy selected messages to another folder"),
 +	  G_CALLBACK (action_mail_copy_cb) },
 +
 +	{ "mail-delete",
 +	  "user-trash",
 +	  N_("_Delete Message"),
 +	  "<Control>d",
 +	  N_("Mark the selected messages for deletion"),
 +	  G_CALLBACK (action_mail_delete_cb) },
 +
 +	{ "mail-filter-on-mailing-list",
 +	  NULL,
 +	  N_("Filter on Mailing _List..."),
 +	  NULL,
 +	  N_("Create a rule to filter messages to this mailing list"),
 +	  G_CALLBACK (action_mail_filter_on_mailing_list_cb) },
 +
 +	{ "mail-filter-on-recipients",
 +	  NULL,
 +	  N_("Filter on _Recipients..."),
 +	  NULL,
 +	  N_("Create a rule to filter messages to these recipients"),
 +	  G_CALLBACK (action_mail_filter_on_recipients_cb) },
 +
 +	{ "mail-filter-on-sender",
 +	  NULL,
 +	  N_("Filter on Se_nder..."),
 +	  NULL,
 +	  N_("Create a rule to filter messages from this sender"),
 +	  G_CALLBACK (action_mail_filter_on_sender_cb) },
 +
 +	{ "mail-filter-on-subject",
 +	  NULL,
 +	  N_("Filter on _Subject..."),
 +	  NULL,
 +	  N_("Create a rule to filter messages with this subject"),
 +	  G_CALLBACK (action_mail_filter_on_subject_cb) },
 +
 +	{ "mail-filters-apply",
 +	  "stock_mail-filters-apply",
 +	  N_("A_pply Filters"),
 +	  "<Control>y",
 +	  N_("Apply filter rules to the selected messages"),
 +	  G_CALLBACK (action_mail_filters_apply_cb) },
 +
 +	{ "mail-find",
 +	  GTK_STOCK_FIND,
 +	  N_("_Find in Message..."),
 +	  "<Shift><Control>f",
 +	  N_("Search for text in the body of the displayed message"),
 +	  G_CALLBACK (action_mail_find_cb) },
 +
 +	{ "mail-flag-clear",
 +	  NULL,
 +	  N_("_Clear Flag"),
 +	  NULL,
 +	  N_("Remove the follow-up flag from the selected messages"),
 +	  G_CALLBACK (action_mail_flag_clear_cb) },
 +
 +	{ "mail-flag-completed",
 +	  NULL,
 +	  N_("_Flag Completed"),
 +	  NULL,
 +	  N_("Set the follow-up flag to completed on the selected messages"),
 +	  G_CALLBACK (action_mail_flag_completed_cb) },
 +
 +	{ "mail-flag-for-followup",
 +	  "stock_mail-flag-for-followup",
 +	  N_("Follow _Up..."),
 +	  "<Shift><Control>g",
 +	  N_("Flag the selected messages for follow-up"),
 +	  G_CALLBACK (action_mail_flag_for_followup_cb) },
 +
 +	{ "mail-forward",
 +	  "mail-forward",
 +	  N_("_Forward"),
 +	  "<Control>f",
 +	  N_("Forward the selected message to someone"),
 +	  G_CALLBACK (action_mail_forward_cb) },
 +
 +	{ "mail-forward-attached",
 +	  NULL,
 +	  N_("_Attached"),
 +	  NULL,
 +	  N_("Forward the selected message to someone as an attachment"),
 +	  G_CALLBACK (action_mail_forward_attached_cb) },
 +
 +	{ "mail-forward-inline",
 +	  NULL,
 +	  N_("_Inline"),
 +	  NULL,
 +	  N_("Forward the selected message in the body of a new message"),
 +	  G_CALLBACK (action_mail_forward_inline_cb) },
 +
 +	{ "mail-forward-quoted",
 +	  NULL,
 +	  N_("_Quoted"),
 +	  NULL,
 +	  N_("Forward the selected message quoted like a reply"),
 +	  G_CALLBACK (action_mail_forward_quoted_cb) },
 +
 +	{ "mail-load-images",
 +	  "image-x-generic",
 +	  N_("_Load Images"),
 +	  "<Control>i",
 +	  N_("Force images in HTML mail to be loaded"),
 +	  G_CALLBACK (action_mail_load_images_cb) },
 +
 +	{ "mail-mark-important",
 +	  "mail-mark-important",
 +	  N_("_Important"),
 +	  NULL,
 +	  N_("Mark the selected messages as important"),
 +	  G_CALLBACK (action_mail_mark_important_cb) },
 +
 +	{ "mail-mark-junk",
 +	  "mail-mark-junk",
 +	  N_("_Junk"),
 +	  "<Control>j",
 +	  N_("Mark the selected messages as junk"),
 +	  G_CALLBACK (action_mail_mark_junk_cb) },
 +
 +	{ "mail-mark-notjunk",
 +	  "mail-mark-notjunk",
 +	  N_("_Not Junk"),
 +	  "<Shift><Control>j",
 +	  N_("Mark the selected messasges as not being junk"),
 +	  G_CALLBACK (action_mail_mark_notjunk_cb) },
 +
 +	{ "mail-mark-read",
 +	  "mail-mark-read",
 +	  N_("_Read"),
 +	  "<Control>k",
 +	  N_("Mark the selected messages as having been read"),
 +	  G_CALLBACK (action_mail_mark_read_cb) },
 +
 +	{ "mail-mark-unimportant",
 +	  NULL,
 +	  N_("Uni_mportant"),
 +	  NULL,
 +	  N_("Mark the selected messages as unimportant"),
 +	  G_CALLBACK (action_mail_mark_unimportant_cb) },
 +
 +	{ "mail-mark-unread",
 +	  "mail-mark-unread",
 +	  N_("_Unread"),
 +	  "<Shift><Control>k",
 +	  N_("Mark the selected messages as not having been read"),
 +	  G_CALLBACK (action_mail_mark_unread_cb) },
 +
 +	{ "mail-message-edit",
 +	  NULL,
 +	  N_("_Edit as New Message..."),
 +	  NULL,
 +	  N_("Open the selected messages in the composer for editing"),
 +	  G_CALLBACK (action_mail_message_edit_cb) },
 +
 +	{ "mail-message-new",
 +	  "mail-message-new",
 +	  N_("Compose _New Message"),
 +	  "<Shift><Control>m",
 +	  N_("Open a window for composing a mail message"),
 +	  G_CALLBACK (action_mail_message_new_cb) },
 +
 +	{ "mail-message-open",
 +	  NULL,
 +	  N_("_Open in New Window"),
 +	  "<Control>o",
 +	  N_("Open the selected messages in a new window"),
 +	  G_CALLBACK (action_mail_message_open_cb) },
 +
 +	{ "mail-move",
 +	  "mail-move",
 +	  N_("_Move to Folder..."),
 +	  "<Shift><Control>v",
 +	  N_("Move selected messages to another folder"),
 +	  G_CALLBACK (action_mail_move_cb) },
 +
 +	{ "mail-next",
 +	  GTK_STOCK_GO_FORWARD,
 +	  N_("_Next Message"),
 +	  "<Control>Page_Down",
 +	  N_("Display the next message"),
 +	  G_CALLBACK (action_mail_next_cb) },
 +
 +	{ "mail-next-important",
 +	  NULL,
 +	  N_("Next _Important Message"),
 +	  NULL,
 +	  N_("Display the next important message"),
 +	  G_CALLBACK (action_mail_next_important_cb) },
 +
 +	{ "mail-next-thread",
 +	  NULL,
 +	  N_("Next _Thread"),
 +	  NULL,
 +	  N_("Display the next thread"),
 +	  G_CALLBACK (action_mail_next_thread_cb) },
 +
 +	{ "mail-next-unread",
 +	  NULL,
 +	  N_("Next _Unread Message"),
 +	  "<Control>bracketright",
 +	  N_("Display the next unread message"),
 +	  G_CALLBACK (action_mail_next_unread_cb) },
 +
 +	{ "mail-previous",
 +	  GTK_STOCK_GO_BACK,
 +	  N_("_Previous Message"),
 +	  "<Control>Page_Up",
 +	  N_("Display the previous message"),
 +	  G_CALLBACK (action_mail_previous_cb) },
 +
 +	{ "mail-previous-important",
 +	  NULL,
 +	  N_("Pr_evious Important Message"),
 +	  NULL,
 +	  N_("Display the previous important message"),
 +	  G_CALLBACK (action_mail_previous_important_cb) },
 +
 +	{ "mail-previous-unread",
 +	  NULL,
 +	  N_("P_revious Unread Message"),
 +	  "<Control>bracketleft",
 +	  N_("Display the previous unread message"),
 +	  G_CALLBACK (action_mail_previous_unread_cb) },
 +
 +	{ "mail-print",
 +	  GTK_STOCK_PRINT,
 +	  NULL,
 +	  "<Control>p",
 +	  N_("Print this message"),
 +	  G_CALLBACK (action_mail_print_cb) },
 +
 +	{ "mail-print-preview",
 +	  GTK_STOCK_PRINT_PREVIEW,
 +	  NULL,
 +	  NULL,
 +	  N_("Preview the message to be printed"),
 +	  G_CALLBACK (action_mail_print_preview_cb) },
 +
 +	{ "mail-redirect",
 +	  NULL,
 +	  N_("Re_direct"),
 +	  NULL,
 +	  N_("Redirect (bounce) the selected message to someone"),
 +	  G_CALLBACK (action_mail_redirect_cb) },
 +
 +	{ "mail-reply-all",
 +	  "mail-reply-all",
 +	  N_("Reply to _All"),
 +	  "<Shift><Control>r",
 +	  N_("Compose a reply to all the recipients of the selected message"),
 +	  G_CALLBACK (action_mail_reply_all_cb) },
 +
 +	{ "mail-reply-list",
 +	  NULL,
 +	  N_("Reply to _List"),
 +	  "<Control>l",
 +	  N_("Compose a reply to the mailing list of the selected message"),
 +	  G_CALLBACK (action_mail_reply_list_cb) },
 +
 +	{ "mail-reply-sender",
 +	  "mail-reply-sender",
 +	  N_("_Reply to Sender"),
 +	  "<Control>r",
 +	  N_("Compose a reply to the sender of the selected message"),
 +	  G_CALLBACK (action_mail_reply_sender_cb) },
 +
 +	{ "mail-save-as",
 +	  GTK_STOCK_SAVE_AS,
 +	  N_("_Save as mbox..."),
 +	  NULL,
 +	  N_("Save selected messages as an mbox file"),
 +	  G_CALLBACK (action_mail_save_as_cb) },
 +
 +	{ "mail-search-folder-from-mailing-list",
 +	  NULL,
 +	  N_("Search Folder from Mailing _List..."),
 +	  NULL,
 +	  N_("Create a search folder for this mailing list"),
 +	  G_CALLBACK (action_mail_search_folder_from_mailing_list_cb) },
 +
 +	{ "mail-search-folder-from-recipients",
 +	  NULL,
 +	  N_("Search Folder from Recipien_ts..."),
 +	  NULL,
 +	  N_("Create a search folder for these recipients"),
 +	  G_CALLBACK (action_mail_search_folder_from_recipients_cb) },
 +
 +	{ "mail-search-folder-from-sender",
 +	  NULL,
 +	  N_("Search Folder from Sen_der..."),
 +	  NULL,
 +	  N_("Create a search folder for this sender"),
 +	  G_CALLBACK (action_mail_search_folder_from_sender_cb) },
 +
 +	{ "mail-search-folder-from-subject",
 +	  NULL,
 +	  N_("Search Folder from S_ubject..."),
 +	  NULL,
 +	  N_("Create a search folder for this subject"),
 +	  G_CALLBACK (action_mail_search_folder_from_subject_cb) },
 +
 +	{ "mail-select-all",
 +	  NULL,
 +	  N_("Select _All Text"),
 +	  "<Shift><Control>x",
 +	  N_("Select all the text in a message"),
 +	  G_CALLBACK (action_mail_select_all_cb) },
 +
 +	{ "mail-show-source",
 +	  NULL,
 +	  N_("_Message Source"),
 +	  "<Control>u",
 +	  N_("Show the raw email source of the message"),
 +	  G_CALLBACK (action_mail_show_source_cb) },
 +
 +	{ "mail-toggle-important",
 +	  NULL,
 +	  NULL,  /* No menu item; key press only */
 +	  NULL,
 +	  NULL,
 +	  G_CALLBACK (action_mail_toggle_important_cb) },
 +
 +	{ "mail-undelete",
 +	  NULL,
 +	  N_("_Undelete Message"),
 +	  "<Shift><Control>d",
 +	  N_("Undelete the selected messages"),
 +	  G_CALLBACK (action_mail_undelete_cb) },
 +
 +	{ "mail-zoom-100",
 +	  GTK_STOCK_ZOOM_100,
 +	  N_("_Normal Size"),
 +	  "<Control>0",
 +	  N_("Reset the text to its original size"),
 +	  G_CALLBACK (action_mail_zoom_100_cb) },
 +
 +	{ "mail-zoom-in",
 +	  GTK_STOCK_ZOOM_IN,
 +	  N_("_Zoom In"),
 +	  "<Control>plus",
 +	  N_("Increase the text size"),
 +	  G_CALLBACK (action_mail_zoom_in_cb) },
 +
 +	{ "mail-zoom-out",
 +	  GTK_STOCK_ZOOM_OUT,
 +	  N_("Zoom _Out"),
 +	  "<Control>minus",
 +	  N_("Decrease the text size"),
 +	  G_CALLBACK (action_mail_zoom_out_cb) },
 +
 +	/*** Menus ***/
 +
 +	{ "mail-create-rule-menu",
 +	  NULL,
 +	  N_("Create R_ule"),
 +	  NULL,
 +	  NULL,
 +	  NULL },
 +
 +	{ "mail-encoding-menu",
 +	  NULL,
 +	  N_("Ch_aracter Encoding"),
 +	  NULL,
 +	  NULL,
 +	  NULL },
 +
 +	{ "mail-forward-as-menu",
 +	  NULL,
 +	  N_("F_orward As..."),
 +	  NULL,
 +	  NULL,
 +	  NULL },
 +
 +	{ "mail-goto-menu",
 +	  GTK_STOCK_JUMP_TO,
 +	  N_("_Go To"),
 +	  NULL,
 +	  NULL,
 +	  NULL },
 +
 +	{ "mail-mark-as-menu",
 +	  NULL,
 +	  N_("Mar_k As"),
 +	  NULL,
 +	  NULL,
 +	  NULL },
 +
 +	{ "mail-message-menu",
 +	  NULL,
 +	  N_("_Message"),
 +	  NULL,
 +	  NULL,
 +	  NULL },
 +
 +	{ "mail-zoom-menu",
 +	  NULL,
 +	  N_("_Zoom"),
 +	  NULL,
 +	  NULL,
 +	  NULL }
 +};
 +
 +static EPopupActionEntry mail_reader_popup_entries[] = {
 +
 +	{ "mail-popup-copy",
 +	  NULL,
 +	  "mail-copy" },
 +
 +	{ "mail-popup-delete",
 +	  NULL,
 +	  "mail-delete" },
 +
 +	{ "mail-popup-flag-for-followup",
 +	  N_("Mark for Follo_w Up..."),
 +	  "mail-flag-for-followup" },
 +
 +	{ "mail-popup-forward",
 +	  NULL,
 +	  "mail-forward" },
 +
 +	{ "mail-popup-mark-important",
 +	  N_("Mark as _Important"),
 +	  "mail-mark-important" },
 +
 +	{ "mail-popup-mark-junk",
 +	  N_("Mark as _Junk"),
 +	  "mail-mark-junk" },
 +
 +	{ "mail-popup-mark-notjunk",
 +	  N_("Mark as _Not Junk"),
 +	  "mail-mark-notjunk" },
 +
 +	{ "mail-popup-mark-read",
 +	  N_("Mark as _Read"),
 +	  "mail-mark-read" },
 +
 +	{ "mail-popup-mark-unimportant",
 +	  N_("Mark as Uni_mportant"),
 +	  "mail-mark-unimportant" },
 +
 +	{ "mail-popup-mark-unread",
 +	  N_("Mark as _Unread"),
 +	  "mail-mark-unread" },
 +
 +	{ "mail-popup-message-edit",
 +	  NULL,
 +	  "mail-message-edit" },
 +
 +	{ "mail-popup-move",
 +	  NULL,
 +	  "mail-move" },
 +
 +	{ "mail-popup-print",
 +	  NULL,
 +	  "mail-print" },
 +
 +	{ "mail-popup-reply-all",
 +	  NULL,
 +	  "mail-reply-all" },
 +
 +	{ "mail-popup-reply-sender",
 +	  NULL,
 +	  "mail-reply-sender" },
 +
 +	{ "mail-popup-save-as",
 +	  NULL,
 +	  "mail-save-as" },
 +
 +	{ "mail-popup-undelete",
 +	  NULL,
 +	  "mail-undelete" }
 +};
 +
 +static GtkToggleActionEntry mail_reader_toggle_entries[] = {
 +
 +	{ "mail-caret-mode",
 +	  NULL,
 +	  N_("_Caret Mode"),
 +	  "F7",
 +	  N_("Show a blinking cursor in the body of displayed messages"),
 +	  NULL,  /* No callback required */
 +	  FALSE },
 +
 +	{ "mail-show-all-headers",
 +	  NULL,
 +	  N_("All Message _Headers"),
 +	  NULL,
 +	  N_("Show messages with all email headers"),
 +	  G_CALLBACK (action_mail_show_all_headers_cb),
 +	  FALSE }
 +};
 +
 +static gboolean
 +mail_reader_button_release_event_cb (EMailReader *reader,
 +                                     GdkEventButton *button,
 +                                     GtkHTML *html)
 +{
 +	GtkAction *action;
 +	const gchar *action_name;
 +	gboolean selection_active;
 +
 +	action_name = "mail-clipboard-copy";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	selection_active = gtk_html_command (html, "is-selection-active");
 +	gtk_action_set_sensitive (action, selection_active);
 +
 +	return FALSE;
 +}
 +
 +static void
 +mail_reader_double_click_cb (EMailReader *reader,
 +                             gint row,
 +                             ETreePath path,
 +                             gint col,
 +                             GdkEvent *event)
 +{
 +	/* Ignore double clicks on columns that handle their own state. */
 +	if (MESSAGE_LIST_COLUMN_IS_ACTIVE (col))
 +		return;
 +
 +	e_mail_reader_activate (reader, "mail-message-open");
 +}
 +
 +static gboolean
 +mail_reader_key_press_event_cb (EMailReader *reader,
 +                                GdkEventKey *event)
 +{
 +	const gchar *action_name;
 +
 +	if ((event->state & GDK_CONTROL_MASK) != 0)
 +		goto ctrl;
 +
 +	/* <keyval> alone */
 +	switch (event->keyval) {
 +		case GDK_Delete:
 +		case GDK_KP_Delete:
 +			action_name = "mail-delete";
 +			break;
 +
 +		case GDK_Return:
 +		case GDK_KP_Enter:
 +		case GDK_ISO_Enter:
 +			action_name = "mail-message-open";
 +			break;
 +
 +		case GDK_period:
++		case GDK_bracketleft:
 +			action_name = "mail-next-unread";
 +			break;
 +
 +		case GDK_comma:
++		case GDK_bracketright:
 +			action_name = "mail-previous-unread";
 +			break;
 +
 +#ifdef HAVE_XFREE
 +		case XF86XK_Reply:
 +			action_name = "mail-reply-all";
 +			break;
 +
 +		case XF86XK_MailForward:
 +			action_name = "mail-forward";
 +			break;
 +#endif
 +
- 		case '!':
++		case GDK_exclam:
 +			action_name = "mail-toggle-important";
 +			break;
 +
 +		default:
 +			return FALSE;
 +	}
 +
 +	goto exit;
 +
 +ctrl:
 +
 +	/* Ctrl + <keyval> */
 +	switch (event->keyval) {
 +		case GDK_period:
 +			action_name = "mail-next-unread";
 +			break;
 +
 +		case GDK_comma:
 +			action_name = "mail-previous-unread";
 +			break;
 +
 +		default:
 +			return FALSE;
 +	}
 +
 +exit:
 +	e_mail_reader_activate (reader, action_name);
 +
 +	return TRUE;
 +}
 +
 +static gint
 +mail_reader_key_press_cb (EMailReader *reader,
 +                          gint row,
 +                          ETreePath path,
 +                          gint col,
 +                          GdkEvent *event)
 +{
 +	return mail_reader_key_press_event_cb (reader, &event->key);
 +}
 +
 +static gboolean
 +mail_reader_message_read_cb (EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	const gchar *uid;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	uid = g_object_get_data (G_OBJECT (reader), "mark-read-uid");
 +	g_return_val_if_fail (uid != NULL, FALSE);
 +
 +	if (g_strcmp0 (message_list->cursor_uid, uid) == 0)
 +		e_mail_reader_mark_as_read (reader, uid);
 +
 +	return FALSE;
 +}
 +
 +static void
 +mail_reader_message_loaded_cb (CamelFolder *folder,
 +                               const gchar *message_uid,
 +                               CamelMimeMessage *message,
 +                               gpointer user_data,
 +                               CamelException *ex)
 +{
 +	EMailReader *reader = user_data;
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	EShellBackend *shell_backend;
 +	EShellSettings *shell_settings;
 +	EShell *shell;
 +	EMEvent *event;
 +	EMEventTargetMessage *target;
 +	gboolean mark_read;
 +	gint timeout_interval;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	shell_backend = e_mail_reader_get_shell_backend (reader);
 +	shell = e_shell_backend_get_shell (shell_backend);
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	/* If the user picked a different message in the time it took
 +	 * to fetch this message, then don't bother rendering it. */
 +	if (g_strcmp0 (message_list->cursor_uid, message_uid) != 0)
 +		return;
 +
 +	/** @Event: message.reading
 +	 * @Title: Viewing a message
 +	 * @Target: EMEventTargetMessage
 +	 *
 +	 * message.reading is emitted whenever a user views a message.
 +	 */
 +	event = em_event_peek ();
 +	target = em_event_target_new_message (
 +		event, folder, message, message_uid, 0);
 +	e_event_emit (
 +		(EEvent *) event, "message.reading",
 +		(EEventTarget *) target);
 +
 +	em_format_format (
 +		EM_FORMAT (html_display), folder, message_uid, message);
 +
 +	/* Reset the shell view icon. */
 +	e_shell_event (shell, "mail-icon", (gpointer) "evolution-mail");
 +
 +	/* Determine whether to mark the message as read. */
 +	mark_read = e_shell_settings_get_boolean (
 +		shell_settings, "mail-mark-seen");
 +	timeout_interval = e_shell_settings_get_int (
 +		shell_settings, "mail-mark-seen-timeout");
 +
 +	g_object_set_data_full (
 +		G_OBJECT (reader), "mark-read-uid",
 +		g_strdup (message_uid), (GDestroyNotify) g_free);
 +
 +	if (message_list->seen_id > 0)
 +		g_source_remove (message_list->seen_id);
 +
 +	if (mark_read) {
 +		message_list->seen_id = g_timeout_add (
 +			timeout_interval, (GSourceFunc)
 +			mail_reader_message_read_cb, reader);
 +
 +	} else if (camel_exception_is_set (ex)) {
 +		GtkHTMLStream *stream;
 +
 +		/* Display the error inline and clear the exception. */
 +		stream = gtk_html_begin (
 +			EM_FORMAT_HTML (html_display)->html);
 +		gtk_html_stream_printf (
 +			stream, "<h2>%s</h2><p>%s</p>",
 +			_("Unable to retrieve message"),
 +			ex->desc);
 +		gtk_html_stream_close (stream, GTK_HTML_STREAM_OK);
 +		camel_exception_clear (ex);
 +	}
 +
 +	e_mail_reader_update_actions (reader);
 +
 +	/* We referenced this in the call to mail_get_messagex(). */
 +	g_object_unref (reader);
 +}
 +
 +static gboolean
 +mail_reader_message_selected_timeout_cb (EMailReader *reader)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	const gchar *cursor_uid;
 +	const gchar *format_uid;
 +	const gchar *key;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	cursor_uid = message_list->cursor_uid;
 +	format_uid = EM_FORMAT (html_display)->uid;
 +
 +	if (message_list->last_sel_single) {
 +		if (g_strcmp0 (cursor_uid, format_uid) != 0)
 +			mail_get_messagex (
 +				message_list->folder, cursor_uid,
 +				mail_reader_message_loaded_cb,
 +				g_object_ref (reader),
 +				mail_msg_fast_ordered_push);
 +	} else
 +		em_format_format (EM_FORMAT (html_display), NULL, NULL, NULL);
 +
 +	key = "message-selected-timeout";
 +	g_object_set_data (G_OBJECT (reader), key, NULL);
 +
 +	return FALSE;
 +}
 +
 +static void
 +mail_reader_message_selected_cb (EMailReader *reader,
 +                                 const gchar *uid)
 +{
 +	const gchar *key;
 +	guint source_id;
 +	gpointer data;
 +
 +	/* XXX This is kludgy, but we have no other place to store
 +	 *     timeout state information. */
 +
 +	key = "message-selected-timeout";
 +	data = g_object_get_data (G_OBJECT (reader), key);
 +	source_id = GPOINTER_TO_UINT (data);
 +
 +	if (source_id > 0)
 +		g_source_remove (source_id);
 +
 +	source_id = g_timeout_add (
 +		100, (GSourceFunc)
 +		mail_reader_message_selected_timeout_cb, reader);
 +
 +	data = GUINT_TO_POINTER (source_id);
 +	g_object_set_data (G_OBJECT (reader), key, data);
 +
 +	e_mail_reader_changed (reader);
 +}
 +
 +static void
 +mail_reader_emit_folder_loaded (EMailReader *reader)
 +{
 +	g_signal_emit (reader, signals[FOLDER_LOADED], 0);
 +}
 +
 +static void
 +mail_reader_set_folder (EMailReader *reader,
 +                        CamelFolder *folder,
 +                        const gchar *folder_uri)
 +{
 +	EMFormatHTMLDisplay *html_display;
 +	MessageList *message_list;
 +	gboolean outgoing;
 +
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	outgoing = folder != NULL && folder_uri != NULL && (
 +		em_utils_folder_is_drafts (folder, folder_uri) ||
 +		em_utils_folder_is_outbox (folder, folder_uri) ||
 +		em_utils_folder_is_sent (folder, folder_uri));
 +
 +	if (message_list->folder != NULL)
 +		mail_sync_folder (message_list->folder, NULL, NULL);
 +
 +	em_format_format (EM_FORMAT (html_display), NULL, NULL, NULL);
 +	message_list_set_folder (message_list, folder, folder_uri, outgoing);
 +
 +	mail_reader_emit_folder_loaded (reader);
 +}
 +
 +static void
 +mail_reader_set_message (EMailReader *reader,
 +                         const gchar *uid,
 +                         gboolean mark_read)
 +{
 +	MessageList *message_list;
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	message_list_select_uid (message_list, uid);
 +}
 +
 +static void
 +mail_reader_init_charset_actions (EMailReader *reader)
 +{
 +	GtkActionGroup *action_group;
 +	GtkRadioAction *default_action;
 +	GSList *radio_group;
 +
 +	action_group = e_mail_reader_get_action_group (reader);
 +
 +	radio_group = e_charset_add_radio_actions (
 +		action_group, "mail-charset-", NULL,
 +		G_CALLBACK (action_mail_charset_cb), reader);
 +
 +	/* XXX Add a tooltip! */
 +	default_action = gtk_radio_action_new (
 +		"mail-charset-default", _("Default"), NULL, NULL, -1);
 +
 +	gtk_radio_action_set_group (default_action, radio_group);
 +
 +	g_signal_connect (
 +		default_action, "changed",
 +		G_CALLBACK (action_mail_charset_cb), reader);
 +
 +	gtk_action_group_add_action (
 +		action_group, GTK_ACTION (default_action));
 +
 +	gtk_radio_action_set_current_value (default_action, -1);
 +}
 +
 +static void
 +mail_reader_class_init (EMailReaderIface *iface)
 +{
 +	iface->set_folder = mail_reader_set_folder;
 +	iface->set_message = mail_reader_set_message;
 +
 +	signals[CHANGED] = g_signal_new (
 +		"changed",
 +		G_OBJECT_CLASS_TYPE (iface),
 +		G_SIGNAL_RUN_FIRST,
 +		0, NULL, NULL,
 +		g_cclosure_marshal_VOID__VOID,
 +		G_TYPE_NONE, 0);
 +
 +	signals[FOLDER_LOADED] = g_signal_new (
 +		"folder-loaded",
 +		G_OBJECT_CLASS_TYPE (iface),
 +		G_SIGNAL_RUN_FIRST,
 +		0, NULL, NULL,
 +		g_cclosure_marshal_VOID__VOID,
 +		G_TYPE_NONE, 0);
 +
 +	signals[SHOW_SEARCH_BAR] = g_signal_new (
 +		"show-search-bar",
 +		G_OBJECT_CLASS_TYPE (iface),
 +		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
 +		G_STRUCT_OFFSET (EMailReaderIface, show_search_bar),
 +		NULL, NULL,
 +		g_cclosure_marshal_VOID__VOID,
 +		G_TYPE_NONE, 0);
 +}
 +
 +GType
 +e_mail_reader_get_type (void)
 +{
 +	static GType type = 0;
 +
 +	if (G_UNLIKELY (type == 0)) {
 +		static const GTypeInfo type_info = {
 +			sizeof (EMailReaderIface),
 +			(GBaseInitFunc) NULL,
 +			(GBaseFinalizeFunc) NULL,
 +			(GClassInitFunc) mail_reader_class_init,
 +			(GClassFinalizeFunc) NULL,
 +			NULL,  /* class_data */
 +			0,     /* instance_size */
 +			0,     /* n_preallocs */
 +			(GInstanceInitFunc) NULL,
 +			NULL   /* value_table */
 +		};
 +
 +		type = g_type_register_static (
 +			G_TYPE_INTERFACE, "EMailReader", &type_info, 0);
 +
 +		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
 +	}
 +
 +	return type;
 +}
 +
 +void
 +e_mail_reader_init (EMailReader *reader)
 +{
 +	EShell *shell;
 +	EShellBackend *shell_backend;
 +	EShellSettings *shell_settings;
 +	EMFormatHTMLDisplay *html_display;
 +	EMailDisplay *display;
 +	GtkActionGroup *action_group;
 +	MessageList *message_list;
 +	GConfBridge *bridge;
 +	GtkAction *action;
 +	const gchar *action_name;
 +	const gchar *key;
 +
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +
 +	action_group = e_mail_reader_get_action_group (reader);
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	shell_backend = e_mail_reader_get_shell_backend (reader);
 +
 +	shell = e_shell_backend_get_shell (shell_backend);
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	display = E_MAIL_DISPLAY (EM_FORMAT_HTML (html_display)->html);
 +
 +	gtk_action_group_add_actions (
 +		action_group, mail_reader_entries,
 +		G_N_ELEMENTS (mail_reader_entries), reader);
 +	e_action_group_add_popup_actions (
 +		action_group, mail_reader_popup_entries,
 +		G_N_ELEMENTS (mail_reader_popup_entries));
 +	gtk_action_group_add_toggle_actions (
 +		action_group, mail_reader_toggle_entries,
 +		G_N_ELEMENTS (mail_reader_toggle_entries), reader);
 +
 +	mail_reader_init_charset_actions (reader);
 +
 +	/* Bind GObject properties to GConf keys. */
 +
 +	bridge = gconf_bridge_get ();
 +
 +	action_name = "mail-caret-mode";
 +	key = "/apps/evolution/mail/display/caret_mode";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gconf_bridge_bind_property (bridge, key, G_OBJECT (action), "active");
 +
 +	action_name = "mail-show-all-headers";
 +	key = "/apps/evolution/mail/display/show_all_headers";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gconf_bridge_bind_property (bridge, key, G_OBJECT (action), "active");
 +
 +	/* Fine tuning. */
 +
 +	action_name = "mail-clipboard-copy";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, FALSE);
 +
 +	action_name = "mail-delete";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	g_object_set (action, "short-label", _("Delete"), NULL);
 +
 +	action_name = "mail-next";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	g_object_set (action, "short-label", _("Next"), NULL);
 +
 +	action_name = "mail-previous";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	g_object_set (action, "short-label", _("Previous"), NULL);
 +
 +	action_name = "mail-reply-sender";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	g_object_set (action, "short-label", _("Reply"), NULL);
 +
 +	action_name = "search-folder-recipient";
 +	action = e_mail_display_get_action (display, action_name);
 +	g_signal_connect (
 +		action, "activate",
 +		G_CALLBACK (action_search_folder_recipient_cb), reader);
 +
 +	action_name = "search-folder-sender";
 +	action = e_mail_display_get_action (display, action_name);
 +	g_signal_connect (
 +		action, "activate",
 +		G_CALLBACK (action_search_folder_sender_cb), reader);
 +
 +	/* Bind properties. */
 +
 +	e_binding_new_full (
 +		G_OBJECT (shell_settings), "mail-citation-color",
 +		G_OBJECT (html_display), "citation-color",
 +		e_binding_transform_string_to_color,
 +		NULL, NULL);
 +
 +	e_binding_new (
 +		G_OBJECT (shell_settings), "mail-image-loading-policy",
 +		G_OBJECT (html_display), "image-loading-policy");
 +
 +	e_binding_new (
 +		G_OBJECT (shell_settings), "mail-only-local-photos",
 +		G_OBJECT (html_display), "only-local-photos");
 +
 +	e_binding_new (
 +		G_OBJECT (shell_settings), "mail-show-animated-images",
 +		G_OBJECT (display), "animate");
 +
 +	e_binding_new (
 +		G_OBJECT (shell_settings), "mail-show-sender-photo",
 +		G_OBJECT (html_display), "show-sender-photo");
 +
 +	action_name = "mail-caret-mode";
 +	action = e_mail_reader_get_action (reader, action_name);
 +
 +	e_mutual_binding_new (
 +		G_OBJECT (action), "active",
 +		G_OBJECT (display), "caret-mode");
 +
 +	/* Connect signals. */
 +
 +	g_signal_connect_swapped (
 +		display, "button-release-event",
 +		G_CALLBACK (mail_reader_button_release_event_cb), reader);
 +
 +	g_signal_connect_swapped (
 +		display, "key-press-event",
 +		G_CALLBACK (mail_reader_key_press_event_cb), reader);
 +
 +	g_signal_connect_swapped (
 +		message_list, "message-selected",
 +		G_CALLBACK (mail_reader_message_selected_cb), reader);
 +
 +	g_signal_connect_swapped (
 +		message_list, "message-list-built",
 +		G_CALLBACK (mail_reader_emit_folder_loaded), reader);
 +
 +	g_signal_connect_swapped (
 +		message_list->tree, "double-click",
 +		G_CALLBACK (mail_reader_double_click_cb), reader);
 +
 +	g_signal_connect_swapped (
 +		message_list->tree, "key-press",
 +		G_CALLBACK (mail_reader_key_press_cb), reader);
 +
 +	g_signal_connect_swapped (
 +		message_list->tree, "selection-change",
 +		G_CALLBACK (e_mail_reader_changed), reader);
 +}
 +
 +void
 +e_mail_reader_changed (EMailReader *reader)
 +{
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +
 +	g_signal_emit (reader, signals[CHANGED], 0);
 +}
 +
 +guint32
 +e_mail_reader_check_state (EMailReader *reader)
 +{
 +	MessageList *message_list;
 +	GPtrArray *uids;
 +	CamelFolder *folder;
 +	CamelStore *store = NULL;
 +	const gchar *folder_uri;
 +	const gchar *tag;
 +	gboolean can_clear_flags = FALSE;
 +	gboolean can_flag_completed = FALSE;
 +	gboolean can_flag_for_followup = FALSE;
 +	gboolean has_deleted = FALSE;
 +	gboolean has_important = FALSE;
 +	gboolean has_junk = FALSE;
 +	gboolean has_not_junk = FALSE;
 +	gboolean has_read = FALSE;
 +	gboolean has_undeleted = FALSE;
 +	gboolean has_unimportant = FALSE;
 +	gboolean has_unread = FALSE;
 +	gboolean drafts_or_outbox;
 +	gboolean store_supports_vjunk = FALSE;
 +	guint32 state = 0;
 +	guint ii;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), 0);
 +
 +	message_list = e_mail_reader_get_message_list (reader);
 +	uids = message_list_get_selected (message_list);
 +	folder_uri = message_list->folder_uri;
 +	folder = message_list->folder;
 +
 +	if (folder != NULL) {
 +		store = CAMEL_STORE (folder->parent_store);
 +		store_supports_vjunk = (store->flags & CAMEL_STORE_VJUNK);
 +	}
 +
 +	drafts_or_outbox =
 +		em_utils_folder_is_drafts (folder, folder_uri) ||
 +		em_utils_folder_is_outbox (folder, folder_uri);
 +
 +	for (ii = 0; ii < uids->len; ii++) {
 +		CamelMessageInfo *info;
 +		guint32 flags;
 +
 +		info = camel_folder_get_message_info (
 +			folder, uids->pdata[ii]);
 +		if (info == NULL)
 +			continue;
 +
 +		flags = camel_message_info_flags (info);
 +
 +		if (flags & CAMEL_MESSAGE_SEEN)
 +			has_read = TRUE;
 +		else
 +			has_unread = TRUE;
 +
 +		if (drafts_or_outbox) {
 +			has_junk = FALSE;
 +			has_not_junk = FALSE;
 +		} else if (store_supports_vjunk) {
 +			guint32 bitmask;
 +
 +			/* XXX Strictly speaking, this logic is correct.
 +			 *     Problem is there's nothing in the message
 +			 *     list that indicates whether a message is
 +			 *     already marked "Not Junk".  So the user may
 +			 *     think the "Not Junk" button is enabling and
 +			 *     disabling itself randomly as he reads mail. */
 +
 +			if (flags & CAMEL_MESSAGE_JUNK)
 +				has_junk = TRUE;
 +			if (flags & CAMEL_MESSAGE_NOTJUNK)
 +				has_not_junk = TRUE;
 +
 +			bitmask = CAMEL_MESSAGE_JUNK | CAMEL_MESSAGE_NOTJUNK;
 +
 +			/* If neither junk flag is set, the
 +			 * message can be marked either way. */
 +			if ((flags & bitmask) == 0) {
 +				has_junk = TRUE;
 +				has_not_junk = TRUE;
 +			}
 +
 +		} else {
 +			has_junk = TRUE;
 +			has_not_junk = TRUE;
 +		}
 +
 +		if (flags & CAMEL_MESSAGE_DELETED)
 +			has_deleted = TRUE;
 +		else
 +			has_undeleted = TRUE;
 +
 +		if (flags & CAMEL_MESSAGE_FLAGGED)
 +			has_important = TRUE;
 +		else
 +			has_unimportant = TRUE;
 +
 +		tag = camel_message_info_user_tag (info, "follow-up");
 +		if (tag != NULL && *tag != '\0') {
 +			can_clear_flags = TRUE;
 +			tag = camel_message_info_user_tag (
 +				info, "completed-on");
 +			if (tag != NULL && *tag != '\0')
 +				can_flag_completed = TRUE;
 +		} else
 +			can_flag_for_followup = TRUE;
 +	}
 +
 +	if (em_utils_check_user_can_send_mail ())
 +		state |= E_MAIL_READER_HAVE_ACCOUNT;
 +	if (uids->len == 1)
 +		state |= E_MAIL_READER_SELECTION_SINGLE;
 +	if (uids->len > 1)
 +		state |= E_MAIL_READER_SELECTION_MULTIPLE;
 +	if (!drafts_or_outbox && uids->len == 1)
 +		state |= E_MAIL_READER_SELECTION_CAN_ADD_SENDER;
 +#if 0  /* FIXME */
 +	if (can_edit)
 +		state |= E_MAIL_READER_SELECTION_CAN_EDIT;
 +#endif
 +	if (can_clear_flags)
 +		state |= E_MAIL_READER_SELECTION_FLAG_CLEAR;
 +	if (can_flag_completed)
 +		state |= E_MAIL_READER_SELECTION_FLAG_COMPLETED;
 +	if (can_flag_for_followup)
 +		state |= E_MAIL_READER_SELECTION_FLAG_FOLLOWUP;
 +	if (has_deleted)
 +		state |= E_MAIL_READER_SELECTION_HAS_DELETED;
 +	if (has_important)
 +		state |= E_MAIL_READER_SELECTION_HAS_IMPORTANT;
 +	if (has_junk)
 +		state |= E_MAIL_READER_SELECTION_HAS_JUNK;
 +	if (has_not_junk)
 +		state |= E_MAIL_READER_SELECTION_HAS_NOT_JUNK;
 +	if (has_read)
 +		state |= E_MAIL_READER_SELECTION_HAS_READ;
 +	if (has_undeleted)
 +		state |= E_MAIL_READER_SELECTION_HAS_UNDELETED;
 +	if (has_unimportant)
 +		state |= E_MAIL_READER_SELECTION_HAS_UNIMPORTANT;
 +	if (has_unread)
 +		state |= E_MAIL_READER_SELECTION_HAS_UNREAD;
 +#if 0  /* FIXME */
 +	if (has_http_uri)
 +		state |= E_MAIL_READER_SELECTION_HAS_URI_HTTP;
 +	if (has_mailto_uri)
 +		state |= E_MAIL_READER_SELECTION_HAS_URI_MAILTO;
 +	if (is_mailing_list)
 +		state |= E_MAIL_READER_SELECTION_IS_MAILING_LIST;
 +#endif
 +
 +	em_utils_uids_free (uids);
 +
 +	return state;
 +
 +}
 +
 +void
 +e_mail_reader_update_actions (EMailReader *reader)
 +{
 +	EShell *shell;
 +	EShellBackend *shell_backend;
 +	EShellSettings *shell_settings;
 +	GtkAction *action;
 +	GtkActionGroup *action_group;
 +	const gchar *action_name;
 +	gboolean sensitive;
 +	guint32 state;
 +
 +	/* Be descriptive. */
 +	gboolean any_messages_selected;
 +	gboolean disable_printing;
 +	gboolean enable_flag_clear;
 +	gboolean enable_flag_completed;
 +	gboolean enable_flag_for_followup;
 +	gboolean have_an_account;
 +	gboolean multiple_messages_selected;
 +	gboolean selection_has_deleted_messages;
 +	gboolean selection_has_important_messages;
 +	gboolean selection_has_junk_messages;
 +	gboolean selection_has_not_junk_messages;
 +	gboolean selection_has_read_messages;
 +	gboolean selection_has_undeleted_messages;
 +	gboolean selection_has_unimportant_messages;
 +	gboolean selection_has_unread_messages;
 +	gboolean selection_is_mailing_list;
 +	gboolean single_message_selected;
 +
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +
 +	action_group = e_mail_reader_get_action_group (reader);
 +	state = e_mail_reader_check_state (reader);
 +
 +	shell_backend = e_mail_reader_get_shell_backend (reader);
 +	shell = e_shell_backend_get_shell (shell_backend);
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	disable_printing = e_shell_settings_get_boolean (
 +		shell_settings, "disable-printing");
 +
 +	have_an_account =
 +		(state & E_MAIL_READER_HAVE_ACCOUNT);
 +	single_message_selected =
 +		(state & E_MAIL_READER_SELECTION_SINGLE);
 +	multiple_messages_selected =
 +		(state & E_MAIL_READER_SELECTION_MULTIPLE);
 +	/* FIXME Missing booleans */
 +	enable_flag_clear =
 +		(state & E_MAIL_READER_SELECTION_FLAG_CLEAR);
 +	enable_flag_completed =
 +		(state & E_MAIL_READER_SELECTION_FLAG_COMPLETED);
 +	enable_flag_for_followup =
 +		(state & E_MAIL_READER_SELECTION_FLAG_FOLLOWUP);
 +	selection_has_deleted_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_DELETED);
 +	selection_has_important_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_IMPORTANT);
 +	selection_has_junk_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_JUNK);
 +	selection_has_not_junk_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_NOT_JUNK);
 +	selection_has_read_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_READ);
 +	selection_has_undeleted_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_UNDELETED);
 +	selection_has_unimportant_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_UNIMPORTANT);
 +	selection_has_unread_messages =
 +		(state & E_MAIL_READER_SELECTION_HAS_UNREAD);
 +	/* FIXME Missing booleans */
 +	selection_is_mailing_list =
 +		(state & E_MAIL_READER_SELECTION_IS_MAILING_LIST);
 +
 +	any_messages_selected =
 +		(single_message_selected || multiple_messages_selected);
 +
 +	action_name = "mail-check-for-junk";
 +	sensitive = any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-copy";
 +	sensitive = any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-delete";
 +	sensitive = selection_has_undeleted_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-filters-apply";
 +	sensitive = any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-forward";
 +	sensitive = have_an_account && any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-forward-attached";
 +	sensitive = have_an_account && any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-forward-inline";
 +	sensitive = have_an_account && single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-forward-quoted";
 +	sensitive = have_an_account && single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-load-images";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-mark-important";
 +	sensitive = selection_has_unimportant_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-mark-junk";
 +	sensitive = selection_has_not_junk_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-mark-notjunk";
 +	sensitive = selection_has_junk_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-mark-read";
 +	sensitive = selection_has_unread_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-mark-unimportant";
 +	sensitive = selection_has_important_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-mark-unread";
 +	sensitive = selection_has_read_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-message-edit";
 +	sensitive = have_an_account && single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-message-new";
 +	sensitive = have_an_account;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-message-open";
 +	sensitive = any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-move";
 +	sensitive = any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-next-important";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-next-thread";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-next-unread";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-previous-important";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-previous-unread";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-print";
 +	sensitive = single_message_selected && !disable_printing;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-print-preview";
 +	sensitive = single_message_selected && !disable_printing;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-redirect";
 +	sensitive = have_an_account && single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-reply-all";
 +	sensitive = have_an_account && single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-reply-list";
 +	sensitive = have_an_account && single_message_selected &&
 +		selection_is_mailing_list;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-reply-sender";
 +	sensitive = have_an_account && single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-save-as";
 +	sensitive = any_messages_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-select-all";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-show-source";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-undelete";
 +	sensitive = selection_has_deleted_messages;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-zoom-100";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-zoom-in";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +
 +	action_name = "mail-zoom-out";
 +	sensitive = single_message_selected;
 +	action = e_mail_reader_get_action (reader, action_name);
 +	gtk_action_set_sensitive (action, sensitive);
 +}
 +
 +GtkAction *
 +e_mail_reader_get_action (EMailReader *reader,
 +                          const gchar *action_name)
 +{
 +	GtkActionGroup *action_group;
 +	GtkAction *action;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 +	g_return_val_if_fail (action_name != NULL, NULL);
 +
 +	action_group = e_mail_reader_get_action_group (reader);
 +	action = gtk_action_group_get_action (action_group, action_name);
 +
 +	if (action == NULL)
 +		g_critical (
 +			"%s: action `%s' not found", G_STRFUNC, action_name);
 +
 +	return action;
 +}
 +
 +GtkActionGroup *
 +e_mail_reader_get_action_group (EMailReader *reader)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_val_if_fail (iface->get_action_group != NULL, NULL);
 +
 +	return iface->get_action_group (reader);
 +}
 +
 +gboolean
 +e_mail_reader_get_hide_deleted (EMailReader *reader)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), FALSE);
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_val_if_fail (iface->get_hide_deleted != NULL, FALSE);
 +
 +	return iface->get_hide_deleted (reader);
 +}
 +
 +EMFormatHTMLDisplay *
 +e_mail_reader_get_html_display (EMailReader *reader)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_val_if_fail (iface->get_html_display != NULL, NULL);
 +
 +	return iface->get_html_display (reader);
 +}
 +
 +MessageList *
 +e_mail_reader_get_message_list (EMailReader *reader)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_val_if_fail (iface->get_message_list != NULL, NULL);
 +
 +	return iface->get_message_list (reader);
 +}
 +
 +EShellBackend *
 +e_mail_reader_get_shell_backend (EMailReader *reader)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_val_if_fail (iface->get_shell_backend != NULL, NULL);
 +
 +	return iface->get_shell_backend (reader);
 +}
 +
 +GtkWindow *
 +e_mail_reader_get_window (EMailReader *reader)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_val_if_fail (iface->get_window != NULL, NULL);
 +
 +	return iface->get_window (reader);
 +}
 +
 +void
 +e_mail_reader_set_folder (EMailReader *reader,
 +                          CamelFolder *folder,
 +                          const gchar *folder_uri)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_if_fail (iface->set_folder != NULL);
 +
 +	iface->set_folder (reader, folder, folder_uri);
 +}
 +
 +/* Helper for e_mail_reader_set_folder_uri() */
 +static void
 +mail_reader_got_folder_cb (gchar *folder_uri,
 +                           CamelFolder *folder,
 +                           gpointer user_data)
 +{
 +	EMailReader *reader = user_data;
 +
 +	e_mail_reader_set_folder (reader, folder, folder_uri);
 +}
 +
 +void
 +e_mail_reader_set_folder_uri (EMailReader *reader,
 +                              const gchar *folder_uri)
 +{
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +	g_return_if_fail (folder_uri != NULL);
 +
 +	/* Fetch the CamelFolder asynchronously. */
 +	mail_get_folder (
 +		folder_uri, 0, mail_reader_got_folder_cb,
 +		reader, mail_msg_fast_ordered_push);
 +}
 +
 +void
 +e_mail_reader_set_message (EMailReader *reader,
 +                           const gchar *uid,
 +                           gboolean mark_read)
 +{
 +	EMailReaderIface *iface;
 +
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +
 +	iface = E_MAIL_READER_GET_IFACE (reader);
 +	g_return_if_fail (iface->set_message != NULL);
 +
 +	iface->set_message (reader, uid, mark_read);
 +}
 +
 +void
 +e_mail_reader_create_charset_menu (EMailReader *reader,
 +                                   GtkUIManager *ui_manager,
 +                                   guint merge_id)
 +{
 +	GtkAction *action;
 +	const gchar *action_name;
 +	const gchar *path;
 +	GSList *list;
 +
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +	g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager));
 +
 +	action_name = "mail-charset-default";
 +	action = e_mail_reader_get_action (reader, action_name);
 +	g_return_if_fail (action != NULL);
 +
 +	list = gtk_radio_action_get_group (GTK_RADIO_ACTION (action));
 +	list = g_slist_copy (list);
 +	list = g_slist_remove (list, action);
 +	list = g_slist_sort (list, (GCompareFunc) e_action_compare_by_label);
 +
 +	path = "/main-menu/view-menu/mail-message-view-actions/mail-encoding-menu";
 +
 +	while (list != NULL) {
 +		action = list->data;
 +
 +		gtk_ui_manager_add_ui (
 +			ui_manager, merge_id, path,
 +			gtk_action_get_name (action),
 +			gtk_action_get_name (action),
 +			GTK_UI_MANAGER_AUTO, FALSE);
 +
 +		list = g_slist_delete_link (list, list);
 +	}
 +
 +	gtk_ui_manager_ensure_update (ui_manager);
 +}
 +
 +void
 +e_mail_reader_show_search_bar (EMailReader *reader)
 +{
 +	g_return_if_fail (E_IS_MAIL_READER (reader));
 +
 +	g_signal_emit (reader, signals[SHOW_SEARCH_BAR], 0);
 +}
diff --cc mail/em-account-editor.c
index e835d28,287fecc..e25d055
--- a/mail/em-account-editor.c
+++ b/mail/em-account-editor.c
@@@ -3047,9 -2746,7 +3047,11 @@@ emae_check_complete (EConfig *ec, cons
  	const gchar *tmp;
  	EAccount *ea;
  	gboolean refresh = FALSE;
 -	gboolean edit = emae->original != NULL;
++	gboolean edit;
 +
 +	account = em_account_editor_get_modified_account (emae);
 +	original_account = em_account_editor_get_modified_account (emae);
++	edit = (original_account != NULL);
  
  	/* We use the page-check of various pages to 'prepare' or
  	   pre-load their values, only in the druid */
@@@ -3085,22 -2782,31 +3087,22 @@@
  				user[at-tmp] = 0;
  				at++;
  
 -				index = check_servers(at);
 -				gtk_entry_set_text(emae->priv->source.username, user);
 -				gtk_entry_set_text(emae->priv->transport.username, user);
 -				if (!edit && uri && (url = camel_url_new(uri, NULL)) != NULL) {
 +				index = check_servers (at);
 +				gtk_entry_set_text (emae->priv->source.username, user);
 +				gtk_entry_set_text (emae->priv->transport.username, user);
- 				if (uri && (url = camel_url_new (uri, NULL)) != NULL) {
++				if (!edit && uri && (url = camel_url_new (uri, NULL)) != NULL) {
  					refresh = TRUE;
 +					camel_url_set_protocol (url, mail_servers[index].proto);
 +					camel_url_set_param (url, "use_ssl", mail_servers[index].ssl);
 +					camel_url_set_host (url, mail_servers[index].recv);
  					camel_url_set_user (url, user);
 -					if (index != -1) {
 -						camel_url_set_protocol(url, mail_servers[index].proto);
 -						camel_url_set_param(url, "use_ssl", mail_servers[index].ssl);
 -						camel_url_set_host (url, mail_servers[index].recv);
 -						gtk_entry_set_text(emae->priv->source.hostname, mail_servers[index].recv);
 -						gtk_entry_set_text(emae->priv->transport.hostname, mail_servers[index].send);
 -						camel_url_set_host (url, mail_servers[index].recv);
 -
 -					} else {
 -						camel_url_set_host (url, "");
 -					}
 -					camel_url_set_user (url, user);
 +					gtk_entry_set_text (emae->priv->source.hostname, mail_servers[index].recv);
 +					gtk_entry_set_text (emae->priv->transport.hostname, mail_servers[index].send);
 +					uri = camel_url_to_string (url, 0);
 +					e_account_set_string (account, E_ACCOUNT_SOURCE_URL, uri);
 +
  					g_free (uri);
 -					uri = camel_url_to_string(url, 0);
 -					e_account_set_string(emae->account, E_ACCOUNT_SOURCE_URL, uri);
 -					g_free(uri);
 -					camel_url_free(url);
 -				} else {
 -					g_free(uri);
 +					camel_url_free (url);
  				}
  
  			}
diff --cc mail/em-composer-utils.h
index b16a1e5,66168e3..f8c2cd5
--- a/mail/em-composer-utils.h
+++ b/mail/em-composer-utils.h
@@@ -36,9 -44,9 +36,9 @@@ void em_utils_compose_new_message (cons
  EMsgComposer * em_utils_compose_lite_new_message (const gchar *fromuri);
  
  /* FIXME: mailto?  url?  should make up its mind what its called.  imho use 'uri' */
 -EMsgComposer * em_utils_compose_new_message_with_mailto (const gchar *url, const gchar *fromuri);
 +void em_utils_compose_new_message_with_mailto (const gchar *url, const gchar *fromuri);
  
- void em_utils_edit_message (CamelMimeMessage *message, CamelFolder *folder);
+ GtkWidget * em_utils_edit_message (CamelMimeMessage *message, CamelFolder *folder);
  void em_utils_edit_messages (CamelFolder *folder, GPtrArray *uids, gboolean replace);
  
  void em_utils_forward_attached (CamelFolder *folder, GPtrArray *uids, const gchar *fromuri);
diff --cc mail/em-folder-tree.c
index d10b88d,1702713..96c2e13
--- a/mail/em-folder-tree.c
+++ b/mail/em-folder-tree.c
@@@ -154,406 -154,54 +154,403 @@@ static guint signals[LAST_SIGNAL] = { 
  extern CamelSession *session;
  extern CamelStore *vfolder_store;
  
 -static void em_folder_tree_class_init (EMFolderTreeClass *klass);
 -static void em_folder_tree_init (EMFolderTree *emft);
 -static void em_folder_tree_destroy (GtkObject *obj);
 -static void em_folder_tree_finalize (GObject *obj);
 +struct _emft_selection_data {
 +	GtkTreeModel *model;
 +	GtkTreeIter *iter;
 +	gboolean set;
 +};
  
 -static gboolean emft_save_state (EMFolderTree *emft);
 -static void emft_queue_save_state (EMFolderTree *emft);
 +static gpointer parent_class = NULL;
 +
 +struct _EMFolderTreeGetFolderInfo {
 +	MailMsg base;
 +
 +	/* input data */
 +	GtkTreeRowReference *root;
 +	EMFolderTree *emft;
 +	CamelStore *store;
 +	guint32 flags;
 +	gchar *top;
  
 -static void emft_update_model_expanded_state (struct _EMFolderTreePrivate *priv, GtkTreeIter *iter, gboolean expanded);
 +	/* output data */
 +	CamelFolderInfo *fi;
 +};
  
 -static void emft_tree_row_activated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, EMFolderTree *emft);
 -static gboolean emft_tree_test_collapse_row (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *path, EMFolderTree *emft);
 -static void emft_tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *path, EMFolderTree *emft);
 -static gboolean emft_tree_button_press (GtkTreeView *treeview, GdkEventButton *event, EMFolderTree *emft);
 -static void emft_tree_selection_changed (GtkTreeSelection *selection, EMFolderTree *emft);
 -static gboolean emft_tree_user_event (GtkTreeView *treeview, GdkEvent *e, EMFolderTree *emft);
 -static gboolean emft_popup_menu (GtkWidget *widget);
 +static gchar *
 +emft_get_folder_info__desc (struct _EMFolderTreeGetFolderInfo *m)
 +{
 +	gchar *ret, *name;
  
 -struct _emft_selection_data {
 +	name = camel_service_get_name((CamelService *)m->store, TRUE);
 +	ret = g_strdup_printf(_("Scanning folders in \"%s\""), name);
 +	g_free(name);
 +	return ret;
 +}
 +
 +static void
 +emft_get_folder_info__exec (struct _EMFolderTreeGetFolderInfo *m)
 +{
 +	guint32 flags = m->flags | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
 +
 +	m->fi = camel_store_get_folder_info (m->store, m->top, flags, &m->base.ex);
 +}
 +
 +static void
 +emft_get_folder_info__done (struct _EMFolderTreeGetFolderInfo *m)
 +{
 +	struct _EMFolderTreeModelStoreInfo *si;
 +	GtkTreeIter root, iter, titer;
 +	CamelFolderInfo *fi;
 +	GtkTreeView *tree_view;
  	GtkTreeModel *model;
 -	GtkTreeIter *iter;
 -	gboolean set;
 +	GtkTreePath *path;
 +	gboolean is_store;
 +
 +	/* check that we haven't been destroyed */
 +	g_return_if_fail (GTK_IS_TREE_VIEW (m->emft));
 +
 +	/* check that our parent folder hasn't been deleted/unsubscribed */
 +	if (!gtk_tree_row_reference_valid (m->root))
 +		return;
 +
 +	tree_view = GTK_TREE_VIEW (m->emft);
 +	model = gtk_tree_view_get_model (tree_view);
 +
 +	si = em_folder_tree_model_lookup_store_info (
 +		EM_FOLDER_TREE_MODEL (model), m->store);
 +	if (si == NULL) {
 +		/* store has been removed in the interim - do nothing */
 +		return;
 +	}
 +
 +	path = gtk_tree_row_reference_get_path (m->root);
 +	gtk_tree_model_get_iter (model, &root, path);
 +
 +	/* if we had an error, then we need to re-set the load subdirs state and collapse the node */
 +	if (!m->fi && camel_exception_is_set(&m->base.ex)) {
 +		gtk_tree_store_set(
 +			GTK_TREE_STORE (model), &root,
 +			COL_BOOL_LOAD_SUBDIRS, TRUE, -1);
 +		gtk_tree_view_collapse_row (tree_view, path);
 +		gtk_tree_path_free (path);
 +		return;
 +	}
 +
 +	gtk_tree_path_free (path);
 +
 +	/* make sure we still need to load the tree subfolders... */
 +	gtk_tree_model_get (model, &root, COL_BOOL_IS_STORE, &is_store, -1);
 +
 +	/* get the first child (which will be a dummy node) */
 +	gtk_tree_model_iter_children (model, &iter, &root);
 +
 +	/* Traverse to the last valid iter */
 +	titer = iter;
 +	while (gtk_tree_model_iter_next (model, &iter))
 +		titer = iter; /* Preserve the last valid iter */
 +
 +	iter = titer;
 +
 +	/* FIXME: camel's IMAP code is totally on crack here, @top's
 +	 * folder info should be @fi and fi->child should be what we
 +	 * want to fill our tree with... *sigh* */
 +	if (m->top && m->fi && !strcmp (m->fi->full_name, m->top)) {
 +		if (!(fi = m->fi->child))
 +			fi = m->fi->next;
 +	} else
 +		fi = m->fi;
 +
 +	if (fi == NULL) {
 +		/* no children afterall... remove the "Loading..." placeholder node */
 +		gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
 +
 +		if (is_store) {
 +			path = gtk_tree_model_get_path (model, &root);
 +			gtk_tree_view_collapse_row (tree_view, path);
 +			gtk_tree_path_free (path);
 +			return;
 +		}
 +	} else {
 +		gint fully_loaded = (m->flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) ? TRUE : FALSE;
 +
 +		do {
 +			gboolean known = g_hash_table_lookup (si->full_hash, fi->full_name) != NULL;
 +
 +			if (!known)
 +				em_folder_tree_model_set_folder_info (
 +					EM_FOLDER_TREE_MODEL (model),
 +					&iter, si, fi, fully_loaded);
 +
 +			if ((fi = fi->next) != NULL && !known)
 +				gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &root);
 +		} while (fi != NULL);
 +	}
 +
 +	gtk_tree_store_set (
 +		GTK_TREE_STORE (model), &root,
 +		COL_BOOL_LOAD_SUBDIRS, FALSE, -1);
 +}
 +
 +static void
 +emft_get_folder_info__free (struct _EMFolderTreeGetFolderInfo *m)
 +{
 +	camel_store_free_folder_info (m->store, m->fi);
 +
 +	gtk_tree_row_reference_free (m->root);
 +	g_object_unref(m->emft);
 +	camel_object_unref (m->store);
 +	g_free (m->top);
 +}
 +
 +static MailMsgInfo get_folder_info_info = {
 +	sizeof (struct _EMFolderTreeGetFolderInfo),
 +	(MailMsgDescFunc) emft_get_folder_info__desc,
 +	(MailMsgExecFunc) emft_get_folder_info__exec,
 +	(MailMsgDoneFunc) emft_get_folder_info__done,
 +	(MailMsgFreeFunc) emft_get_folder_info__free
  };
  
 -static GtkVBoxClass *parent_class = NULL;
 +static void
 +folder_tree_emit_popup_event (EMFolderTree *emft,
 +                              GdkEvent *event)
 +{
 +	g_signal_emit (emft, signals[POPUP_EVENT], 0, event);
 +}
 +
 +static void
 +emft_free_select_uri (struct _selected_uri *u)
 +{
 +	g_free (u->uri);
 +	if (u->store)
 +		camel_object_unref (u->store);
 +	g_free (u->key);
 +	g_free (u->path);
 +	g_free (u);
 +}
  
 -GType
 -em_folder_tree_get_type (void)
 +static gboolean
 +emft_select_func (GtkTreeSelection *selection,
 +                  GtkTreeModel *model,
 +                  GtkTreePath *path,
 +                  gboolean selected)
  {
 -	static GType type = 0;
 +	EMFolderTreePrivate *priv;
 +	GtkTreeView *tree_view;
 +	gboolean is_store;
 +	guint32 flags;
 +	GtkTreeIter iter;
  
 -	if (!type) {
 -		static const GTypeInfo info = {
 -			sizeof (EMFolderTreeClass),
 -			NULL, /* base_class_init */
 -			NULL, /* base_class_finalize */
 -			(GClassInitFunc) em_folder_tree_class_init,
 -			NULL, /* class_finalize */
 -			NULL, /* class_data */
 -			sizeof (EMFolderTree),
 -			0,    /* n_preallocs */
 -			(GInstanceInitFunc) em_folder_tree_init,
 -		};
 +	tree_view = gtk_tree_selection_get_tree_view (selection);
 +
 +	priv = EM_FOLDER_TREE_GET_PRIVATE (tree_view);
 +
 +	if (selected)
 +		return TRUE;
 +
 +	if (priv->excluded == 0 && priv->excluded_func == NULL)
 +		return TRUE;
 +
 +	if (!gtk_tree_model_get_iter (model, &iter, path))
 +		return TRUE;
 +
 +	if (priv->excluded_func != NULL)
 +		return priv->excluded_func(
 +			EM_FOLDER_TREE (tree_view), model,
 +			&iter, priv->excluded_data);
 +
 +	gtk_tree_model_get (
 +		model, &iter, COL_UINT_FLAGS, &flags,
 +		COL_BOOL_IS_STORE, &is_store, -1);
 +
 +	if (is_store)
 +		flags |= CAMEL_FOLDER_NOSELECT;
 +
 +	return (flags & priv->excluded) == 0;
 +}
 +
 +static void
 +emft_clear_selected_list(EMFolderTree *emft)
 +{
 +	EMFolderTreePrivate *priv = emft->priv;
 +
 +	g_slist_foreach(priv->select_uris, (GFunc) emft_free_select_uri, NULL);
 +	g_slist_free(priv->select_uris);
 +	g_hash_table_destroy(priv->select_uris_table);
 +	priv->select_uris = NULL;
 +	priv->select_uris_table = g_hash_table_new(g_str_hash, g_str_equal);
 +	priv->cursor_set = FALSE;
 +}
 +
 +static void
 +emft_selection_changed_cb (EMFolderTree *emft,
 +                           GtkTreeSelection *selection)
 +{
 +	GtkTreeModel *model;
 +	GtkTreeIter iter;
 +	GList *list;
 +	guint32 flags = 0;
 +	guint unread = 0;
 +	guint old_unread = 0;
 +	gchar *full_name = NULL;
 +	gchar *uri = NULL;
 +
 +	list = gtk_tree_selection_get_selected_rows (selection, &model);
 +
 +	if (list == NULL)
 +		goto exit;
 +
 +	gtk_tree_model_get_iter (model, &iter, list->data);
 +
 +	gtk_tree_model_get (
 +		model, &iter,
 +		COL_STRING_FULL_NAME, &full_name,
 +		COL_STRING_URI, &uri, COL_UINT_FLAGS, &flags,
 +		COL_UINT_UNREAD, &unread, COL_UINT_UNREAD_LAST_SEL,
 +		&old_unread, -1);
 +
 +	/* Sync unread counts to distinguish new incoming mail. */
 +	if (unread != old_unread)
 +		gtk_tree_store_set (
 +			GTK_TREE_STORE (model), &iter,
 +			COL_UINT_UNREAD_LAST_SEL, unread, -1);
  
 -		type = g_type_register_static (GTK_TYPE_VBOX, "EMFolderTree", &info, 0);
 +exit:
 +	g_signal_emit (
 +		emft, signals[FOLDER_SELECTED], 0, full_name, uri, flags);
 +
 +	g_free (full_name);
 +	g_free (uri);
 +
 +	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
 +	g_list_free (list);
 +}
 +
 +static void
 +folder_tree_finalize (GObject *object)
 +{
 +	EMFolderTreePrivate *priv;
 +
 +	priv = EM_FOLDER_TREE_GET_PRIVATE (object);
 +
 +	if (priv->select_uris != NULL) {
 +		g_slist_foreach (
 +			priv->select_uris,
 +			(GFunc) emft_free_select_uri, NULL);
 +		g_slist_free (priv->select_uris);
 +		g_hash_table_destroy (priv->select_uris_table);
 +		priv->select_uris = NULL;
  	}
  
 -	return type;
 +	/* Chain up to parent's finalize() method. */
 +	G_OBJECT_CLASS (parent_class)->finalize (object);
 +}
 +
 +static void
 +em_folder_tree_destroy (GtkObject *object)
 +{
 +	EMFolderTreePrivate *priv;
 +	GtkTreeModel *model;
 +
 +	priv = EM_FOLDER_TREE_GET_PRIVATE (object);
 +
 +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (object));
 +
 +	if (priv->loaded_row_id != 0) {
 +		g_signal_handler_disconnect (model, priv->loaded_row_id);
 +		priv->loaded_row_id = 0;
 +	}
 +
 +	if (priv->autoscroll_id != 0) {
 +		g_source_remove (priv->autoscroll_id);
 +		priv->autoscroll_id = 0;
 +	}
 +
 +	if (priv->autoexpand_id != 0) {
 +		gtk_tree_row_reference_free (priv->autoexpand_row);
 +		priv->autoexpand_row = NULL;
 +
 +		g_source_remove (priv->autoexpand_id);
 +		priv->autoexpand_id = 0;
 +	}
 +
 +	/* Chain up to parent's destroy() method. */
 +	GTK_OBJECT_CLASS (parent_class)->destroy (object);
 +}
 +
 +static gboolean
 +emft_button_press_event (GtkWidget *widget,
 +                         GdkEventButton *event)
 +{
 +	EMFolderTreePrivate *priv;
 +	GtkWidgetClass *widget_class;
 +	GtkTreeSelection *selection;
 +	GtkTreeView *tree_view;
 +	GtkTreePath *path;
 +
 +	priv = EM_FOLDER_TREE_GET_PRIVATE (widget);
 +
 +	tree_view = GTK_TREE_VIEW (widget);
 +	selection = gtk_tree_view_get_selection (tree_view);
 +
 +	if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE)
 +		emft_clear_selected_list (EM_FOLDER_TREE (widget));
 +
 +	priv->cursor_set = TRUE;
 +
 +	if (event->button != 3)
 +		goto chainup;
 +
 +	if (!gtk_tree_view_get_path_at_pos (
 +		tree_view, event->x, event->y,
 +		&path, NULL, NULL, NULL))
 +		goto chainup;
 +
 +	/* select/focus the row that was right-clicked */
 +	gtk_tree_selection_select_path (selection, path);
 +	gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
 +
 +	gtk_tree_path_free (path);
 +
 +	folder_tree_emit_popup_event (
 +		EM_FOLDER_TREE (tree_view), (GdkEvent *) event);
 +
 +chainup:
 +
 +	/* Chain up to parent's button_press_event() method. */
 +	widget_class = GTK_WIDGET_CLASS (parent_class);
 +	return widget_class->button_press_event (widget, event);
 +}
 +
 +static gboolean
 +emft_key_press_event (GtkWidget *widget,
 +                      GdkEventKey *event)
 +{
 +	EMFolderTreePrivate *priv;
 +	GtkWidgetClass *widget_class;
 +	GtkTreeSelection *selection;
 +	GtkTreeView *tree_view;
 +
 +	priv = EM_FOLDER_TREE_GET_PRIVATE (widget);
 +
 +	tree_view = GTK_TREE_VIEW (widget);
 +	selection = gtk_tree_view_get_selection (tree_view);
 +
- 	if (event->keyval == GDK_space)
- 		return TRUE;
- 
 +	if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE)
 +		emft_clear_selected_list (EM_FOLDER_TREE (widget));
 +
 +	priv->cursor_set = TRUE;
 +
 +	/* Chain up to parent's key_press_event() method. */
 +	widget_class = GTK_WIDGET_CLASS (parent_class);
 +	return widget_class->key_press_event (widget, event);
 +}
 +
 +static gboolean
 +emft_popup_menu (GtkWidget *widget)
 +{
 +	folder_tree_emit_popup_event (EM_FOLDER_TREE (widget), NULL);
 +
 +	return TRUE;
  }
  
  static void
diff --cc mail/em-vfolder-editor.c
index 1aab32d,3fd822f..9c6f5f8
--- a/mail/em-vfolder-editor.c
+++ b/mail/em-vfolder-editor.c
@@@ -113,10 -113,11 +113,11 @@@ em_vfolder_editor_new (EMVFolderContex
  	g_free (gladefile);
  
  	rule_editor_construct ((RuleEditor *) ve, (RuleContext *) vc, gui, "incoming", _("Search _Folders"));
-         gtk_widget_hide(glade_xml_get_widget (gui, "filter_source"));
+ 	gtk_widget_hide (glade_xml_get_widget (gui, "label17"));
+ 	gtk_widget_hide (glade_xml_get_widget (gui, "filter_source_combobox"));
  	g_object_unref (gui);
  
 -	return ve;
 +	return GTK_WIDGET (ve);
  }
  
  static FilterRule *
diff --cc modules/mail/e-mail-shell-view-private.c
index d410abb,0000000..ca2aa85
mode 100644,000000..100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@@ -1,959 -1,0 +1,1009 @@@
 +/*
 + * e-mail-shell-view-private.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 <http://www.gnu.org/licenses/>
 + *
 + *
 + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 + *
 + */
 +
 +#include "e-mail-shell-view-private.h"
 +
 +#include "widgets/menus/gal-view-factory-etable.h"
 +
 +static void
 +mail_shell_view_folder_tree_selected_cb (EMailShellView *mail_shell_view,
 +                                         const gchar *full_name,
 +                                         const gchar *uri,
 +                                         guint32 flags,
 +                                         EMFolderTree *folder_tree)
 +{
 +	EShellView *shell_view;
 +	EMailReader *reader;
 +	gboolean folder_selected;
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
 +
 +	folder_selected =
 +		!(flags & CAMEL_FOLDER_NOSELECT) &&
 +		full_name != NULL;
 +
 +	if (folder_selected)
 +		e_mail_reader_set_folder_uri (reader, uri);
 +	else
 +		e_mail_reader_set_folder (reader, NULL, NULL);
 +
 +	e_shell_view_update_actions (shell_view);
 +}
 +
++static gboolean
++mail_shell_view_folder_tree_key_press_event_cb (EMailShellView *mail_shell_view,
++                                                GdkEventKey *event)
++{
++	EMailReader *reader;
++	gboolean handled = FALSE;
++
++	if ((event->state & GDK_CONTROL_MASK) != 0)
++		goto ctrl;
++
++	/* <keyval> alone */
++	switch (event->keyval) {
++		case GDK_period:
++		case GDK_comma:
++		case GDK_bracketleft:
++		case GDK_bracketright:
++			goto emit;
++
++		default:
++			goto exit;
++	}
++
++ctrl:
++	/* Ctrl + <keyval> */
++	switch (event->keyval) {
++		case GDK_period:
++		case GDK_comma:
++			goto emit;
++
++		default:
++			goto exit;
++	}
++
++	/* All branches jump past this. */
++	g_return_val_if_reached (FALSE);
++
++emit:
++	/* Forward the key press to the EMailReader interface. */
++	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
++	g_signal_emit_by_name (reader, "key-press-event", event, &handled);
++
++exit:
++	return handled;
++}
++
 +static void
 +mail_shell_view_folder_tree_popup_event_cb (EShellView *shell_view,
 +                                            GdkEventButton *event)
 +{
 +	const gchar *widget_path;
 +
 +	widget_path = "/mail-folder-popup";
 +	e_shell_view_show_popup_menu (shell_view, widget_path, event);
 +}
 +
 +static gboolean
 +mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view,
 +                                    GdkEventKey *event)
 +{
 +	EShellView *shell_view;
 +	EShellWindow *shell_window;
 +	GtkAction *action;
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_window = e_shell_view_get_shell_window (shell_view);
 +
 +	if ((event->state & GDK_CONTROL_MASK) != 0)
 +		return FALSE;
 +
 +	switch (event->keyval) {
 +		case GDK_space:
 +			action = ACTION (MAIL_SMART_FORWARD);
 +			break;
 +
 +		case GDK_BackSpace:
 +			action = ACTION (MAIL_SMART_BACKWARD);
 +			break;
 +
 +		default:
 +			return FALSE;
 +	}
 +
 +	gtk_action_activate (action);
 +
 +	return TRUE;
 +}
 +
 +static gint
 +mail_shell_view_message_list_key_press_cb (EMailShellView *mail_shell_view,
 +                                           gint row,
 +                                           ETreePath path,
 +                                           gint col,
 +                                           GdkEvent *event)
 +{
 +	return mail_shell_view_key_press_event_cb (
 +		mail_shell_view, &event->key);
 +}
 +
 +static gboolean
 +mail_shell_view_message_list_right_click_cb (EShellView *shell_view,
 +                                             gint row,
 +                                             ETreePath path,
 +                                             gint col,
 +                                             GdkEventButton *event)
 +{
 +	const gchar *widget_path;
 +
 +	widget_path = "/mail-message-popup";
 +	e_shell_view_show_popup_menu (shell_view, widget_path, event);
 +
 +	return TRUE;
 +}
 +
 +static void
 +mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
 +                                   EMailReader *reader)
 +{
 +	EMailShellContent *mail_shell_content;
 +
 +	mail_shell_content = mail_shell_view->priv->mail_shell_content;
 +	e_mail_shell_content_update_view_instance (mail_shell_content);
 +	e_mail_shell_view_update_sidebar (mail_shell_view);
 +}
 +
 +static void
 +mail_shell_view_reader_status_message_cb (EMailShellView *mail_shell_view,
 +                                          const gchar *status_message)
 +{
 +	EShellView *shell_view;
 +	EShellTaskbar *shell_taskbar;
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
 +
 +	e_shell_taskbar_set_message (shell_taskbar, status_message);
 +}
 +
 +static void
 +mail_shell_view_scroll_cb (EMailShellView *mail_shell_view,
 +                           GtkOrientation orientation,
 +                           GtkScrollType scroll_type,
 +                           gfloat position,
 +                           GtkHTML *html)
 +{
 +	EShell *shell;
 +	EShellView *shell_view;
 +	EShellWindow *shell_window;
 +	EShellSettings *shell_settings;
 +	EMailReader *reader;
 +	MessageList *message_list;
 +	gboolean magic_spacebar;
 +
 +	if (html->binding_handled)
 +		return;
 +
 +	if (orientation != GTK_ORIENTATION_VERTICAL)
 +		return;
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_window = e_shell_view_get_shell_window (shell_view);
 +	shell = e_shell_window_get_shell (shell_window);
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	magic_spacebar = e_shell_settings_get_boolean (
 +		shell_settings, "mail-magic-spacebar");
 +
 +	if (!magic_spacebar)
 +		return;
 +
 +	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	if (scroll_type == GTK_SCROLL_PAGE_FORWARD) {
 +		gtk_widget_grab_focus (GTK_WIDGET (message_list));
 +		message_list_select (
 +			message_list, MESSAGE_LIST_SELECT_NEXT,
 +			0, CAMEL_MESSAGE_SEEN);
 +	} else {
 +		gtk_widget_grab_focus (GTK_WIDGET (message_list));
 +		message_list_select (
 +			message_list, MESSAGE_LIST_SELECT_PREVIOUS,
 +			0, CAMEL_MESSAGE_SEEN);
 +	}
 +}
 +
 +static void
 +mail_shell_view_load_view_collection (EShellViewClass *shell_view_class)
 +{
 +	GalViewCollection *collection;
 +	GalViewFactory *factory;
 +	ETableSpecification *spec;
 +	const gchar *base_dir;
 +	gchar *filename;
 +
 +	collection = shell_view_class->view_collection;
 +
 +	base_dir = EVOLUTION_ETSPECDIR;
 +	spec = e_table_specification_new ();
 +	filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
 +	if (!e_table_specification_load_from_file (spec, filename))
 +		g_critical ("Unable to load ETable specification file "
 +			    "for mail");
 +	g_free (filename);
 +
 +	factory = gal_view_factory_etable_new (spec);
 +	gal_view_collection_add_factory (collection, factory);
 +	g_object_unref (factory);
 +	g_object_unref (spec);
 +
 +	gal_view_collection_load (collection);
 +}
 +
 +static void
 +mail_shell_view_notify_view_id_cb (EMailShellView *mail_shell_view)
 +{
 +	EMailShellContent *mail_shell_content;
 +	GalViewInstance *view_instance;
 +	const gchar *view_id;
 +
 +	mail_shell_content = mail_shell_view->priv->mail_shell_content;
 +	view_instance = NULL;  /* FIXME */
 +	view_id = e_shell_view_get_view_id (E_SHELL_VIEW (mail_shell_view));
 +
 +	/* A NULL view ID implies we're in a custom view.  But you can
 +	 * only get to a custom view via the "Define Views" dialog, which
 +	 * would have already modified the view instance appropriately.
 +	 * Furthermore, there's no way to refer to a custom view by ID
 +	 * anyway, since custom views have no IDs. */
 +	if (view_id == NULL)
 +		return;
 +
 +	gal_view_instance_set_current_view_id (view_instance, view_id);
 +}
 +
 +void
 +e_mail_shell_view_private_init (EMailShellView *mail_shell_view,
 +                                EShellViewClass *shell_view_class)
 +{
 +	if (!gal_view_collection_loaded (shell_view_class->view_collection))
 +		mail_shell_view_load_view_collection (shell_view_class);
 +
 +	g_signal_connect (
 +		mail_shell_view, "notify::view-id",
 +		G_CALLBACK (mail_shell_view_notify_view_id_cb), NULL);
 +}
 +
 +void
 +e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 +{
 +	EMailShellViewPrivate *priv = mail_shell_view->priv;
 +	EMailShellSidebar *mail_shell_sidebar;
 +	EShell *shell;
 +	EShellView *shell_view;
 +	EShellBackend *shell_backend;
 +	EShellContent *shell_content;
 +	EShellSettings *shell_settings;
 +	EShellSidebar *shell_sidebar;
 +	EShellWindow *shell_window;
 +	EMFormatHTMLDisplay *html_display;
 +	EMFolderTree *folder_tree;
 +	RuleContext *context;
 +	FilterRule *rule = NULL;
 +	GtkTreeModel *tree_model;
 +	GtkUIManager *ui_manager;
 +	MessageList *message_list;
 +	EMailReader *reader;
 +	GtkHTML *html;
 +	const gchar *source;
 +	guint merge_id;
 +	gint ii = 0;
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_backend = e_shell_view_get_shell_backend (shell_view);
 +	shell_content = e_shell_view_get_shell_content (shell_view);
 +	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 +	shell_window = e_shell_view_get_shell_window (shell_view);
 +	ui_manager = e_shell_window_get_ui_manager (shell_window);
 +
 +	shell = e_shell_window_get_shell (shell_window);
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	tree_model = e_shell_settings_get_object (
 +		shell_settings, "mail-label-list-store");
 +
 +	e_shell_window_add_action_group (shell_window, "mail");
 +	e_shell_window_add_action_group (shell_window, "mail-filter");
 +	e_shell_window_add_action_group (shell_window, "mail-label");
 +
 +	merge_id = gtk_ui_manager_new_merge_id (ui_manager);
 +	priv->label_merge_id = merge_id;
 +
 +	/* Cache these to avoid lots of awkward casting. */
 +	priv->mail_shell_backend = g_object_ref (shell_backend);
 +	priv->mail_shell_content = g_object_ref (shell_content);
 +	priv->mail_shell_sidebar = g_object_ref (shell_sidebar);
 +
 +	reader = E_MAIL_READER (shell_content);
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
 +	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
 +
 +	html = EM_FORMAT_HTML (html_display)->html;
 +
 +	g_signal_connect_swapped (
 +		folder_tree, "folder-selected",
 +		G_CALLBACK (mail_shell_view_folder_tree_selected_cb),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
++		folder_tree, "key-press-event",
++		G_CALLBACK (mail_shell_view_folder_tree_key_press_event_cb),
++		mail_shell_view);
++
++	g_signal_connect_swapped (
 +		folder_tree, "popup-event",
 +		G_CALLBACK (mail_shell_view_folder_tree_popup_event_cb),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		message_list->tree, "key-press",
 +		G_CALLBACK (mail_shell_view_message_list_key_press_cb),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		message_list->tree, "right-click",
 +		G_CALLBACK (mail_shell_view_message_list_right_click_cb),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		reader, "changed",
 +		G_CALLBACK (mail_shell_view_reader_changed_cb),
 +		mail_shell_view);
 +
 +	/* Use the same callback as "changed". */
 +	g_signal_connect_swapped (
 +		reader, "folder-loaded",
 +		G_CALLBACK (mail_shell_view_reader_changed_cb),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		reader, "folder-loaded",
 +		G_CALLBACK (e_mail_shell_view_restore_state),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		tree_model, "row-changed",
 +		G_CALLBACK (e_mail_shell_view_update_search_filter),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		tree_model, "row-deleted",
 +		G_CALLBACK (e_mail_shell_view_update_search_filter),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		tree_model, "row-inserted",
 +		G_CALLBACK (e_mail_shell_view_update_search_filter),
 +		mail_shell_view);
 +
 +	g_signal_connect_swapped (
 +		html, "key-press-event",
 +		G_CALLBACK (mail_shell_view_key_press_event_cb),
 +		mail_shell_view);
 +
 +	g_signal_connect_data (
 +		html, "scroll",
 +		G_CALLBACK (mail_shell_view_scroll_cb),
 +		mail_shell_view, (GClosureNotify) NULL,
 +		G_CONNECT_AFTER | G_CONNECT_SWAPPED);
 +
 +	g_signal_connect_swapped (
 +		html, "status-message",
 +		G_CALLBACK (mail_shell_view_reader_status_message_cb),
 +		mail_shell_view);
 +
 +	e_mail_shell_view_actions_init (mail_shell_view);
 +	e_mail_shell_view_update_search_filter (mail_shell_view);
 +	e_mail_reader_init (reader);
 +
 +	/* Populate built-in rules for search entry popup menu.
 +	 * Keep the assertions, please.  If the conditions aren't
 +	 * met we're going to crash anyway, just more mysteriously. */
 +	context = e_shell_content_get_search_context (shell_content);
 +	source = FILTER_SOURCE_DEMAND;
 +	while ((rule = rule_context_next_rule (context, rule, source))) {
 +		g_assert (ii < MAIL_NUM_SEARCH_RULES);
 +		priv->search_rules[ii++] = g_object_ref (rule);
 +	}
 +	g_assert (ii == MAIL_NUM_SEARCH_RULES);
 +}
 +
 +void
 +e_mail_shell_view_private_dispose (EMailShellView *mail_shell_view)
 +{
 +	EMailShellViewPrivate *priv = mail_shell_view->priv;
 +	gint ii;
 +
 +	DISPOSE (priv->mail_shell_backend);
 +	DISPOSE (priv->mail_shell_content);
 +	DISPOSE (priv->mail_shell_sidebar);
 +
 +	for (ii = 0; ii < MAIL_NUM_SEARCH_RULES; ii++)
 +		DISPOSE (priv->search_rules[ii]);
 +}
 +
 +void
 +e_mail_shell_view_private_finalize (EMailShellView *mail_shell_view)
 +{
 +	/* XXX Nothing to do? */
 +}
 +
 +void
 +e_mail_shell_view_restore_state (EMailShellView *mail_shell_view)
 +{
 +	EShellView *shell_view;
 +	EShellContent *shell_content;
 +	EMailReader *reader;
 +	MessageList *message_list;
 +	const gchar *folder_uri;
 +	gchar *group_name;
 +
 +	/* XXX Move this to EMailShellContent. */
 +
 +	g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_content = e_shell_view_get_shell_content (shell_view);
 +
 +	reader = E_MAIL_READER (shell_content);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	folder_uri = message_list->folder_uri;
 +	g_return_if_fail (folder_uri != NULL);
 +
 +	group_name = g_strdup_printf ("Folder %s", folder_uri);
 +	e_shell_content_restore_state (shell_content, group_name);
 +	g_free (group_name);
 +}
 +
 +void
 +e_mail_shell_view_execute_search (EMailShellView *mail_shell_view)
 +{
 +	EShell *shell;
 +	EShellView *shell_view;
 +	EShellWindow *shell_window;
 +	EShellContent *shell_content;
 +	EShellSettings *shell_settings;
 +	EMFormatHTMLDisplay *html_display;
 +	EMailShellContent *mail_shell_content;
 +	MessageList *message_list;
 +	FilterRule *rule;
 +	EMailReader *reader;
 +	CamelFolder *folder;
 +	GtkAction *action;
 +	GtkTreeModel *model;
 +	GtkTreePath *path;
 +	GtkTreeIter tree_iter;
 +	GString *string;
 +	GList *iter;
 +	GSList *search_strings = NULL;
 +	const gchar *folder_uri;
 +	const gchar *text;
 +	gboolean valid;
 +	gchar *query;
 +	gchar *temp;
 +	gchar *tag;
 +	gint value;
 +
 +	g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_window = e_shell_view_get_shell_window (shell_view);
 +	shell_content = e_shell_view_get_shell_content (shell_view);
 +
 +	shell = e_shell_window_get_shell (shell_window);
 +	shell_settings = e_shell_get_shell_settings (shell);
 +
 +	mail_shell_content = mail_shell_view->priv->mail_shell_content;
 +
 +	reader = E_MAIL_READER (shell_content);
 +	html_display = e_mail_reader_get_html_display (reader);
 +	message_list = e_mail_reader_get_message_list (reader);
 +
 +	folder_uri = message_list->folder_uri;
 +	folder = message_list->folder;
 +
 +	/* This returns a new object reference. */
 +	model = e_shell_settings_get_object (
 +		shell_settings, "mail-label-list-store");
 +
 +	text = e_shell_content_get_search_text (shell_content);
 +	if (text == NULL || *text == '\0') {
 +		query = g_strdup ("");
 +		goto filter;
 +	}
 +
 +	/* Replace variables in the selected rule with the
 +	 * current search text and extract a query string. */
 +
 +	action = ACTION (MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS);
 +	value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
 +	g_return_if_fail (value >= 0 && value < MAIL_NUM_SEARCH_RULES);
 +	rule = mail_shell_view->priv->search_rules[value];
 +
 +	for (iter = rule->parts; iter != NULL; iter = iter->next) {
 +		FilterPart *part = iter->data;
 +		FilterElement *element = NULL;
 +
 +		if (strcmp (part->name, "subject") == 0)
 +			element = filter_part_find_element (part, "subject");
 +		else if (strcmp (part->name, "body") == 0)
 +			element = filter_part_find_element (part, "word");
 +		else if (strcmp (part->name, "sender") == 0)
 +			element = filter_part_find_element (part, "sender");
 +		else if (strcmp (part->name, "to") == 0)
 +			element = filter_part_find_element (part, "recipient");
 +
 +		if (strcmp (part->name, "body") == 0) {
 +			struct _camel_search_words *words;
 +			gint ii;
 +
 +			words = camel_search_words_split ((guchar *) text);
 +			for (ii = 0; ii < words->len; ii++)
 +				search_strings = g_slist_prepend (
 +					search_strings, g_strdup (
 +					words->words[ii]->word));
 +			camel_search_words_free (words);
 +		}
 +
 +		if (element != NULL) {
 +			FilterInput *input = FILTER_INPUT (element);
 +			filter_input_set_value (input, text);
 +		}
 +	}
 +
 +	string = g_string_sized_new (1024);
 +	filter_rule_build_code (rule, string);
 +	query = g_string_free (string, FALSE);
 +
 +filter:
 +
 +	/* Apply selected filter. */
 +
 +	value = e_shell_content_get_filter_value (shell_content);
 +	switch (value) {
 +		case MAIL_FILTER_ALL_MESSAGES:
 +			break;
 +
 +		case MAIL_FILTER_UNREAD_MESSAGES:
 +			temp = g_strdup_printf (
 +				"(and %s (match-all (not "
 +				"(system-flag \"Seen\"))))", query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		case MAIL_FILTER_NO_LABEL:
 +			string = g_string_sized_new (1024);
 +			g_string_append_printf (
 +				string, "(and %s (and ", query);
 +			valid = gtk_tree_model_get_iter_first (
 +				model, &tree_iter);
 +			while (valid) {
 +				tag = e_mail_label_list_store_get_tag (
 +					E_MAIL_LABEL_LIST_STORE (model),
 +					&tree_iter);
 +				g_string_append_printf (
 +					string, " (match-all (not (or "
 +					"(= (user-tag \"label\") \"%s\") "
 +					"(user-flag \"$Label%s\") "
 +					"(user-flag \"%s\"))))",
 +					tag, tag, tag);
 +				g_free (tag);
 +
 +				valid = gtk_tree_model_iter_next (
 +					model, &tree_iter);
 +			}
 +			g_string_append_len (string, "))", 2);
 +			g_free (query);
 +			query = g_string_free (string, FALSE);
 +			break;
 +
 +		case MAIL_FILTER_READ_MESSAGES:
 +			temp = g_strdup_printf (
 +				"(and %s (match-all "
 +				"(system-flag \"Seen\")))", query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		case MAIL_FILTER_RECENT_MESSAGES:
 +			if (em_utils_folder_is_sent (folder, folder_uri))
 +				temp = g_strdup_printf (
 +					"(and %s (match-all "
 +					"(> (get-sent-date) "
 +					"(- (get-current-date) 86400))))",
 +					query);
 +			else
 +				temp = g_strdup_printf (
 +					"(and %s (match-all "
 +					"(> (get-received-date) "
 +					"(- (get-current-date) 86400))))",
 +					query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		case MAIL_FILTER_LAST_5_DAYS_MESSAGES:
 +			if (em_utils_folder_is_sent (folder, folder_uri))
 +				temp = g_strdup_printf (
 +					"(and %s (match-all "
 +					"(> (get-sent-date) "
 +					"(- (get-current-date) 432000))))",
 +					query);
 +			else
 +				temp = g_strdup_printf (
 +					"(and %s (match-all "
 +					"(> (get-received-date) "
 +					"(- (get-current-date) 432000))))",
 +					query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		case MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS:
 +			temp = g_strdup_printf (
 +				"(and %s (match-all "
 +				"(system-flag \"Attachments\")))", query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		case MAIL_FILTER_IMPORTANT_MESSAGES:
 +			temp = g_strdup_printf (
 +				"(and %s (match-all "
 +				"(system-flag \"Flagged\")))", query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		case MAIL_FILTER_MESSAGES_NOT_JUNK:
 +			temp = g_strdup_printf (
 +				"(and %s (match-all (not "
 +				"(system-flag \"junk\"))))", query);
 +			g_free (query);
 +			query = temp;
 +			break;
 +
 +		default:
 +			/* The action value also serves as a path for
 +			 * the label list store.  That's why we number
 +			 * the label actions from zero. */
 +			path = gtk_tree_path_new_from_indices (value, -1);
 +			gtk_tree_model_get_iter (model, &tree_iter, path);
 +			gtk_tree_path_free (path);
 +
 +			tag = e_mail_label_list_store_get_tag (
 +				E_MAIL_LABEL_LIST_STORE (model), &tree_iter);
 +			temp = g_strdup_printf (
 +				"(and %s (match-all (or "
 +				"(= (user-tag \"label\") \"%s\") "
 +				"(user-flag \"$Label%s\") "
 +				"(user-flag \"%s\"))))",
 +				query, tag, tag, tag);
 +			g_free (tag);
 +
 +			g_free (query);
 +			query = temp;
 +			break;
 +	}
 +
 +	message_list_set_search (message_list, query);
 +
 +	e_mail_shell_content_set_search_strings (
 +		mail_shell_content, search_strings);
 +
 +	g_slist_foreach (search_strings, (GFunc) g_free, NULL);
 +	g_slist_free (search_strings);
 +
 +	g_object_unref (model);
 +	g_free (query);
 +}
 +
 +/* Helper for e_mail_shell_view_create_filter_from_selected() */
 +static void
 +mail_shell_view_create_filter_cb (CamelFolder *folder,
 +                                  const gchar *uid,
 +                                  CamelMimeMessage *message,
 +                                  gpointer user_data)
 +{
 +	struct {
 +		const gchar *source;
 +		gint type;
 +	} *filter_data = user_data;
 +
 +	if (message != NULL)
 +		filter_gui_add_from_message (
 +			message, filter_data->source, filter_data->type);
 +
 +	g_free (filter_data);
 +}
 +
 +void
 +e_mail_shell_view_create_filter_from_selected (EMailShellView *mail_shell_view,
 +                                               gint filter_type)
 +{
 +	EMailReader *reader;
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	const gchar *filter_source;
 +	const gchar *folder_uri;
 +	GPtrArray *uids;
 +
 +	struct {
 +		const gchar *source;
 +		gint type;
 +	} *filter_data;
 +
 +	g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
 +
 +	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	folder_uri = message_list->folder_uri;
 +	folder = message_list->folder;
 +
 +	if (em_utils_folder_is_sent (folder, folder_uri))
 +		filter_source = FILTER_SOURCE_OUTGOING;
 +	else if (em_utils_folder_is_outbox (folder, folder_uri))
 +		filter_source = FILTER_SOURCE_OUTGOING;
 +	else
 +		filter_source = FILTER_SOURCE_INCOMING;
 +
 +	uids = message_list_get_selected (message_list);
 +
 +	if (uids->len == 1) {
 +		filter_data = g_malloc (sizeof (*filter_data));
 +		filter_data->source = filter_source;
 +		filter_data->type = filter_type;
 +
 +		mail_get_message (
 +			folder, uids->pdata[0],
 +			mail_shell_view_create_filter_cb,
 +			filter_data, mail_msg_unordered_push);
 +	}
 +
 +	em_utils_uids_free (uids);
 +}
 +
 +/* Helper for e_mail_shell_view_create_vfolder_from_selected() */
 +static void
 +mail_shell_view_create_vfolder_cb (CamelFolder *folder,
 +                                   const gchar *uid,
 +                                   CamelMimeMessage *message,
 +                                   gpointer user_data)
 +{
 +	struct {
 +		gchar *uri;
 +		gint type;
 +	} *vfolder_data = user_data;
 +
 +	if (message != NULL)
 +		vfolder_gui_add_from_message (
 +			message, vfolder_data->type, vfolder_data->uri);
 +
 +	g_free (vfolder_data->uri);
 +	g_free (vfolder_data);
 +}
 +
 +void
 +e_mail_shell_view_create_vfolder_from_selected (EMailShellView *mail_shell_view,
 +                                                gint vfolder_type)
 +{
 +	EMailReader *reader;
 +	MessageList *message_list;
 +	CamelFolder *folder;
 +	const gchar *folder_uri;
 +	GPtrArray *uids;
 +
 +	struct {
 +		gchar *uri;
 +		gint type;
 +	} *vfolder_data;
 +
 +	g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
 +
 +	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	folder_uri = message_list->folder_uri;
 +	folder = message_list->folder;
 +
 +	uids = message_list_get_selected (message_list);
 +
 +	if (uids->len == 1) {
 +		vfolder_data = g_malloc (sizeof (*vfolder_data));
 +		vfolder_data->uri = g_strdup (folder_uri);
 +		vfolder_data->type = vfolder_type;
 +
 +		mail_get_message (
 +			folder, uids->pdata[0],
 +			mail_shell_view_create_vfolder_cb,
 +			vfolder_data, mail_msg_unordered_push);
 +	}
 +
 +	em_utils_uids_free (uids);
 +}
 +
 +void
 +e_mail_shell_view_update_sidebar (EMailShellView *mail_shell_view)
 +{
 +	EMailShellContent *mail_shell_content;
 +	EShellSidebar *shell_sidebar;
 +	EShellView *shell_view;
 +	EMailReader *reader;
 +	MessageList *message_list;
 +	CamelStore *local_store;
 +	CamelFolder *folder;
 +	GPtrArray *selected;
 +	GString *buffer;
 +	const gchar *display_name;
 +	const gchar *folder_uri;
 +	gchar *folder_name;
 +	gchar *title;
 +	guint32 num_deleted;
 +	guint32 num_junked;
 +	guint32 num_junked_not_deleted;
 +	guint32 num_unread;
 +	guint32 num_visible;
 +
 +	g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
 +
 +	mail_shell_content = mail_shell_view->priv->mail_shell_content;
 +
 +	shell_view = E_SHELL_VIEW (mail_shell_view);
 +	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 +
 +	reader = E_MAIL_READER (mail_shell_content);
 +	message_list = e_mail_reader_get_message_list (reader);
 +	folder_uri = message_list->folder_uri;
 +	folder = message_list->folder;
 +
 +	local_store = e_mail_local_get_store ();
 +
 +	/* If no folder is selected, reset the sidebar banners
 +	 * to their default values and stop. */
 +	if (folder == NULL) {
 +		GtkAction *action;
 +		gchar *label;
 +
 +		action = e_shell_view_get_action (shell_view);
 +
 +		g_object_get (action, "label", &label, NULL);
 +		e_shell_sidebar_set_secondary_text (shell_sidebar, NULL);
 +		e_shell_view_set_title (shell_view, label);
 +		g_free (label);
 +
 +		return;
 +	}
 +
 +	camel_object_get (
 +		folder, NULL,
 +		CAMEL_FOLDER_NAME, &folder_name,
 +		CAMEL_FOLDER_DELETED, &num_deleted,
 +		CAMEL_FOLDER_JUNKED, &num_junked,
 +		CAMEL_FOLDER_JUNKED_NOT_DELETED, &num_junked_not_deleted,
 +		CAMEL_FOLDER_UNREAD, &num_unread,
 +		CAMEL_FOLDER_VISIBLE, &num_visible,
 +		NULL);
 +
 +	buffer = g_string_sized_new (256);
 +	selected = message_list_get_selected (message_list);
 +
 +	if (selected->len > 1)
 +		g_string_append_printf (
 +			buffer, ngettext ("%d selected, ", "%d selected, ",
 +			selected->len), selected->len);
 +
 +	if (CAMEL_IS_VTRASH_FOLDER (folder)) {
 +		CamelVTrashFolder *trash_folder;
 +
 +		trash_folder = (CamelVTrashFolder *) folder;
 +
 +		/* "Trash" folder */
 +		if (trash_folder->type == CAMEL_VTRASH_FOLDER_TRASH)
 +			g_string_append_printf (
 +				buffer, ngettext ("%d deleted",
 +				"%d deleted", num_deleted), num_deleted);
 +
 +		/* "Junk" folder (hide deleted messages) */
 +		else if (e_mail_reader_get_hide_deleted (reader))
 +			g_string_append_printf (
 +				buffer, ngettext ("%d junk",
 +				"%d junk", num_junked_not_deleted),
 +				num_junked_not_deleted);
 +
 +		/* "Junk" folder (show deleted messages) */
 +		else
 +			g_string_append_printf (
 +				buffer, ngettext ("%d junk", "%d junk",
 +				num_junked), num_junked);
 +
 +	/* "Drafts" folder */
 +	} else if (em_utils_folder_is_drafts (folder, folder_uri)) {
 +		g_string_append_printf (
 +			buffer, ngettext ("%d draft", "%d drafts",
 +			num_visible), num_visible);
 +
 +	/* "Outbox" folder */
 +	} else if (em_utils_folder_is_outbox (folder, folder_uri)) {
 +		g_string_append_printf (
 +			buffer, ngettext ("%d unsent", "%d unsent",
 +			num_visible), num_visible);
 +
 +	/* "Sent" folder */
 +	} else if (em_utils_folder_is_sent (folder, folder_uri)) {
 +		g_string_append_printf (
 +			buffer, ngettext ("%d sent", "%d sent",
 +			num_visible), num_visible);
 +
 +	/* Normal folder */
 +	} else {
 +		if (!e_mail_reader_get_hide_deleted (reader))
 +			num_visible +=
 +				num_deleted - num_junked +
 +				num_junked_not_deleted;
 +
 +		if (num_unread > 0 && selected->len <= 1)
 +			g_string_append_printf (
 +				buffer, ngettext ("%d unread, ",
 +				"%d unread, ", num_unread), num_unread);
 +		g_string_append_printf (
 +			buffer, ngettext ("%d total", "%d total",
 +			num_visible), num_visible);
 +	}
 +
 +	message_list_free_uids (message_list, selected);
 +
 +	/* Choose a suitable folder name for displaying. */
 +	if (folder->parent_store == local_store && (
 +		strcmp (folder_name, "Drafts") == 0 ||
 +		strcmp (folder_name, "Inbox") == 0 ||
 +		strcmp (folder_name, "Outbox") == 0 ||
 +		strcmp (folder_name, "Sent") == 0 ||
 +		strcmp (folder_name, "Templates") == 0))
 +		display_name = _(folder_name);
 +	else if (strcmp (folder_name, "INBOX") == 0)
 +		display_name = _("Inbox");
 +	else
 +		display_name = folder_name;
 +
 +	title = g_strdup_printf ("%s (%s)", display_name, buffer->str);
 +	e_shell_sidebar_set_secondary_text (shell_sidebar, buffer->str);
 +	e_shell_view_set_title (shell_view, title);
 +	g_free (title);
 +
 +	camel_object_free (folder, CAMEL_FOLDER_NAME, folder_name);
 +	g_string_free (buffer, TRUE);
 +}
diff --cc plugins/tnef-attachments/Makefile.am
index 262096e,d9447f2..2273e6e
--- a/plugins/tnef-attachments/Makefile.am
+++ b/plugins/tnef-attachments/Makefile.am
@@@ -1,6 -1,13 +1,14 @@@
+ if OS_WIN32
+ NO_UNDEFINED_REQUIRED_LIBS = 				\
+ 	$(EVOLUTION_MAIL_LIBS)				\
+ 	$(GNOME_PLATFORM_LIBS)				\
+ 	$(top_builddir)/e-util/libeutil.la		\
+ 	$(top_builddir)/mail/libevolution-mail.la
+ endif
+ 
  AM_CPPFLAGS =						\
  	-I$(top_srcdir)					\
 +	-I$(top_srcdir)/widgets				\
  	-DGETTEXT_PACKAGE="\"$(GETTEXT_PACKAGE)\""	\
  	-DLOCALEDIR="\"$(localedir)\""			\
  	$(EVOLUTION_MAIL_CFLAGS)			\
diff --cc widgets/misc/Makefile.am
index 69f9cf6,303efa8..ee11ea7
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@@ -1,6 -1,10 +1,10 @@@
+ if OS_WIN32
+ WIN32_BOOTSTRAP_LIBS = $(top_builddir)/win32/libfilter.la
+ endif
+ 
  AM_CPPFLAGS =								\
  	-I$(top_srcdir)							\
 -	-I$(top_srcdir)/a11y/widgets					\
 +	-I$(top_srcdir)/filter						\
  	-I$(top_srcdir)/widgets						\
  	-DEVOLUTION_IMAGES=\""$(imagesdir)"\"				\
  	-DEVOLUTION_GLADEDIR=\""$(gladedir)"\"				\
diff --cc widgets/misc/test-calendar.c
index 7b2d590,20b8773..ed670a8
--- a/widgets/misc/test-calendar.c
+++ b/widgets/misc/test-calendar.c
@@@ -118,11 -118,11 +118,11 @@@ on_date_range_changed (ECalendarItem *c
  		 start_day, start_month + 1, start_year,
  		 end_day, end_month + 1, end_year);
  
 -	/* These days should appear bold. Remember month is 0 to 11. */
 +	/* These days should windowear bold. Remember month is 0 to 11. */
  	e_calendar_item_mark_day (calitem, 2000, 7, 26, /* 26th Aug 2000. */
- 				  E_CALENDAR_ITEM_MARK_BOLD);
+ 				  E_CALENDAR_ITEM_MARK_BOLD, FALSE);
  	e_calendar_item_mark_day (calitem, 2000, 8, 13, /* 13th Sep 2000. */
- 				  E_CALENDAR_ITEM_MARK_BOLD);
+ 				  E_CALENDAR_ITEM_MARK_BOLD, FALSE);
  }
  
  



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]