[evolution/kill-bonobo: 63/65] Use key files for tracking widget states.



commit be8ee5393471a83b24aed4de1669afd723cb3168
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Jun 9 23:15:20 2009 -0400

    Use key files for tracking widget states.
    
    Each EShellView now maintains a GKeyFile for recording disposable widget
    state such as tree view path expansion, scroll bar positions, combo box
    selections, etc.  The EShellView records changes to the key file to
    ~/.evolution/<shell-backend>/config/state, and automatically restores
    the GKeyFile at startup.
    
    Currently only the mailer uses the key file, but it's intended to serve
    all shell views.  It replaces the use of Camel "cmeta" files, as well as
    "et-expanded-*" and "folder-tree-expand-state.xml" files.
    
    Also, the mailer's folder tree model now includes a column for tracking
    which sidebar folders are expanded.  Folder tree widgets appearing in
    dialog windows can copy the sidebar's expanded state using
    em_folder_tree_clone_expanded().

 .../gui/component/e-book-shell-view-actions.c      |    8 +-
 calendar/module/e-cal-shell-view-actions.c         |    8 +-
 calendar/module/e-memo-shell-view-actions.c        |    8 +-
 calendar/module/e-task-shell-view-actions.c        |    8 +-
 doc/reference/shell/tmpl/eshell-unused.sgml        |   16 +
 e-util/e-util.c                                    |   35 ++
 e-util/e-util.h                                    |    2 +
 mail/e-mail-reader.c                               |    4 +
 mail/e-mail-shell-content.c                        |  176 +++++---
 mail/e-mail-shell-sidebar.c                        |  344 +++++++++++++++-
 mail/e-mail-shell-view-actions.c                   |  129 +++++-
 mail/e-mail-shell-view-private.c                   |   73 ++--
 mail/e-mail-shell-view-private.h                   |    2 +
 mail/em-composer-utils.c                           |    9 +-
 mail/em-folder-selection-button.c                  |    1 +
 mail/em-folder-selection.c                         |   67 ++--
 mail/em-folder-selection.h                         |   24 +-
 mail/em-folder-tree-model.c                        |  445 +-------------------
 mail/em-folder-tree-model.h                        |   22 +-
 mail/em-folder-tree.c                              |  176 +++-----
 mail/em-folder-tree.h                              |    3 +-
 mail/em-folder-utils.c                             |   16 +-
 mail/em-folder-utils.h                             |    7 +-
 plugins/groupwise-features/install-shared.c        |    2 +
 plugins/groupwise-features/share-folder-common.c   |    2 +
 shell/e-shell-content.c                            |   79 ++++-
 shell/e-shell-content.h                            |    2 +
 shell/e-shell-view.c                               |  125 ++++++
 shell/e-shell-view.h                               |    2 +
 29 files changed, 1022 insertions(+), 773 deletions(-)
---
diff --git a/addressbook/gui/component/e-book-shell-view-actions.c b/addressbook/gui/component/e-book-shell-view-actions.c
index 73766c7..773eb3f 100644
--- a/addressbook/gui/component/e-book-shell-view-actions.c
+++ b/addressbook/gui/component/e-book-shell-view-actions.c
@@ -562,7 +562,13 @@ action_search_filter_cb (GtkRadioAction *action,
                          GtkRadioAction *current,
                          EBookShellView *book_shell_view)
 {
-	e_book_shell_view_execute_search (book_shell_view);
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+
+	shell_view = E_SHELL_VIEW (book_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+
+	gtk_action_activate (ACTION (SEARCH_EXECUTE));
 }
 
 static GtkActionEntry contact_entries[] = {
diff --git a/calendar/module/e-cal-shell-view-actions.c b/calendar/module/e-cal-shell-view-actions.c
index 5f5380d..02b7af6 100644
--- a/calendar/module/e-cal-shell-view-actions.c
+++ b/calendar/module/e-cal-shell-view-actions.c
@@ -599,7 +599,13 @@ action_search_filter_cb (GtkRadioAction *action,
                          GtkRadioAction *current,
                          ECalShellView *cal_shell_view)
 {
-	e_cal_shell_view_execute_search (cal_shell_view);
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+
+	shell_view = E_SHELL_VIEW (cal_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+
+	gtk_action_activate (ACTION (SEARCH_EXECUTE));
 }
 
 static GtkActionEntry calendar_entries[] = {
diff --git a/calendar/module/e-memo-shell-view-actions.c b/calendar/module/e-memo-shell-view-actions.c
index 855a7b1..1736606 100644
--- a/calendar/module/e-memo-shell-view-actions.c
+++ b/calendar/module/e-memo-shell-view-actions.c
@@ -548,7 +548,13 @@ action_search_filter_cb (GtkRadioAction *action,
                          GtkRadioAction *current,
                          EMemoShellView *memo_shell_view)
 {
-	e_memo_shell_view_execute_search (memo_shell_view);
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+
+	shell_view = E_SHELL_VIEW (memo_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+
+	gtk_action_activate (ACTION (SEARCH_EXECUTE));
 }
 
 static GtkActionEntry memo_entries[] = {
diff --git a/calendar/module/e-task-shell-view-actions.c b/calendar/module/e-task-shell-view-actions.c
index 8431b64..4deed9f 100644
--- a/calendar/module/e-task-shell-view-actions.c
+++ b/calendar/module/e-task-shell-view-actions.c
@@ -62,7 +62,13 @@ action_search_filter_cb (GtkRadioAction *action,
                          GtkRadioAction *current,
                          ETaskShellView *task_shell_view)
 {
-	e_task_shell_view_execute_search (task_shell_view);
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+
+	shell_view = E_SHELL_VIEW (task_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+
+	gtk_action_activate (ACTION (SEARCH_EXECUTE));
 }
 
 static void
diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml
index 72168ee..dcfb2af 100644
--- a/doc/reference/shell/tmpl/eshell-unused.sgml
+++ b/doc/reference/shell/tmpl/eshell-unused.sgml
@@ -525,6 +525,22 @@ intelligent
 @shutdown: 
 @migrate: 
 
+<!-- ##### SIGNAL EShellView::capture-state ##### -->
+<para>
+
+</para>
+
+ eshellview: the object which received the signal.
+ arg1: 
+
+<!-- ##### SIGNAL EShellView::restore-state ##### -->
+<para>
+
+</para>
+
+ eshellview: the object which received the signal.
+ arg1: 
+
 <!-- ##### ARG EShellView:shell-module ##### -->
 <para>
 
diff --git a/e-util/e-util.c b/e-util/e-util.c
index 0eaf8c3..55eb6eb 100644
--- a/e-util/e-util.c
+++ b/e-util/e-util.c
@@ -372,6 +372,41 @@ e_action_group_remove_all_actions (GtkActionGroup *action_group)
 }
 
 /**
+ * e_radio_action_get_current_action:
+ * @radio_action: a #GtkRadioAction
+ *
+ * Returns the currently active member of the group to which @radio_action
+ * belongs.
+ *
+ * Returns: the currently active group member
+ **/
+GtkRadioAction *
+e_radio_action_get_current_action (GtkRadioAction *radio_action)
+{
+	GSList *group;
+	gint current_value;
+
+	g_return_val_if_fail (GTK_IS_RADIO_ACTION (radio_action), NULL);
+
+	group = gtk_radio_action_get_group (radio_action);
+	current_value = gtk_radio_action_get_current_value (radio_action);
+
+	while (group != NULL) {
+		gint value;
+
+		radio_action = GTK_RADIO_ACTION (group->data);
+		g_object_get (radio_action, "value", &value, NULL);
+
+		if (value == current_value)
+			return radio_action;
+
+		group = g_slist_next (group);
+	}
+
+	return NULL;
+}
+
+/**
  * e_str_without_underscores:
  * @s: the string to strip underscores from.
  *
diff --git a/e-util/e-util.h b/e-util/e-util.h
index abc7635..33bc940 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -57,6 +57,8 @@ gint		e_action_compare_by_label	(GtkAction *action1,
 						 GtkAction *action2);
 void		e_action_group_remove_all_actions
 						(GtkActionGroup *action_group);
+GtkRadioAction *e_radio_action_get_current_action
+						(GtkRadioAction *radio_action);
 
 gchar *		e_str_without_underscores	(const gchar *s);
 gint		e_str_compare			(gconstpointer x,
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index edea7cf..94855ee 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -167,6 +167,8 @@ action_mail_copy_cb (GtkAction *action,
 
 	folder = message_list->folder;
 
+	em_folder_tree_clone_expanded (folder_tree);
+
 	em_folder_tree_set_excluded (
 		EM_FOLDER_TREE (folder_tree),
 		EMFT_EXCLUDE_NOSELECT | EMFT_EXCLUDE_VIRTUAL |
@@ -577,6 +579,8 @@ action_mail_move_cb (GtkAction *action,
 
 	folder = message_list->folder;
 
+	em_folder_tree_clone_expanded (folder_tree);
+
 	em_folder_tree_set_excluded (
 		EM_FOLDER_TREE (folder_tree),
 		EMFT_EXCLUDE_NOSELECT | EMFT_EXCLUDE_VIRTUAL |
diff --git a/mail/e-mail-shell-content.c b/mail/e-mail-shell-content.c
index 203876f..abb7e8f 100644
--- a/mail/e-mail-shell-content.c
+++ b/mail/e-mail-shell-content.c
@@ -44,6 +44,9 @@
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentPrivate))
 
+#define STATE_KEY_SCROLLBAR_POSITION	"ScrollbarPosition"
+#define STATE_KEY_SELECTED_MESSAGE	"SelectedMessage"
+
 struct _EMailShellContentPrivate {
 	GtkWidget *paned;
 	GtkWidget *message_list;
@@ -52,8 +55,6 @@ struct _EMailShellContentPrivate {
 	EMFormatHTMLDisplay *html_display;
 	GalViewInstance *view_instance;
 
-	gchar *selected_uid;
-
 	/* ETable scrolling hack */
 	gdouble default_scrollbar_position;
 
@@ -97,52 +98,78 @@ static void
 mail_shell_content_message_list_scrolled_cb (EMailShellContent *mail_shell_content,
                                              MessageList *message_list)
 {
+	EShellContent *shell_content;
+	EShellView *shell_view;
+	GKeyFile *key_file;
+	const gchar *folder_uri;
 	const gchar *key;
+	gchar *group_name;
 	gdouble position;
-	gchar *value;
 
 	/* Save the scrollbar position for the current folder. */
 
-	if (message_list->folder == NULL)
+	folder_uri = message_list->folder_uri;
+
+	if (folder_uri == NULL)
 		return;
 
-	key = "evolution:list_scroll_position";
+	shell_content = E_SHELL_CONTENT (mail_shell_content);
+	shell_view = e_shell_content_get_shell_view (shell_content);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	key = STATE_KEY_SCROLLBAR_POSITION;
+	group_name = g_strdup_printf ("Folder %s", folder_uri);
 	position = message_list_get_scrollbar_position (message_list);
-	value = g_strdup_printf ("%f", position);
 
-	if (camel_object_meta_set (message_list->folder, key, value))
-		camel_object_state_write (message_list->folder);
+	g_key_file_set_double (key_file, group_name, key, position);
+	e_shell_view_set_state_dirty (shell_view);
 
-	g_free (value);
+	g_free (group_name);
 }
 
 static gboolean
 mail_shell_content_scroll_timeout_cb (EMailShellContent *mail_shell_content)
 {
 	EMailShellContentPrivate *priv = mail_shell_content->priv;
+	EShellContent *shell_content;
+	EShellView *shell_view;
 	MessageList *message_list;
 	EMailReader *reader;
+	GKeyFile *key_file;
+	const gchar *folder_uri;
 	const gchar *key;
-	gdouble position;
-	gchar *value;
+	gchar *group_name;
 
 	/* Initialize the scrollbar position for the current folder
 	 * and setup a callback to handle scrollbar position changes. */
 
+	shell_content = E_SHELL_CONTENT (mail_shell_content);
+	shell_view = e_shell_content_get_shell_view (shell_content);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
 	reader = E_MAIL_READER (mail_shell_content);
 	message_list = e_mail_reader_get_message_list (reader);
-	position = priv->default_scrollbar_position;
+	folder_uri = message_list->folder_uri;
+
+	if (folder_uri == NULL)
+		goto skip;
+
+	/* Restore the message list scrollbar position. */
 
-	key = "evolution:list_scroll_position";
-	value = camel_object_meta_get (message_list->folder, key);
+	key = STATE_KEY_SCROLLBAR_POSITION;
+	group_name = g_strdup_printf ("Folder %s", folder_uri);
 
-	if (value != NULL) {
-		position = strtod (value, NULL);
-		g_free (value);
+	if (g_key_file_has_key (key_file, group_name, key, NULL)) {
+		gdouble position;
+
+		position = g_key_file_get_double (
+			key_file, group_name, key, NULL);
+		message_list_set_scrollbar_position (message_list, position);
 	}
 
-	message_list_set_scrollbar_position (message_list, position);
+	g_free (group_name);
 
+skip:
 	priv->message_list_scrolled_id = g_signal_connect_swapped (
 		message_list, "message-list-scrolled",
 		G_CALLBACK (mail_shell_content_message_list_scrolled_cb),
@@ -158,37 +185,59 @@ mail_shell_content_message_list_built_cb (EMailShellContent *mail_shell_content,
                                           MessageList *message_list)
 {
 	EMailShellContentPrivate *priv = mail_shell_content->priv;
+	EShellContent *shell_content;
+	EShellView *shell_view;
 	GtkScrolledWindow *scrolled_window;
 	GtkWidget *vscrollbar;
-	gdouble position = 0.0;
+	GKeyFile *key_file;
+	gchar *uid;
 
 	g_signal_handler_disconnect (
 		message_list, priv->message_list_built_id);
 	priv->message_list_built_id = 0;
 
-	if (message_list->cursor_uid == NULL && priv->selected_uid != NULL) {
+	shell_content = E_SHELL_CONTENT (mail_shell_content);
+	shell_view = e_shell_content_get_shell_view (shell_content);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	if (message_list->cursor_uid != NULL)
+		uid = NULL;
+
+	else if (message_list->folder_uri == NULL)
+		uid = NULL;
+
+	else if (mail_shell_content->priv->suppress_message_selection)
+		uid = NULL;
+
+	else {
+		const gchar *folder_uri;
+		const gchar *key;
+		gchar *group_name;
+
+		key = STATE_KEY_SELECTED_MESSAGE;
+		folder_uri = message_list->folder_uri;
+		group_name = g_strdup_printf ("Folder %s", folder_uri);
+		uid = g_key_file_get_string (key_file, group_name, key, NULL);
+		g_free (group_name);
+	}
+
+	if (uid != NULL) {
+		CamelFolder *folder;
 		CamelMessageInfo *info;
 
-		/* If the message isn't in the folder yet, keep selected_uid
-		 * around, as it could be caught by a set_folder() at some
-		 * later date. */
-		info = camel_folder_get_message_info (
-			message_list->folder, priv->selected_uid);
+		folder = message_list->folder;
+		info = camel_folder_get_message_info (folder, uid);
 		if (info != NULL) {
-			camel_folder_free_message_info (
-				message_list->folder, info);
-			e_mail_reader_set_message (
-				E_MAIL_READER (mail_shell_content),
-				priv->selected_uid, TRUE);
-			g_free (priv->selected_uid);
-			priv->selected_uid = NULL;
+			EMailReader *reader;
+
+			reader = E_MAIL_READER (mail_shell_content);
+			e_mail_reader_set_message (reader, uid, TRUE);
+			camel_folder_free_message_info (folder, info);
 		}
 
-		position = message_list_get_scrollbar_position (message_list);
+		g_free (uid);
 	}
 
-	priv->default_scrollbar_position = position;
-
 	/* FIXME This is a gross workaround for an ETable bug that I can't
 	 *       fix (Ximian bug #55303).
 	 *
@@ -227,25 +276,38 @@ mail_shell_content_display_view_cb (EMailShellContent *mail_shell_content,
 
 static void
 mail_shell_content_message_selected_cb (EMailShellContent *mail_shell_content,
-                                        const gchar *selected_uid,
+                                        const gchar *message_uid,
                                         MessageList *message_list)
 {
-	const gchar *key = "evolution:selected_uid";
-	CamelFolder *folder;
+	EShellContent *shell_content;
+	EShellView *shell_view;
+	GKeyFile *key_file;
+	const gchar *folder_uri;
+	const gchar *key;
+	gchar *group_name;
 
-	folder = message_list->folder;
+	folder_uri = message_list->folder_uri;
 
 	/* This also gets triggered when selecting a store name on
 	 * the sidebar such as "On This Computer", in which case
-	 * 'folder' will be NULL. */
-	if (folder == NULL)
+	 * 'folder_uri' will be NULL. */
+	if (folder_uri == NULL)
 		return;
 
-	if (camel_object_meta_set (folder, key, selected_uid))
-		camel_object_state_write (folder);
+	shell_content = E_SHELL_CONTENT (mail_shell_content);
+	shell_view = e_shell_content_get_shell_view (shell_content);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	key = STATE_KEY_SELECTED_MESSAGE;
+	group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+	if (message_uid != NULL)
+		g_key_file_set_string (key_file, group_name, key, message_uid);
+	else
+		g_key_file_remove_key (key_file, group_name, key, NULL);
+	e_shell_view_set_state_dirty (shell_view);
 
-	g_free (mail_shell_content->priv->selected_uid);
-	mail_shell_content->priv->selected_uid = NULL;
+	g_free (group_name);
 }
 
 static void
@@ -345,19 +407,6 @@ mail_shell_content_dispose (GObject *object)
 }
 
 static void
-mail_shell_content_finalize (GObject *object)
-{
-	EMailShellContentPrivate *priv;
-
-	priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object);
-
-	g_free (priv->selected_uid);
-
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
 mail_shell_content_constructed (GObject *object)
 {
 	EMailShellContentPrivate *priv;
@@ -543,7 +592,6 @@ mail_shell_content_set_folder (EMailReader *reader,
 	EMailReaderIface *default_iface;
 	MessageList *message_list;
 	gboolean different_folder;
-	gchar *meta_data;
 
 	priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader);
 
@@ -570,15 +618,6 @@ mail_shell_content_set_folder (EMailReader *reader,
 	if (different_folder)
 		priv->suppress_message_selection = FALSE;
 
-	if (!priv->suppress_message_selection)
-		meta_data = camel_object_meta_get (
-			folder, "evolution:selected_uid");
-	else
-		meta_data = NULL;
-
-	g_free (priv->selected_uid);
-	priv->selected_uid = meta_data;
-
 	/* This is a one-time-only callback. */
 	if (message_list->cursor_uid == NULL && priv->message_list_built_id == 0)
 		priv->message_list_built_id = g_signal_connect_swapped (
@@ -613,7 +652,6 @@ mail_shell_content_class_init (EMailShellContentClass *class)
 	object_class->set_property = mail_shell_content_set_property;
 	object_class->get_property = mail_shell_content_get_property;
 	object_class->dispose = mail_shell_content_dispose;
-	object_class->finalize = mail_shell_content_finalize;
 	object_class->constructed = mail_shell_content_constructed;
 
 	shell_content_class = E_SHELL_CONTENT_CLASS (class);
diff --git a/mail/e-mail-shell-sidebar.c b/mail/e-mail-shell-sidebar.c
index 1b090fa..10f0062 100644
--- a/mail/e-mail-shell-sidebar.c
+++ b/mail/e-mail-shell-sidebar.c
@@ -33,6 +33,8 @@
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarPrivate))
 
+#define STATE_KEY_EXPANDED	"Expanded"
+
 struct _EMailShellSidebarPrivate {
 	GtkWidget *folder_tree;
 };
@@ -46,6 +48,301 @@ static gpointer parent_class;
 static GType mail_shell_sidebar_type;
 
 static void
+mail_shell_sidebar_restore_state (EMailShellSidebar *mail_shell_sidebar)
+{
+	EShellView *shell_view;
+	EShellSidebar *shell_sidebar;
+	EMFolderTreeModel *folder_tree_model;
+	EMFolderTree *folder_tree;
+	GtkTreeModel *tree_model;
+	GtkTreeView *tree_view;
+	GtkTreeIter iter;
+	GHashTable *hash_table;
+	GKeyFile *key_file;
+	gboolean valid;
+	gchar *selected;
+	gchar **groups;
+	gint ii;
+
+	shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar);
+	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+	folder_tree_model = em_folder_tree_get_model (folder_tree);
+	hash_table = folder_tree_model->uri_hash;
+
+	tree_view = GTK_TREE_VIEW (folder_tree);
+	tree_model = GTK_TREE_MODEL (folder_tree_model);
+
+	/* Restore selected folder. */
+
+	selected = g_key_file_get_string (
+		key_file, "Folder Tree", "Selected", NULL);
+	if (selected != NULL) {
+		em_folder_tree_set_selected (folder_tree, selected, FALSE);
+		g_free (selected);
+	}
+
+	/* Set the initial folder tree expanded state in two stages:
+	 *
+	 * 1) Iterate over the "Store" and "Folder" state file groups
+	 *    and apply the "Expanded" keys where possible.
+	 *
+	 * 2) Iterate over the top-level nodes in the folder tree
+	 *    (these are all stores) and expand those that have no
+	 *    corresponding "Expanded" key in the state file.  This
+	 *    ensures that new stores are expanded by default.
+	 *
+	 * The expanded state is recorded and maintained in the tree model
+	 * so that folder trees in other contexts can duplicate it using
+	 * em_folder_tree_clone_expanded().
+	 */
+
+	/* Stage 1 */
+
+	groups = g_key_file_get_groups (key_file, NULL);
+
+	for (ii = 0; groups[ii] != NULL; ii++) {
+		GtkTreeRowReference *reference;
+		GtkTreePath *path;
+		GtkTreeIter iter;
+		const gchar *group_name = groups[ii];
+		const gchar *key = STATE_KEY_EXPANDED;
+		const gchar *uri;
+		gboolean expanded;
+
+		if (g_str_has_prefix (group_name, "Store ")) {
+			uri = group_name + 6;
+			expanded = TRUE;
+		} else if (g_str_has_prefix (group_name, "Folder ")) {
+			uri = group_name + 7;
+			expanded = FALSE;
+		} else
+			continue;
+
+		if (g_key_file_has_key (key_file, group_name, key, NULL))
+			expanded = g_key_file_get_boolean (
+				key_file, group_name, key, NULL);
+
+		if (!expanded)
+			continue;
+
+		reference = g_hash_table_lookup (hash_table, uri);
+		if (reference == NULL)
+			continue;
+
+		path = gtk_tree_row_reference_get_path (reference);
+		gtk_tree_model_get_iter (tree_model, &iter, path);
+		gtk_tree_view_expand_row (tree_view, path, FALSE);
+		gtk_tree_path_free (path);
+
+		/* The expanded column is used to clone the sidebar's
+		 * expanded state in other EMFolderTree instances. */
+		gtk_tree_store_set (
+			GTK_TREE_STORE (tree_model), &iter,
+			COL_BOOL_EXPANDED, TRUE, -1);
+	}
+
+	g_strfreev (groups);
+
+	/* Stage 2 */
+
+	valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+	while (valid) {
+		const gchar *key = STATE_KEY_EXPANDED;
+		gchar *group_name;
+		gchar *uri;
+
+		gtk_tree_model_get (
+			tree_model, &iter, COL_STRING_URI, &uri, -1);
+
+		if (uri == NULL)
+			goto next;
+
+		group_name = g_strdup_printf ("Store %s", uri);
+
+		if (!g_key_file_has_key (key_file, group_name, key, NULL)) {
+			GtkTreePath *path;
+
+			path = gtk_tree_model_get_path (tree_model, &iter);
+			gtk_tree_view_expand_row (tree_view, path, FALSE);
+			gtk_tree_path_free (path);
+
+			/* The expanded column is used to clone the sidebar's
+			 * expanded state in other EMFolderTree instances. */
+			gtk_tree_store_set (
+				GTK_TREE_STORE (tree_model), &iter,
+				COL_BOOL_EXPANDED, TRUE, -1);
+		}
+
+		g_free (group_name);
+		g_free (uri);
+
+	next:
+		valid = gtk_tree_model_iter_next (tree_model, &iter);
+	}
+}
+
+static void
+mail_shell_sidebar_row_collapsed_cb (EShellSidebar *shell_sidebar,
+                                     GtkTreeIter *iter,
+                                     GtkTreePath *path,
+                                     GtkTreeView *tree_view)
+{
+	EShellView *shell_view;
+	GtkTreeModel *model;
+	GKeyFile *key_file;
+	const gchar *key;
+	gboolean is_folder;
+	gboolean is_store;
+	gchar *group_name;
+	gchar *uri;
+
+	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	model = gtk_tree_view_get_model (tree_view);
+
+	gtk_tree_model_get (
+		model, iter,
+		COL_STRING_URI, &uri,
+		COL_BOOL_IS_STORE, &is_store,
+		COL_BOOL_IS_FOLDER, &is_folder, -1);
+
+	g_return_if_fail (is_store || is_folder);
+
+	/* The expanded column is used to clone the sidebar's
+	 * expanded state in other EMFolderTree instances. */
+	gtk_tree_store_set (
+		GTK_TREE_STORE (model), iter,
+		COL_BOOL_EXPANDED, FALSE, -1);
+
+	key = STATE_KEY_EXPANDED;
+	if (is_store)
+		group_name = g_strdup_printf ("Store %s", uri);
+	else
+		group_name = g_strdup_printf ("Folder %s", uri);
+
+	g_key_file_set_boolean (key_file, group_name, key, FALSE);
+	e_shell_view_set_state_dirty (shell_view);
+
+	g_free (group_name);
+	g_free (uri);
+}
+
+static void
+mail_shell_sidebar_row_expanded_cb (EShellSidebar *shell_sidebar,
+                                    GtkTreeIter *unused,
+                                    GtkTreePath *path,
+                                    GtkTreeView *tree_view)
+{
+	EShellView *shell_view;
+	GtkTreeModel *model;
+	GKeyFile *key_file;
+	const gchar *key;
+	gboolean is_folder;
+	gboolean is_store;
+	gchar *group_name;
+	gchar *uri;
+
+	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	path = gtk_tree_path_copy (path);
+	model = gtk_tree_view_get_model (tree_view);
+
+	/* Expand the node and all ancestors. */
+	while (gtk_tree_path_get_depth (path) > 0) {
+		GtkTreeIter iter;
+
+		gtk_tree_model_get_iter (model, &iter, path);
+
+		gtk_tree_model_get (
+			model, &iter,
+			COL_STRING_URI, &uri,
+			COL_BOOL_IS_STORE, &is_store,
+			COL_BOOL_IS_FOLDER, &is_folder, -1);
+
+		g_return_if_fail (is_store || is_folder);
+
+		/* The expanded column is used to clone the sidebar's
+		 * expanded state in other EMFolderTree instances. */
+		gtk_tree_store_set (
+			GTK_TREE_STORE (model), &iter,
+			COL_BOOL_EXPANDED, TRUE, -1);
+
+		key = STATE_KEY_EXPANDED;
+		if (is_store)
+			group_name = g_strdup_printf ("Store %s", uri);
+		else
+			group_name = g_strdup_printf ("Folder %s", uri);
+
+		g_key_file_set_boolean (key_file, group_name, key, TRUE);
+		e_shell_view_set_state_dirty (shell_view);
+
+		g_free (group_name);
+		g_free (uri);
+
+		gtk_tree_path_up (path);
+	}
+
+	gtk_tree_path_free (path);
+}
+
+static void
+mail_shell_sidebar_model_loaded_row_cb (EMailShellSidebar *mail_shell_sidebar,
+                                        GtkTreePath *path,
+                                        GtkTreeIter *iter,
+                                        GtkTreeModel *model)
+{
+	EShellSidebar *shell_sidebar;
+	EShellView *shell_view;
+	GtkTreeView *tree_view;
+	GKeyFile *key_file;
+	gboolean is_folder;
+	gboolean is_store;
+	const gchar *key;
+	gchar *group_name;
+	gchar *uri;
+	gboolean expanded;
+
+	shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar);
+	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree);
+
+	gtk_tree_model_get (
+		model, iter,
+		COL_STRING_URI, &uri,
+		COL_BOOL_IS_STORE, &is_store,
+		COL_BOOL_IS_FOLDER, &is_folder, -1);
+
+	g_return_if_fail (is_store || is_folder);
+
+	key = STATE_KEY_EXPANDED;
+	if (is_store) {
+		group_name = g_strdup_printf ("Store %s", uri);
+		expanded = TRUE;
+	} else {
+		group_name = g_strdup_printf ("Folder %s", uri);
+		expanded = FALSE;
+	}
+
+	if (g_key_file_has_key (key_file, group_name, key, NULL))
+		expanded = g_key_file_get_boolean (
+			key_file, group_name, key, NULL);
+
+	if (expanded)
+		gtk_tree_view_expand_row (tree_view, path, FALSE);
+
+	g_free (group_name);
+	g_free (uri);
+}
+
+static void
 mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar,
                                          GtkTreeSelection *selection)
 {
@@ -53,21 +350,34 @@ mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar,
 	EShellViewClass *shell_view_class;
 	GtkTreeModel *model;
 	GtkTreeIter iter;
+	GKeyFile *key_file;
 	const gchar *icon_name;
 	gchar *display_name = NULL;
+	gchar *uri = NULL;
 	gboolean is_folder = FALSE;
 	guint flags = 0;
 
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 	shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
+	key_file = e_shell_view_get_state_key_file (shell_view);
 
 	if (gtk_tree_selection_get_selected (selection, &model, &iter))
 		gtk_tree_model_get (
 			model, &iter,
 			COL_STRING_DISPLAY_NAME, &display_name,
+			COL_STRING_URI, &uri,
 			COL_BOOL_IS_FOLDER, &is_folder,
 			COL_UINT_FLAGS, &flags, -1);
 
+	if (uri != NULL)
+		g_key_file_set_string (
+			key_file, "Folder Tree", "Selected", uri);
+	else
+		g_key_file_remove_key (
+			key_file, "Folder Tree", "Selected", NULL);
+
+	e_shell_view_set_state_dirty (shell_view);
+
 	if (is_folder)
 		icon_name = em_folder_utils_get_icon_name (flags);
 	else {
@@ -79,6 +389,7 @@ mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar,
 	e_shell_sidebar_set_primary_text (shell_sidebar, display_name);
 
 	g_free (display_name);
+	g_free (uri);
 }
 
 static void
@@ -128,8 +439,9 @@ mail_shell_sidebar_finalize (GObject *object)
 static void
 mail_shell_sidebar_constructed (GObject *object)
 {
-	EMailShellSidebarPrivate *priv;
 	EMailShellBackend *mail_shell_backend;
+	EMailShellSidebar *mail_shell_sidebar;
+	EMFolderTreeModel *folder_tree_model;
 	EShellSidebar *shell_sidebar;
 	EShellBackend *shell_backend;
 	EShellView *shell_view;
@@ -138,15 +450,18 @@ mail_shell_sidebar_constructed (GObject *object)
 	GtkWidget *container;
 	GtkWidget *widget;
 
-	priv = E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (object);
-
 	/* Chain up to parent's constructed method. */
 	G_OBJECT_CLASS (parent_class)->constructed (object);
 
 	shell_sidebar = E_SHELL_SIDEBAR (object);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 	shell_backend = e_shell_view_get_shell_backend (shell_view);
+
 	mail_shell_backend = E_MAIL_SHELL_BACKEND (shell_backend);
+	mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (object);
+
+	folder_tree_model = e_mail_shell_backend_get_folder_tree_model (
+		mail_shell_backend);
 
 	/* Build sidebar widgets. */
 
@@ -163,16 +478,33 @@ mail_shell_sidebar_constructed (GObject *object)
 
 	container = widget;
 
-	widget = em_folder_tree_new (mail_shell_backend);
+	widget = em_folder_tree_new_with_model (folder_tree_model);
 	em_folder_tree_set_excluded (EM_FOLDER_TREE (widget), 0);
 	em_folder_tree_enable_drag_and_drop (EM_FOLDER_TREE (widget));
 	gtk_container_add (GTK_CONTAINER (container), widget);
-	priv->folder_tree = g_object_ref (widget);
+	mail_shell_sidebar->priv->folder_tree = g_object_ref (widget);
 	gtk_widget_show (widget);
 
-	tree_view = GTK_TREE_VIEW (priv->folder_tree);
+	tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree);
 	selection = gtk_tree_view_get_selection (tree_view);
 
+	mail_shell_sidebar_restore_state (mail_shell_sidebar);
+
+	g_signal_connect_swapped (
+		tree_view, "row-collapsed",
+		G_CALLBACK (mail_shell_sidebar_row_collapsed_cb),
+		shell_sidebar);
+
+	g_signal_connect_swapped (
+		tree_view, "row-expanded",
+		G_CALLBACK (mail_shell_sidebar_row_expanded_cb),
+		shell_sidebar);
+
+	g_signal_connect_swapped (
+		folder_tree_model, "loaded-row",
+		G_CALLBACK (mail_shell_sidebar_model_loaded_row_cb),
+		shell_sidebar);
+
 	g_signal_connect_swapped (
 		selection, "changed",
 		G_CALLBACK (mail_shell_sidebar_selection_changed_cb),
diff --git a/mail/e-mail-shell-view-actions.c b/mail/e-mail-shell-view-actions.c
index 6e4e7fd..e9cba33 100644
--- a/mail/e-mail-shell-view-actions.c
+++ b/mail/e-mail-shell-view-actions.c
@@ -21,6 +21,10 @@
 
 #include "e-mail-shell-view-private.h"
 
+#define STATE_KEY_SEARCH_FILTER		"SearchFilter"
+#define STATE_KEY_SEARCH_SCOPE		"SearchScope"
+#define STATE_KEY_SEARCH_TEXT		"SearchText"
+
 static void
 action_gal_save_custom_view_cb (GtkAction *action,
                                 EMailShellView *mail_shell_view)
@@ -135,6 +139,7 @@ action_mail_folder_copy_cb (GtkAction *action,
 	EMailShellSidebar *mail_shell_sidebar;
 	CamelFolderInfo *folder_info;
 	EMFolderTree *folder_tree;
+	EMFolderTreeModel *model;
 
 	mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
 	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
@@ -142,7 +147,8 @@ action_mail_folder_copy_cb (GtkAction *action,
 	g_return_if_fail (folder_info != NULL);
 
 	/* XXX Leaking folder_info? */
-	em_folder_utils_copy_folder (folder_info, FALSE);
+	model = em_folder_tree_get_model (folder_tree);
+	em_folder_utils_copy_folder (model, folder_info, FALSE);
 }
 
 static void
@@ -230,6 +236,7 @@ action_mail_folder_move_cb (GtkAction *action,
 	EMailShellSidebar *mail_shell_sidebar;
 	CamelFolderInfo *folder_info;
 	EMFolderTree *folder_tree;
+	EMFolderTreeModel *model;
 
 	mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar;
 	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
@@ -237,7 +244,8 @@ action_mail_folder_move_cb (GtkAction *action,
 	g_return_if_fail (folder_info != NULL);
 
 	/* XXX Leaking folder_info? */
-	em_folder_utils_copy_folder (folder_info, TRUE);
+	model = em_folder_tree_get_model (folder_tree);
+	em_folder_utils_copy_folder (model, folder_info, TRUE);
 }
 
 static void
@@ -600,19 +608,6 @@ action_mail_label_none_cb (GtkAction *action,
 }
 
 static void
-action_mail_preview_cb (GtkToggleAction *action,
-                        EMailShellView *mail_shell_view)
-{
-	EMailShellContent *mail_shell_content;
-	gboolean active;
-
-	mail_shell_content = mail_shell_view->priv->mail_shell_content;
-	active = gtk_toggle_action_get_active (action);
-
-	e_mail_shell_content_set_preview_visible (mail_shell_content, active);
-}
-
-static void
 action_mail_search_cb (GtkRadioAction *action,
                        GtkRadioAction *current,
                        EMailShellView *mail_shell_view)
@@ -883,14 +878,46 @@ action_search_execute_cb (GtkAction *action,
                           EMailShellView *mail_shell_view)
 {
 	EShellView *shell_view;
+	EShellContent *shell_content;
+	EMailReader *reader;
+	MessageList *message_list;
+	GKeyFile *key_file;
+	const gchar *folder_uri;
+
+	shell_view = E_SHELL_VIEW (mail_shell_view);
+	shell_content = e_shell_view_get_shell_content (shell_view);
+	key_file = e_shell_view_get_state_key_file (shell_view);
 
 	/* All shell views respond to the activation of this action,
 	 * which is defined by EShellWindow.  But only the currently
 	 * active shell view proceeds with executing the search. */
-	shell_view = E_SHELL_VIEW (mail_shell_view);
 	if (!e_shell_view_is_active (shell_view))
 		return;
 
+	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;
+
+	if (folder_uri != NULL) {
+		const gchar *key;
+		const gchar *string;
+		gchar *group_name;
+
+		key = STATE_KEY_SEARCH_TEXT;
+		string = e_shell_content_get_search_text (shell_content);
+		group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+		if (string != NULL && *string != '\0')
+			g_key_file_set_string (
+				key_file, group_name, key, string);
+		else
+			g_key_file_remove_key (
+				key_file, group_name, key, NULL);
+		e_shell_view_set_state_dirty (shell_view);
+
+		g_free (group_name);
+	}
+
 	e_mail_shell_view_execute_search (mail_shell_view);
 }
 
@@ -899,7 +926,37 @@ action_search_filter_cb (GtkRadioAction *action,
                          GtkRadioAction *current,
                          EMailShellView *mail_shell_view)
 {
-	e_mail_shell_view_execute_search (mail_shell_view);
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+	EMailReader *reader;
+	MessageList *message_list;
+	GKeyFile *key_file;
+	const gchar *folder_uri;
+
+	shell_view = E_SHELL_VIEW (mail_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	key_file = e_shell_view_get_state_key_file (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;
+
+	if (folder_uri != NULL) {
+		const gchar *key;
+		const gchar *string;
+		gchar *group_name;
+
+		key = STATE_KEY_SEARCH_FILTER;
+		string = gtk_action_get_name (GTK_ACTION (current));
+		group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+		g_key_file_set_string (key_file, group_name, key, string);
+		e_shell_view_set_state_dirty (shell_view);
+
+		g_free (group_name);
+	}
+
+	gtk_action_activate (ACTION (SEARCH_EXECUTE));
 }
 
 static void
@@ -907,7 +964,37 @@ action_search_scope_cb (GtkRadioAction *action,
                         GtkRadioAction *current,
                         EMailShellView *mail_shell_view)
 {
-	e_mail_shell_view_execute_search (mail_shell_view);
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+	EMailReader *reader;
+	MessageList *message_list;
+	GKeyFile *key_file;
+	const gchar *folder_uri;
+
+	shell_view = E_SHELL_VIEW (mail_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	key_file = e_shell_view_get_state_key_file (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;
+
+	if (folder_uri != NULL) {
+		const gchar *key;
+		const gchar *string;
+		gchar *group_name;
+
+		key = STATE_KEY_SEARCH_SCOPE;
+		string = gtk_action_get_name (GTK_ACTION (current));
+		group_name = g_strdup_printf ("Folder %s", folder_uri);
+
+		g_key_file_set_string (key_file, group_name, key, string);
+		e_shell_view_set_state_dirty (shell_view);
+
+		g_free (group_name);
+	}
+
+	gtk_action_activate (ACTION (SEARCH_EXECUTE));
 }
 
 static GtkActionEntry mail_entries[] = {
@@ -1205,7 +1292,7 @@ static GtkToggleActionEntry mail_toggle_entries[] = {
 	  N_("Show Message _Preview"),
 	  "<Control>m",
 	  N_("Show message preview pane"),
-	  G_CALLBACK (action_mail_preview_cb),
+	  NULL,  /* Handled by property bindings */
 	  TRUE },
 
 	{ "mail-threads-group-by",
@@ -1468,6 +1555,10 @@ e_mail_shell_view_actions_init (EMailShellView *mail_shell_view)
 	dst_object = G_OBJECT (ACTION (MAIL_THREADS_EXPAND_ALL));
 	e_binding_new (src_object, "active", dst_object, "sensitive");
 
+	e_mutual_binding_new (
+		G_OBJECT (ACTION (MAIL_PREVIEW)), "active",
+		G_OBJECT (shell_content), "preview-visible");
+
 	/* XXX The boolean sense of the GConf key is the inverse of
 	 *     the menu item, so we have to maintain two properties. */
 	e_mutual_binding_new_with_negation (
diff --git a/mail/e-mail-shell-view-private.c b/mail/e-mail-shell-view-private.c
index 9026c8e..d4fd3c7 100644
--- a/mail/e-mail-shell-view-private.c
+++ b/mail/e-mail-shell-view-private.c
@@ -30,27 +30,23 @@ mail_shell_view_folder_tree_selected_cb (EMailShellView *mail_shell_view,
                                          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) {
-		EMFolderTreeModel *model;
-
-		model = em_folder_tree_get_model (folder_tree);
-		em_folder_tree_model_set_selected (model, uri);
-		em_folder_tree_model_save_state (model);
-
+	if (folder_selected)
 		e_mail_reader_set_folder_uri (reader, uri);
-	} else
+	else
 		e_mail_reader_set_folder (reader, NULL, NULL);
 
-	e_shell_view_update_actions (E_SHELL_VIEW (mail_shell_view));
+	e_shell_view_update_actions (shell_view);
 }
 
 static void
@@ -122,7 +118,8 @@ mail_shell_view_message_list_right_click_cb (EShellView *shell_view,
 }
 
 static void
-mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view)
+mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
+                                   EMailReader *reader)
 {
 	EMailShellContent *mail_shell_content;
 
@@ -218,7 +215,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	EShellSidebar *shell_sidebar;
 	EShellWindow *shell_window;
 	EMFormatHTMLDisplay *html_display;
-	EMFolderTreeModel *folder_tree_model;
 	EMFolderTree *folder_tree;
 	RuleContext *context;
 	FilterRule *rule = NULL;
@@ -229,7 +225,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	GtkHTML *html;
 	const gchar *source;
 	guint merge_id;
-	gchar *uri;
 	gint ii = 0;
 
 	shell_view = E_SHELL_VIEW (mail_shell_view);
@@ -298,6 +293,11 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 		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);
@@ -336,24 +336,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 		priv->search_rules[ii++] = g_object_ref (rule);
 	}
 	g_assert (ii == MAIL_NUM_SEARCH_RULES);
-
-	/* Restore the previously selected folder. */
-	folder_tree_model = em_folder_tree_get_model (folder_tree);
-	uri = em_folder_tree_model_get_selected (folder_tree_model);
-	if (uri != NULL) {
-		gboolean expanded;
-
-		expanded = em_folder_tree_model_get_expanded_uri (
-			folder_tree_model, uri);
-		em_folder_tree_set_selected (folder_tree, uri, FALSE);
-		e_mail_reader_set_folder_uri (reader, uri);
-
-		if (!expanded)
-			em_folder_tree_model_set_expanded_uri (
-				folder_tree_model, uri, expanded);
-
-		g_free (uri);
-	}
 }
 
 void
@@ -377,6 +359,33 @@ e_mail_shell_view_private_finalize (EMailShellView *mail_shell_view)
 }
 
 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;
@@ -538,7 +547,7 @@ filter:
 				temp = g_strdup_printf (
 					"(and %s (match-all "
 					"(> (get-received-date) "
-					"(- (get_current_date) 86400))))",
+					"(- (get-current-date) 86400))))",
 					query);
 			g_free (query);
 			query = temp;
@@ -555,7 +564,7 @@ filter:
 				temp = g_strdup_printf (
 					"(and %s (match-all "
 					"(> (get-received-date) "
-					"(- (get_current_date) 432000))))",
+					"(- (get-current-date) 432000))))",
 					query);
 			g_free (query);
 			query = temp;
diff --git a/mail/e-mail-shell-view-private.h b/mail/e-mail-shell-view-private.h
index 13c76c7..f6646ca 100644
--- a/mail/e-mail-shell-view-private.h
+++ b/mail/e-mail-shell-view-private.h
@@ -149,6 +149,8 @@ void		e_mail_shell_view_private_finalize
 
 void		e_mail_shell_view_actions_init
 					(EMailShellView *mail_shell_view);
+void		e_mail_shell_view_restore_state
+					(EMailShellView *mail_shell_view);
 void		e_mail_shell_view_execute_search
 					(EMailShellView *mail_shell_view);
 void		e_mail_shell_view_create_filter_from_selected
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index 74438b2..6c35a4e 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -117,8 +117,6 @@ emcs_set_drafts_info (struct emcs_t *emcs,
 	camel_object_ref (drafts_folder);
 	emcs->drafts_folder = drafts_folder;
 	emcs->drafts_uid = g_strdup (drafts_uid);
-
-	g_debug ("%s", G_STRFUNC);
 }
 
 static void
@@ -141,8 +139,6 @@ emcs_set_folder_info (struct emcs_t *emcs,
 	emcs->uid = g_strdup (uid);
 	emcs->flags = flags;
 	emcs->set = set;
-
-	g_debug ("%s", G_STRFUNC);
 }
 
 static void
@@ -2412,8 +2408,9 @@ post_header_clicked_cb (EComposerPostHeader *header,
 	model = e_mail_shell_backend_get_folder_tree_model (mail_shell_backend);
 	folder_tree = em_folder_tree_new_with_model (model);
 
-	em_folder_tree_set_multiselect (
-		EM_FOLDER_TREE (folder_tree), TRUE);
+	em_folder_tree_clone_expanded (EM_FOLDER_TREE (folder_tree));
+	em_folder_tree_set_multiselect (EM_FOLDER_TREE (folder_tree), TRUE);
+
 	em_folder_tree_set_excluded (
 		EM_FOLDER_TREE (folder_tree),
 		EMFT_EXCLUDE_NOSELECT |
diff --git a/mail/em-folder-selection-button.c b/mail/em-folder-selection-button.c
index 933393a..ecdccc0 100644
--- a/mail/em-folder-selection-button.c
+++ b/mail/em-folder-selection-button.c
@@ -256,6 +256,7 @@ folder_selection_button_clicked (GtkButton *button)
 
 	emft = (EMFolderTree *) em_folder_tree_new_with_model (priv->model);
 
+	em_folder_tree_clone_expanded (emft);
 	em_folder_tree_set_multiselect (emft, priv->multiple_select);
 	em_folder_tree_set_excluded (
 		emft, EMFT_EXCLUDE_NOSELECT |
diff --git a/mail/em-folder-selection.c b/mail/em-folder-selection.c
index a052855..56c11bc 100644
--- a/mail/em-folder-selection.c
+++ b/mail/em-folder-selection.c
@@ -33,53 +33,48 @@
 #include "em-folder-selector.h"
 #include "em-folder-selection.h"
 
-#include "e-mail-shell-backend.h"
-
 /* TODO: rmeove this file, it could just go on em-folder-selection or em-utils */
 
-struct _select_folder_data {
-	void (*done) (const gchar *uri, gpointer data);
-	gpointer data;
-};
-
-static void
-emfs_selector_response(EMFolderSelector *emfs, gint response, struct _select_folder_data *d)
-{
-	if (response == GTK_RESPONSE_OK) {
-		const gchar *uri = em_folder_selector_get_selected_uri(emfs);
-
-		d->done(uri, d->data);
-	}
-
-	gtk_widget_destroy((GtkWidget *)emfs);
-}
-
 void
-em_select_folder (GtkWindow *parent_window, const gchar *title, const gchar *oklabel, const gchar *default_uri,
-		  EMFTExcludeFunc exclude,
-		  void (*done) (const gchar *uri, gpointer user_data), gpointer user_data)
+em_select_folder (EMFolderTreeModel *model,
+                  const gchar *title,
+                  const gchar *oklabel,
+                  const gchar *default_uri,
+                  EMFTExcludeFunc exclude,
+                  void (*done) (const gchar *uri, gpointer user_data),
+                  gpointer user_data)
 {
-	struct _select_folder_data *d;
-	EMFolderTreeModel *model;
 	GtkWidget *dialog;
 	EMFolderTree *emft;
 
-	model = e_mail_shell_backend_get_folder_tree_model (global_mail_shell_backend);
+	g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
+	g_return_if_fail (done != NULL);
+
+	/* XXX Do we leak this reference? */
 	emft = (EMFolderTree *) em_folder_tree_new_with_model (model);
+	em_folder_tree_clone_expanded (emft);
+
 	if (exclude)
-		em_folder_tree_set_excluded_func(emft, exclude, user_data);
+		em_folder_tree_set_excluded_func (emft, exclude, user_data);
 	else
-		em_folder_tree_set_excluded (emft, EMFT_EXCLUDE_NOSELECT|EMFT_EXCLUDE_VIRTUAL|EMFT_EXCLUDE_VTRASH);
+		em_folder_tree_set_excluded (
+			emft, EMFT_EXCLUDE_NOSELECT |
+			EMFT_EXCLUDE_VIRTUAL | EMFT_EXCLUDE_VTRASH);
 
-	dialog = em_folder_selector_new(emft, EM_FOLDER_SELECTOR_CAN_CREATE, title, NULL, oklabel);
+	dialog = em_folder_selector_new (
+		emft, EM_FOLDER_SELECTOR_CAN_CREATE, title, NULL, oklabel);
 
-	d = g_malloc0(sizeof(*d));
-	d->data = user_data;
-	d->done = done;
-	g_signal_connect(dialog, "response", G_CALLBACK (emfs_selector_response), d);
-	g_object_set_data_full((GObject *)dialog, "e-select-data", d, (GDestroyNotify)g_free);
-	gtk_widget_show(dialog);
+	if (default_uri != NULL)
+		em_folder_selector_set_selected (
+			EM_FOLDER_SELECTOR (dialog), default_uri);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+		const gchar *uri;
+
+		uri = em_folder_selector_get_selected_uri (
+			EM_FOLDER_SELECTOR (dialog));
+		done (uri, user_data);
+	}
 
-	if (default_uri)
-		em_folder_selector_set_selected((EMFolderSelector *)dialog, default_uri);
+	gtk_widget_destroy (dialog);
 }
diff --git a/mail/em-folder-selection.h b/mail/em-folder-selection.h
index 098fa2b..96d14cb 100644
--- a/mail/em-folder-selection.h
+++ b/mail/em-folder-selection.h
@@ -24,22 +24,18 @@
 #ifndef EM_FOLDER_SELECTION_H
 #define EM_FOLDER_SELECTION_H
 
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus */
+#include <mail/em-folder-tree-model.h>
 
-#include "em-folder-tree.h"
+G_BEGIN_DECLS
 
-struct _GtkWindow;
+void		em_select_folder		(EMFolderTreeModel *model,
+						 const gchar *title,
+						 const gchar *oklabel,
+						 const gchar *default_uri,
+						 EMFTExcludeFunc exclude,
+						 void (*done)(const gchar *uri, gpointer data),
+						 gpointer data);
 
-void em_select_folder (struct _GtkWindow *parent_window, const gchar *title, const gchar *oklabel, const gchar *default_uri,
-		       EMFTExcludeFunc exclude,
-		       void (*done)(const gchar *uri, gpointer data),
-		       gpointer data);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* EM_FOLDER_SELECTION_H */
diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c
index f78405a..f1a667b 100644
--- a/mail/em-folder-tree-model.c
+++ b/mail/em-folder-tree-model.c
@@ -107,38 +107,6 @@ store_info_free (EMFolderTreeModelStoreInfo *si)
 	g_free (si);
 }
 
-static void
-folder_tree_model_load_state (EMFolderTreeModel *model,
-                              const gchar *filename)
-{
-	xmlNodePtr root, node;
-
-	if (model->state)
-		xmlFreeDoc (model->state);
-
-	if ((model->state = e_xml_parse_file (filename)) != NULL) {
-		node = xmlDocGetRootElement (model->state);
-		if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) {
-			/* it is not expected XML file, thus free it and use the default */
-			xmlFreeDoc (model->state);
-		} else
-			return;
-	}
-
-	/* setup some defaults - expand "Local Folders" and "Search Folders" */
-	model->state = xmlNewDoc ((const guchar *)"1.0");
-	root = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL);
-	xmlDocSetRootElement (model->state, root);
-
-	node = xmlNewChild (root, NULL, (const guchar *)"node", NULL);
-	xmlSetProp (node, (const guchar *)"name", (const guchar *)"local");
-	xmlSetProp (node, (const guchar *)"expand", (const guchar *)"true");
-
-	node = xmlNewChild (root, NULL, (const guchar *)"node", NULL);
-	xmlSetProp (node, (const guchar *)"name", (const guchar *)"vfolder");
-	xmlSetProp (node, (const guchar *)"expand", (const guchar *)"true");
-}
-
 static gint
 folder_tree_model_sort (GtkTreeModel *model,
                         GtkTreeIter *a,
@@ -256,10 +224,6 @@ folder_tree_model_finalize (GObject *object)
 {
 	EMFolderTreeModel *model = (EMFolderTreeModel *) object;
 
-	g_free (model->filename);
-	if (model->state)
-		xmlFreeDoc (model->state);
-
 	g_hash_table_destroy (model->store_hash);
 	g_hash_table_destroy (model->uri_hash);
 
@@ -272,23 +236,6 @@ folder_tree_model_finalize (GObject *object)
 }
 
 static void
-folder_tree_model_constructed (GObject *object)
-{
-	EMFolderTreeModel *model = EM_FOLDER_TREE_MODEL (object);
-	EShellBackend *shell_backend;
-	const gchar *config_dir;
-	gchar *filename;
-
-	shell_backend = model->priv->shell_backend;
-	config_dir = e_shell_backend_get_config_dir (shell_backend);
-
-	filename = g_build_filename (
-		config_dir, "folder-tree-expand-state.xml", NULL);
-	folder_tree_model_load_state (model, filename);
-	model->filename = filename;
-}
-
-static void
 folder_tree_model_class_init (EMFolderTreeModelClass *class)
 {
 	GObjectClass *object_class;
@@ -300,7 +247,6 @@ folder_tree_model_class_init (EMFolderTreeModelClass *class)
 	object_class->set_property = folder_tree_model_set_property;
 	object_class->get_property = folder_tree_model_get_property;
 	object_class->finalize = folder_tree_model_finalize;
-	object_class->constructed = folder_tree_model_constructed;
 
 	g_object_class_install_property (
 		object_class,
@@ -361,6 +307,7 @@ folder_tree_model_init (EMFolderTreeModel *model)
 		G_TYPE_STRING,   /* uri */
 		G_TYPE_UINT,     /* unread count */
 		G_TYPE_UINT,     /* flags */
+		G_TYPE_BOOLEAN,  /* is expanded in sidebar */
 		G_TYPE_BOOLEAN,  /* is a store node */
 		G_TYPE_BOOLEAN,  /* is a folder node */
 		G_TYPE_BOOLEAN,  /* has not-yet-loaded subfolders */
@@ -399,19 +346,6 @@ folder_tree_model_init (EMFolderTreeModel *model)
 	model->account_removed_id = g_signal_connect (
 		model->accounts, "account-removed",
 		G_CALLBACK (account_removed), model);
-	//g_signal_connect (
-	//	model, "row-changed",
-	//	G_CALLBACK (emft_model_unread_count_changed), NULL);
-}
-
-static void
-tree_model_iface_init (GtkTreeModelIface *iface)
-{
-}
-
-static void
-tree_sortable_iface_init (GtkTreeSortableIface *iface)
-{
 }
 
 GType
@@ -433,41 +367,20 @@ em_folder_tree_model_get_type (void)
 			NULL   /* value_table */
 		};
 
-		static const GInterfaceInfo tree_model_info = {
-			(GInterfaceInitFunc) tree_model_iface_init,
-			NULL,
-			NULL
-		};
-
-		static const GInterfaceInfo sortable_info = {
-			(GInterfaceInitFunc) tree_sortable_iface_init,
-			NULL,
-			NULL
-		};
-
 		type = g_type_register_static (
 			GTK_TYPE_TREE_STORE, "EMFolderTreeModel",
 			&type_info, 0);
-
-		g_type_add_interface_static (
-			type, GTK_TYPE_TREE_MODEL, &tree_model_info);
-		g_type_add_interface_static (
-			type, GTK_TYPE_TREE_SORTABLE, &sortable_info);
 	}
 
 	return type;
 }
 
-
 static void
 emft_model_unread_count_changed (GtkTreeModel *model, GtkTreeIter *iter)
 {
 	GtkTreeIter parent_iter;
 	GtkTreeIter child_iter = *iter;
 
-	g_signal_handlers_block_by_func (
-		model, emft_model_unread_count_changed, NULL);
-
 	/* Folders are displayed with a bold weight to indicate that
 	   they contain unread messages.  We signal that parent rows
 	   have changed here to update them. */
@@ -480,14 +393,8 @@ emft_model_unread_count_changed (GtkTreeModel *model, GtkTreeIter *iter)
 		gtk_tree_path_free (parent_path);
 		child_iter = parent_iter;
 	}
-
-	g_signal_handlers_unblock_by_func (
-		model, emft_model_unread_count_changed, NULL);
 }
 
-
-
-
 EMFolderTreeModel *
 em_folder_tree_model_new (EMailShellBackend *mail_shell_backend)
 {
@@ -1012,7 +919,7 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con
 	camel_object_ref (store);
 	si->store = store;
 	si->account = account;
-	si->row = row;
+	si->row = gtk_tree_row_reference_copy (row);
 	si->full_hash = g_hash_table_new_full (
 		g_str_hash, g_str_equal,
 		(GDestroyNotify) g_free,
@@ -1020,6 +927,9 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con
 	g_hash_table_insert (model->store_hash, store, si);
 	g_hash_table_insert (model->account_hash, account, si);
 
+	/* Transfer ownership of the URI and GtkTreeRowReference. */
+	g_hash_table_insert (model->uri_hash, uri, row);
+
 	/* each store has folders... but we don't load them until the user demands them */
 	root = iter;
 	gtk_tree_store_append ((GtkTreeStore *) model, &iter, &root);
@@ -1034,8 +944,6 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con
 			    COL_UINT_UNREAD_LAST_SEL, 0,
 			    -1);
 
-	g_free (uri);
-
 	/* listen to store events */
 #define CAMEL_CALLBACK(func) ((CamelObjectEventHookFunc) func)
 	si->created_id = camel_object_hook_event (store, "folder_created", CAMEL_CALLBACK (folder_created_cb), model);
@@ -1133,275 +1041,6 @@ em_folder_tree_model_remove_store (EMFolderTreeModel *model, CamelStore *store)
 	em_folder_tree_model_remove_folders (model, si, &iter);
 }
 
-
-static xmlNodePtr
-find_xml_node (xmlNodePtr root, const gchar *name)
-{
-	xmlNodePtr node;
-	gchar *nname;
-
-	node = root->children;
-	while (node != NULL) {
-		if (!strcmp ((gchar *)node->name, "node")) {
-			nname = (gchar *)xmlGetProp (node, (const guchar *)"name");
-			if (nname && !strcmp (nname, name)) {
-				xmlFree (nname);
-				return node;
-			}
-
-			xmlFree (nname);
-		}
-
-		node = node->next;
-	}
-
-	return node;
-}
-
-gboolean
-em_folder_tree_model_get_expanded (EMFolderTreeModel *model, const gchar *key)
-{
-	xmlNodePtr node;
-	const gchar *name;
-	gchar *buf, *p;
-
-	/* This code needs to be rewritten.
-	   First it doesn't belong on the model
-	   Second, it shouldn't use an xml tree to store a bit table in memory! */
-
-	node = model->state ? model->state->children : NULL;
-	if (!node || strcmp ((gchar *)node->name, "tree-state") != 0)
-		return FALSE;
-
-	name = buf = g_alloca (strlen (key) + 1);
-	p = g_stpcpy (buf, key);
-	if (p[-1] == '/')
-		p[-1] = '\0';
-	p = NULL;
-
-	do {
-		if ((p = strchr (name, '/')))
-			*p = '\0';
-
-		if ((node = find_xml_node (node, name))) {
-			gboolean expanded;
-
-			buf = (gchar *)xmlGetProp (node, (const guchar *)"expand");
-			expanded = buf && !strcmp ((gchar *)buf, "true");
-			xmlFree (buf);
-
-			if (!expanded || p == NULL)
-				return expanded;
-		}
-
-		name = p ? p + 1 : NULL;
-	} while (name && node);
-
-	return FALSE;
-}
-
-
-void
-em_folder_tree_model_set_expanded (EMFolderTreeModel *model, const gchar *key, gboolean expanded)
-{
-	xmlNodePtr node, parent;
-	const gchar *name;
-	gchar *buf, *p;
-
-	if (model->state == NULL)
-		model->state = xmlNewDoc ((const guchar *)"1.0");
-
-	if (!model->state->children) {
-		node = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL);
-		xmlDocSetRootElement (model->state, node);
-	} else {
-		node = model->state->children;
-	}
-
-	name = buf = g_alloca (strlen (key) + 1);
-	p = g_stpcpy (buf, key);
-	if (p[-1] == '/')
-		p[-1] = '\0';
-	p = NULL;
-
-	do {
-		parent = node;
-		if ((p = strchr (name, '/')))
-			*p = '\0';
-
-		if (!(node = find_xml_node (node, name))) {
-			if (!expanded) {
-				/* node doesn't exist, so we don't need to set expanded to FALSE */
-				return;
-			}
-
-			/* node (or parent node) doesn't exist, need to add it */
-			node = xmlNewChild (parent, NULL, (const guchar *)"node", NULL);
-			xmlSetProp (node, (const guchar *)"name", (guchar *)name);
-		}
-
-		xmlSetProp (node, (const guchar *)"expand", (const guchar *)(expanded || p ? "true" : "false"));
-
-		name = p ? p + 1 : NULL;
-	} while (name);
-}
-
-/**
- * emftm_uri_to_key
- * Converts uri to key used in functions like em_folder_tree_model_[s/g]et_expanded.
- * @param uri Uri to be converted.
- * @return Key of the uri or NULL, if failed. Returned value should be clear by g_free.
- **/
-static gchar *
-emftm_uri_to_key (const gchar *uri)
-{
-	CamelException ex = { 0 };
-	CamelStore *store;
-	CamelURL *url;
-	gchar *key;
-
-	if (!uri)
-		return NULL;
-
-	store = (CamelStore *)camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex);
-	camel_exception_clear(&ex);
-
-	url = camel_url_new (uri, NULL);
-
-	if (store == NULL || url == NULL) {
-		key = NULL;
-	} else {
-		const gchar *path;
-		EAccount *account;
-
-		if (((CamelService *)store)->provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
-			path = url->fragment;
-		else
-			path = url->path && url->path[0]=='/' ? url->path+1:url->path;
-
-		if (path == NULL)
-			path = "";
-
-		if ( (account = mail_config_get_account_by_source_url (uri)) )
-			key = g_strdup_printf ("%s/%s", account->uid, path);
-		else if (CAMEL_IS_VEE_STORE (store))
-			key = g_strdup_printf ("vfolder/%s", path);
-		else
-			key = g_strdup_printf ("local/%s", path);
-	}
-
-	if (url)
-		camel_url_free (url);
-
-	if (store)
-		camel_object_unref (store);
-
-	return key;
-}
-
-/**
- * em_folder_tree_model_get_expanded_uri
- * Same as @ref em_folder_tree_model_get_expanded, but here we use uri, not key for node.
- **/
-gboolean
-em_folder_tree_model_get_expanded_uri (EMFolderTreeModel *model, const gchar *uri)
-{
-	gchar *key;
-	gboolean expanded;
-
-	g_return_val_if_fail (model != NULL, FALSE);
-	g_return_val_if_fail (uri != NULL, FALSE);
-
-	key = emftm_uri_to_key (uri);
-	expanded = key && em_folder_tree_model_get_expanded (model, key);
-
-	g_free (key);
-
-	return expanded;
-}
-
-/**
- * em_folder_tree_model_set_expanded_uri
- * Same as @ref em_folder_tree_model_set_expanded, but here we use uri, not key for node.
- **/
-void
-em_folder_tree_model_set_expanded_uri (EMFolderTreeModel *model, const gchar *uri, gboolean expanded)
-{
-	gchar *key;
-
-	g_return_if_fail (model != NULL);
-	g_return_if_fail (uri != NULL);
-
-	key = emftm_uri_to_key (uri);
-	if (key)
-		em_folder_tree_model_set_expanded (model, key, expanded);
-
-	g_free (key);
-}
-
-void
-em_folder_tree_model_save_state (EMFolderTreeModel *model)
-{
-	gchar *dirname;
-
-	if (model->state == NULL)
-		return;
-
-	dirname = g_path_get_dirname (model->filename);
-	if (g_mkdir_with_parents (dirname, 0777) == -1 && errno != EEXIST) {
-		g_free (dirname);
-		return;
-	}
-
-	g_free (dirname);
-
-	e_xml_save_file (model->filename, model->state);
-}
-
-
-static void
-expand_foreach_r (EMFolderTreeModel *model, xmlNodePtr parent, const gchar *dirname, EMFTModelExpandFunc func, gpointer user_data)
-{
-	xmlNodePtr node = parent->children;
-	gchar *path, *name, *expand;
-
-	while (node != NULL) {
-		if (!strcmp ((gchar *)node->name, "node")) {
-			name = (gchar *)xmlGetProp (node, (const guchar *)"name");
-			expand = (gchar *)xmlGetProp (node, (const guchar *)"expand");
-
-			if (expand && name && !strcmp ((gchar *)expand, "true")) {
-				if (dirname)
-					path = g_strdup_printf ("%s/%s", dirname, name);
-				else
-					path = g_strdup (name);
-
-				func (model, path, user_data);
-				if (node->children)
-					expand_foreach_r (model, node, path, func, user_data);
-				g_free (path);
-			}
-
-			xmlFree (expand);
-			xmlFree (name);
-		}
-
-		node = node->next;
-	}
-}
-
-void
-em_folder_tree_model_expand_foreach (EMFolderTreeModel *model, EMFTModelExpandFunc func, gpointer user_data)
-{
-	xmlNodePtr root;
-
-	root = model->state ? model->state->children : NULL;
-	if (!root || !root->children || strcmp ((gchar *)root->name, "tree-state") != 0)
-		return;
-
-	expand_foreach_r (model, root, NULL, func, user_data);
-}
-
 gboolean
 em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, CamelStore *store, const gchar *full)
 {
@@ -1487,7 +1126,6 @@ em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *sto
 	GtkTreePath *tree_path;
 	GtkTreeIter iter;
 	guint old_unread = 0;
-	gchar *uri, *sel_uri;
 
 	g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
 	g_return_if_fail (CAMEL_IS_STORE (store));
@@ -1516,82 +1154,15 @@ em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *sto
 
 	gtk_tree_path_free (tree_path);
 
-	sel_uri = em_folder_tree_model_get_selected (model);
 	gtk_tree_model_get (
 		GTK_TREE_MODEL (model), &iter,
-		COL_UINT_UNREAD_LAST_SEL, &old_unread,
-		COL_STRING_URI, &uri, -1);
-	if (!(g_strcmp0 (sel_uri, uri) != 0 && unread > old_unread))
-		old_unread = unread;
+		COL_UINT_UNREAD_LAST_SEL, &old_unread, -1);
+
 	gtk_tree_store_set (
 		GTK_TREE_STORE (model), &iter,
 		COL_UINT_UNREAD, unread,
-		COL_UINT_UNREAD_LAST_SEL, old_unread, -1);
-
-	g_free (uri);
-	g_free (sel_uri);
+		COL_UINT_UNREAD_LAST_SEL, MIN (old_unread, unread), -1);
 
 	/* May be this is from where we should propagate unread count to parents etc. */
 	emft_model_unread_count_changed (GTK_TREE_MODEL (model), &iter);
 }
-
-
-gchar *
-em_folder_tree_model_get_selected (EMFolderTreeModel *model)
-{
-	xmlNodePtr node;
-	gchar *buf, *uri;
-
-	node = model->state ? model->state->children : NULL;
-	if (!node || strcmp ((gchar *)node->name, "tree-state") != 0)
-		return NULL;
-
-	node = node->children;
-	while (node != NULL) {
-		if (!strcmp ((gchar *)node->name, "selected"))
-			break;
-		node = node->next;
-	}
-
-	if (node == NULL)
-		return NULL;
-
-	buf = (gchar *)xmlGetProp (node, (guchar *)"uri");
-	uri = g_strdup (buf);
-	xmlFree (buf);
-
-	if (uri && !*uri) {
-		g_free (uri);
-		return NULL;
-	}
-	return uri;
-}
-
-
-void
-em_folder_tree_model_set_selected (EMFolderTreeModel *model, const gchar *uri)
-{
-	xmlNodePtr root, node;
-
-	if (model->state == NULL)
-		model->state = xmlNewDoc ((guchar *)"1.0");
-
-	if (!model->state->children) {
-		root = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL);
-		xmlDocSetRootElement (model->state, root);
-	} else {
-		root = model->state->children;
-	}
-
-	node = root->children;
-	while (node != NULL) {
-		if (!strcmp ((gchar *)node->name, "selected"))
-			break;
-		node = node->next;
-	}
-
-	if (node == NULL)
-		node = xmlNewChild (root, NULL, (const guchar *)"selected", NULL);
-
-	xmlSetProp (node, (const guchar *)"uri", (guchar *)uri);
-}
diff --git a/mail/em-folder-tree-model.h b/mail/em-folder-tree-model.h
index 418248a..f8952d0 100644
--- a/mail/em-folder-tree-model.h
+++ b/mail/em-folder-tree-model.h
@@ -24,7 +24,6 @@
 #define EM_FOLDER_TREE_MODEL_H
 
 #include <gtk/gtk.h>
-#include <libxml/tree.h>
 #include <camel/camel-store.h>
 #include <libedataserver/e-account-list.h>
 #include <mail/e-mail-shell-backend.h>
@@ -64,6 +63,7 @@ enum {
 	COL_UINT_UNREAD,          /* unread count */
 	COL_UINT_FLAGS,		/* FolderInfo.flags */
 
+	COL_BOOL_EXPANDED,        /* node is expanded in sidebar */
 	COL_BOOL_IS_STORE,        /* toplevel store node? */
 	COL_BOOL_IS_FOLDER,       /* folder (not a store) */
 	COL_BOOL_LOAD_SUBDIRS,    /* %TRUE only if the store/folder
@@ -93,9 +93,6 @@ struct _EMFolderTreeModel {
 	GtkTreeStore parent;
 	EMFolderTreeModelPrivate *priv;
 
-	gchar *filename;            /* state filename */
-	xmlDocPtr state;           /* saved expanded state from previous session */
-
 	GHashTable *store_hash;    /* maps CamelStore's to store-info's */
 	GHashTable *uri_hash;      /* maps URI's to GtkTreeRowReferences */
 
@@ -120,9 +117,6 @@ struct _EMFolderTreeModelClass {
 	void     (* folder_added)       (EMFolderTreeModel *model,
 					 const gchar *path,
 					 const gchar *uri);
-
-	void     (* store_added)        (EMFolderTreeModel *model,
-					 const gchar *uri);
 };
 
 
@@ -142,20 +136,6 @@ void em_folder_tree_model_remove_store (EMFolderTreeModel *model, CamelStore *st
 void em_folder_tree_model_remove_folders (EMFolderTreeModel *model, struct _EMFolderTreeModelStoreInfo *si,
 					  GtkTreeIter *toplevel);
 
-gchar *em_folder_tree_model_get_selected (EMFolderTreeModel *model);
-void em_folder_tree_model_set_selected (EMFolderTreeModel *model, const gchar *uri);
-
-gboolean em_folder_tree_model_get_expanded (EMFolderTreeModel *model, const gchar *key);
-void em_folder_tree_model_set_expanded (EMFolderTreeModel *model, const gchar *key, gboolean expanded);
-
-gboolean em_folder_tree_model_get_expanded_uri (EMFolderTreeModel *model, const gchar *uri);
-void em_folder_tree_model_set_expanded_uri (EMFolderTreeModel *model, const gchar *uri, gboolean expanded);
-
-void em_folder_tree_model_save_state (EMFolderTreeModel *model);
-
-typedef void (* EMFTModelExpandFunc) (EMFolderTreeModel *model, const gchar *path, gpointer user_data);
-void em_folder_tree_model_expand_foreach (EMFolderTreeModel *model, EMFTModelExpandFunc func, gpointer user_data);
-
 void em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *store, const gchar *path, gint unread);
 gboolean em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, CamelStore *store, const gchar *full);
 gchar * em_folder_tree_model_get_folder_name (EMFolderTreeModel *model, CamelStore *store, const gchar *full);
diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c
index e6abb53..de488c7 100644
--- a/mail/em-folder-tree.c
+++ b/mail/em-folder-tree.c
@@ -104,8 +104,6 @@ struct _EMFolderTreePrivate {
 				 * we need to set it when we set the
 				 * selection */
 
-	guint save_state_id;
-
 	guint autoscroll_id;
 	guint autoexpand_id;
 	GtkTreeRowReference *autoexpand_row;
@@ -159,11 +157,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
 extern CamelSession *session;
 extern CamelStore *vfolder_store;
 
-static gboolean emft_save_state (EMFolderTree *emft);
-static void emft_queue_save_state (EMFolderTree *emft);
-
-static void emft_update_model_expanded_state (struct _EMFolderTreePrivate *priv, GtkTreeIter *iter, gboolean expanded);
-
 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);
@@ -230,11 +223,6 @@ em_folder_tree_destroy (GtkObject *object)
 		priv->loaded_row_id = 0;
 	}
 
-	if (priv->save_state_id != 0) {
-		g_source_remove (priv->save_state_id);
-		emft_save_state (EM_FOLDER_TREE (object));
-	}
-
 	if (priv->autoscroll_id != 0) {
 		g_source_remove (priv->autoscroll_id);
 		priv->autoscroll_id = 0;
@@ -418,10 +406,13 @@ render_icon (GtkTreeViewColumn *column,
              GtkTreeModel *model,
              GtkTreeIter *iter)
 {
+	GtkTreeSelection *selection;
+	GtkWidget *tree_view;
 	GIcon *icon;
 	guint unread;
 	guint old_unread;
 	gchar *icon_name;
+	gboolean row_selected;
 
 	gtk_tree_model_get (
 		model, iter,
@@ -434,8 +425,12 @@ render_icon (GtkTreeViewColumn *column,
 
 	icon = g_themed_icon_new (icon_name);
 
+	tree_view = gtk_tree_view_column_get_tree_view (column);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+	row_selected = gtk_tree_selection_iter_is_selected (selection, iter);
+
 	/* Show an emblem if there's new mail. */
-	if (unread > old_unread) {
+	if (!row_selected && unread > old_unread) {
 		GIcon *temp_icon;
 		GEmblem *emblem;
 
@@ -547,22 +542,6 @@ em_folder_tree_construct (EMFolderTree *emft, EMFolderTreeModel *model)
 	g_signal_connect (selection, "changed", G_CALLBACK (emft_tree_selection_changed), emft);
 }
 
-GtkWidget *
-em_folder_tree_new (EMailShellBackend *mail_shell_backend)
-{
-	EMFolderTreeModel *model;
-	EMFolderTree *emft;
-
-	g_return_val_if_fail (
-		E_IS_MAIL_SHELL_BACKEND (mail_shell_backend), NULL);
-
-	model = e_mail_shell_backend_get_folder_tree_model (mail_shell_backend);
-	emft = (EMFolderTree *) em_folder_tree_new_with_model (model);
-	g_object_unref (model);
-
-	return (GtkWidget *) emft;
-}
-
 /* NOTE: Removes and frees the selected uri structure */
 static void
 emft_select_uri(EMFolderTree *emft, GtkTreePath *path, struct _selected_uri *u)
@@ -670,7 +649,6 @@ emft_maybe_expand_row (EMFolderTreeModel *model, GtkTreePath *tree_path, GtkTree
 	gchar *full_name;
 	gchar *key;
 	struct _selected_uri *u;
-	gboolean is_expanded;
 
 	tree_view = GTK_TREE_VIEW (emft);
 
@@ -691,21 +669,14 @@ emft_maybe_expand_row (EMFolderTreeModel *model, GtkTreePath *tree_path, GtkTree
 		key = g_strdup_printf ("local/%s", full_name ? full_name : "");
 	}
 
-	is_expanded = em_folder_tree_model_get_expanded (model, key);
 	u = g_hash_table_lookup(priv->select_uris_table, key);
-	if (is_expanded || u) {
-		if (is_expanded) {
-			gtk_tree_view_expand_to_path (tree_view, tree_path);
-			gtk_tree_view_expand_row (tree_view, tree_path, FALSE);
-		} else {
-			gchar *c = strrchr (key, '/');
+	if (u) {
+		gchar *c = strrchr (key, '/');
 
-			*c = '\0';
-			emft_expand_node (model, key, emft);
-		}
+		*c = '\0';
+		emft_expand_node (model, key, emft);
 
-		if (u)
-			emft_select_uri(emft, tree_path, u);
+		emft_select_uri(emft, tree_path, u);
 	}
 
 	g_free (full_name);
@@ -722,8 +693,6 @@ em_folder_tree_new_with_model (EMFolderTreeModel *model)
 	em_folder_tree_construct (emft, model);
 	g_object_ref (model);
 
-	em_folder_tree_model_expand_foreach (model, (EMFTModelExpandFunc)emft_expand_node, emft);
-
 	emft->priv->loading_row_id = g_signal_connect (model, "loading-row", G_CALLBACK (emft_maybe_expand_row), emft);
 	emft->priv->loaded_row_id = g_signal_connect (model, "loaded-row", G_CALLBACK (emft_maybe_expand_row), emft);
 
@@ -733,6 +702,38 @@ em_folder_tree_new_with_model (EMFolderTreeModel *model)
 	return (GtkWidget *) emft;
 }
 
+static gboolean
+folder_tree_clone_expanded (GtkTreeModel *model,
+                            GtkTreePath *path,
+                            GtkTreeIter *iter,
+                            GtkTreeView *tree_view)
+{
+	gboolean expanded;
+
+	gtk_tree_model_get (model, iter, COL_BOOL_EXPANDED, &expanded, -1);
+
+	if (expanded)
+		gtk_tree_view_expand_row (tree_view, path, FALSE);
+	else
+		gtk_tree_view_collapse_row (tree_view, path);
+
+	return FALSE;
+}
+
+void
+em_folder_tree_clone_expanded (EMFolderTree *emft)
+{
+	GtkTreeModel *model;
+
+	g_return_if_fail (EM_IS_FOLDER_TREE (emft));
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (emft));
+
+	gtk_tree_model_foreach (
+		model, (GtkTreeModelForeachFunc)
+		folder_tree_clone_expanded, emft);
+}
+
 static void
 tree_drag_begin (GtkWidget *widget, GdkDragContext *context, EMFolderTree *emft)
 {
@@ -1532,11 +1533,14 @@ em_folder_tree_enable_drag_and_drop (EMFolderTree *emft)
 }
 
 void
-em_folder_tree_set_multiselect (EMFolderTree *tree, gboolean mode)
+em_folder_tree_set_multiselect (EMFolderTree *tree,
+                                gboolean mode)
 {
 	GtkTreeSelection *sel;
 	GtkTreeView *tree_view;
 
+	g_return_if_fail (EM_IS_FOLDER_TREE (tree));
+
 	tree_view = GTK_TREE_VIEW (tree);
 	sel = gtk_tree_view_get_selection (tree_view);
 
@@ -1544,13 +1548,22 @@ em_folder_tree_set_multiselect (EMFolderTree *tree, gboolean mode)
 	gtk_tree_selection_set_mode (sel, mode ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
 }
 
-void em_folder_tree_set_excluded(EMFolderTree *emft, guint32 flags)
+void
+em_folder_tree_set_excluded (EMFolderTree *emft, guint32 flags)
 {
+	g_return_if_fail (EM_IS_FOLDER_TREE (emft));
+
 	emft->priv->excluded = flags;
 }
 
-void em_folder_tree_set_excluded_func(EMFolderTree *emft, EMFTExcludeFunc exclude, gpointer data)
+void
+em_folder_tree_set_excluded_func (EMFolderTree *emft,
+                                  EMFTExcludeFunc exclude,
+                                  gpointer data)
 {
+	g_return_if_fail (EM_IS_FOLDER_TREE (emft));
+	g_return_if_fail (exclude != NULL);
+
 	emft->priv->excluded_func = exclude;
 	emft->priv->excluded_data = data;
 }
@@ -1684,7 +1697,6 @@ em_folder_tree_set_selected_list (EMFolderTree *emft, GList *list, gboolean expa
 			end = strrchr(expand_key, '/');
 			do {
 				emft_expand_node(priv->model, expand_key, emft);
-				em_folder_tree_model_set_expanded(priv->model, expand_key, TRUE);
 				*end = 0;
 				end = strrchr(expand_key, '/');
 			} while (end);
@@ -1816,14 +1828,11 @@ emft_get_folder_info__done (struct _EMFolderTreeGetFolderInfo *m)
 
 	if (fi == NULL) {
 		/* no children afterall... remove the "Loading..." placeholder node */
-		emft_update_model_expanded_state (priv, &root, FALSE);
-
 		gtk_tree_store_remove (model, &iter);
 
 		if (is_store) {
 			path = gtk_tree_model_get_path ((GtkTreeModel *) model, &root);
 			gtk_tree_view_collapse_row (tree_view, path);
-			emft_queue_save_state (m->emft);
 			gtk_tree_path_free (path);
 			return;
 		}
@@ -1842,7 +1851,6 @@ emft_get_folder_info__done (struct _EMFolderTreeGetFolderInfo *m)
 	}
 
 	gtk_tree_store_set (model, &root, COL_BOOL_LOAD_SUBDIRS, FALSE, -1);
-	emft_queue_save_state (m->emft);
 }
 
 static void
@@ -1865,41 +1873,8 @@ static MailMsgInfo get_folder_info_info = {
 };
 
 static void
-emft_update_model_expanded_state (struct _EMFolderTreePrivate *priv, GtkTreeIter *iter, gboolean expanded)
-{
-	struct _EMFolderTreeModelStoreInfo *si;
-	gboolean is_store;
-	CamelStore *store;
-	EAccount *account;
-	gchar *full_name;
-	gchar *key;
-
-	gtk_tree_model_get ((GtkTreeModel *) priv->model, iter,
-			    COL_STRING_FULL_NAME, &full_name,
-			    COL_POINTER_CAMEL_STORE, &store,
-			    COL_BOOL_IS_STORE, &is_store,
-			    -1);
-
-	si = g_hash_table_lookup (priv->model->store_hash, store);
-	if ((account = e_get_account_by_name (si->display_name))) {
-		key = g_strdup_printf ("%s/%s", account->uid, full_name ? full_name : "");
-	} else if (CAMEL_IS_VEE_STORE (store)) {
-		/* vfolder store */
-		key = g_strdup_printf ("vfolder/%s", full_name ? full_name : "");
-	} else {
-		/* local store */
-		key = g_strdup_printf ("local/%s", full_name ? full_name : "");
-	}
-
-	em_folder_tree_model_set_expanded (priv->model, key, expanded);
-	g_free (full_name);
-	g_free (key);
-}
-
-static void
 emft_tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *tree_path, EMFolderTree *emft)
 {
-	struct _EMFolderTreePrivate *priv = emft->priv;
 	struct _EMFolderTreeGetFolderInfo *m;
 	GtkTreeModel *model;
 	CamelStore *store;
@@ -1914,10 +1889,7 @@ emft_tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *t
 			    COL_BOOL_LOAD_SUBDIRS, &load,
 			    -1);
 
-	emft_update_model_expanded_state (priv, root, TRUE);
-
 	if (!load) {
-		emft_queue_save_state (emft);
 		g_free (full_name);
 		return;
 	}
@@ -1950,9 +1922,6 @@ emft_tree_test_collapse_row (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePa
 			gtk_tree_view_set_cursor (treeview, tree_path, NULL, FALSE);
 	}
 
-	emft_update_model_expanded_state (emft->priv, root, FALSE);
-	emft_queue_save_state (emft);
-
 	return FALSE;
 }
 
@@ -2081,9 +2050,7 @@ emft_tree_selection_changed (GtkTreeSelection *selection, EMFolderTree *emft)
 	guint old_unread = 0;
 
 	if (!emft_selection_get_selected (selection, &model, &iter)) {
-		em_folder_tree_model_set_selected (emft->priv->model, NULL);
 		g_signal_emit (emft, signals[FOLDER_SELECTED], 0, NULL, NULL, 0);
-		emft_queue_save_state (emft);
 		return;
 	}
 
@@ -2401,29 +2368,6 @@ em_folder_tree_get_model_storeinfo (EMFolderTree *emft, CamelStore *store)
 	return si;
 }
 
-static gboolean
-emft_save_state (EMFolderTree *emft)
-{
-	struct _EMFolderTreePrivate *priv = emft->priv;
-
-	em_folder_tree_model_save_state (priv->model);
-	priv->save_state_id = 0;
-
-	return FALSE;
-}
-
-
-static void
-emft_queue_save_state (EMFolderTree *emft)
-{
-	struct _EMFolderTreePrivate *priv = emft->priv;
-
-	if (priv->save_state_id != 0)
-		return;
-
-	priv->save_state_id = g_timeout_add_seconds (1, (GSourceFunc) emft_save_state, emft);
-}
-
 void
 em_folder_tree_set_skip_double_click (EMFolderTree *emft, gboolean skip)
 {
diff --git a/mail/em-folder-tree.h b/mail/em-folder-tree.h
index bcbac54..4d32f00 100644
--- a/mail/em-folder-tree.h
+++ b/mail/em-folder-tree.h
@@ -83,9 +83,8 @@ struct _EMFolderTreeClass {
 };
 
 GType em_folder_tree_get_type (void);
-
-GtkWidget *em_folder_tree_new (EMailShellBackend *mail_shell_backend);
 GtkWidget *em_folder_tree_new_with_model (EMFolderTreeModel *model);
+void em_folder_tree_clone_expanded (EMFolderTree *emft);
 
 void em_folder_tree_enable_drag_and_drop (EMFolderTree *emft);
 
diff --git a/mail/em-folder-utils.c b/mail/em-folder-utils.c
index f92e8e3..4ab00e2 100644
--- a/mail/em-folder-utils.c
+++ b/mail/em-folder-utils.c
@@ -355,17 +355,24 @@ emfu_copy_folder_exclude(EMFolderTree *tree, GtkTreeModel *model, GtkTreeIter *i
 /* FIXME: this interface references the folderinfo without copying it  */
 /* FIXME: these functions must be documented */
 void
-em_folder_utils_copy_folder(CamelFolderInfo *folderinfo, gint delete)
+em_folder_utils_copy_folder (EMFolderTreeModel *model,
+                             CamelFolderInfo *folderinfo,
+                             gint delete)
 {
 	struct _copy_folder_data *cfd;
 
+	g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
+	g_return_if_fail (folderinfo != NULL);
+
 	cfd = g_malloc (sizeof (*cfd));
 	cfd->fi = folderinfo;
 	cfd->delete = delete;
 
-	em_select_folder (NULL, _("Select folder"), delete?_("_Move"):_("C_opy"),
-			  NULL, emfu_copy_folder_exclude,
-			  emfu_copy_folder_selected, cfd);
+	em_select_folder (
+		model, _("Select folder"),
+		delete ? _("_Move") : _("C_opy"),
+		NULL, emfu_copy_folder_exclude,
+		emfu_copy_folder_selected, cfd);
 }
 
 static void
@@ -696,6 +703,7 @@ em_folder_utils_create_folder (CamelFolderInfo *folderinfo, EMFolderTree *emft,
 
 	model = e_mail_shell_backend_get_folder_tree_model (global_mail_shell_backend);
 	folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (model);
+	em_folder_tree_clone_expanded (folder_tree);
 
 	dialog = em_folder_selector_create_new (folder_tree, 0, _("Create Folder"), _("Specify where to create the folder:"));
 	if (folderinfo != NULL)
diff --git a/mail/em-folder-utils.h b/mail/em-folder-utils.h
index db7ddb8..5d1333e 100644
--- a/mail/em-folder-utils.h
+++ b/mail/em-folder-utils.h
@@ -40,14 +40,15 @@ gint		em_folder_utils_copy_folders	(CamelStore *fromstore,
 /* FIXME These API's are really busted.  There is no consistency and
  *       most rely on the wrong data. */
 
-void		em_folder_utils_copy_folder	(CamelFolderInfo *folderinfo,
-						 gint delete);
+void		em_folder_utils_copy_folder	(EMFolderTreeModel *model,
+						 CamelFolderInfo *folderinfo,
+						 gboolean delete);
 
 void		em_folder_utils_delete_folder	(CamelFolder *folder);
 void		em_folder_utils_rename_folder	(CamelFolder *folder);
 
 void		em_folder_utils_create_folder	(CamelFolderInfo *folderinfo,
-						 EMFolderTree * emft,
+						 EMFolderTree *emft,
 						 GtkWindow *parent);
 
 const gchar *	em_folder_utils_get_icon_name	(guint32 flags);
diff --git a/plugins/groupwise-features/install-shared.c b/plugins/groupwise-features/install-shared.c
index 67b1b5b..e9fd927 100644
--- a/plugins/groupwise-features/install-shared.c
+++ b/plugins/groupwise-features/install-shared.c
@@ -166,6 +166,8 @@ accept_clicked(GnomeDruidPage *page, GtkWidget *druid, CamelMimeMessage *msg)
 	accept_data = g_new0(struct AcceptData, 1);
 	model = mail_component_peek_tree_model (NULL);
 	folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (model);
+	em_folder_tree_clone_expanded (folder_tree);
+
 	dialog = em_folder_selector_create_new (folder_tree, 0, _("Create folder"), _("Specify where to create the folder:"));
 	uri = em_folder_tree_get_selected_uri(folder_tree);
 	em_folder_selector_set_selected ((EMFolderSelector *) dialog, uri);
diff --git a/plugins/groupwise-features/share-folder-common.c b/plugins/groupwise-features/share-folder-common.c
index 50bb6fc..900d949 100644
--- a/plugins/groupwise-features/share-folder-common.c
+++ b/plugins/groupwise-features/share-folder-common.c
@@ -383,6 +383,8 @@ create_shared_folder(EPopup *ep, EPopupItem *p, gpointer data)
 
 	model = mail_component_peek_tree_model (mail_component_peek ());
 	folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (model);
+	em_folder_tree_clone_expanded (folder_tree);
+
 	dialog = em_folder_selector_create_new (folder_tree, 0, _("Create folder"), _("Specify where to create the folder:"));
 	uri = em_folder_tree_get_selected_uri(folder_tree);
 	em_folder_selector_set_selected ((EMFolderSelector *) dialog, uri);
diff --git a/shell/e-shell-content.c b/shell/e-shell-content.c
index 216520f..2fc9569 100644
--- a/shell/e-shell-content.c
+++ b/shell/e-shell-content.c
@@ -24,6 +24,7 @@
 #include <glib/gi18n.h>
 
 #include "e-util/e-binding.h"
+#include "e-util/e-util.h"
 #include "filter/rule-editor.h"
 #include "widgets/misc/e-action-combo-box.h"
 #include "widgets/misc/e-hinted-entry.h"
@@ -36,6 +37,10 @@
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_SHELL_CONTENT, EShellContentPrivate))
 
+#define STATE_KEY_SEARCH_FILTER		"SearchFilter"
+#define STATE_KEY_SEARCH_SCOPE		"SearchScope"
+#define STATE_KEY_SEARCH_TEXT		"SearchText"
+
 struct _EShellContentPrivate {
 
 	gpointer shell_view;  /* weak pointer */
@@ -55,8 +60,6 @@ struct _EShellContentPrivate {
 	GtkWidget *search_entry;
 	GtkWidget *scope_label;
 	GtkWidget *scope_combo_box;
-
-	GtkStateType search_state;
 };
 
 enum {
@@ -138,7 +141,10 @@ action_search_execute_cb (GtkAction *action,
 	/* Direct the focus away from the search entry, so that a
 	 * focus-in event is required before the text can be changed.
 	 * This will reset the entry to the appropriate visual state. */
-	gtk_widget_grab_focus (gtk_bin_get_child (GTK_BIN (shell_content)));
+	widget = gtk_bin_get_child (GTK_BIN (shell_content));
+	if (GTK_IS_PANED (widget))
+		widget = gtk_paned_get_child1 (GTK_PANED (widget));
+	gtk_widget_grab_focus (widget);
 }
 
 static void
@@ -897,7 +903,6 @@ shell_content_init (EShellContent *shell_content)
 	gtk_label_set_mnemonic_widget (label, widget);
 	gtk_box_pack_start (box, widget, TRUE, TRUE, 0);
 	shell_content->priv->search_entry = g_object_ref (widget);
-	shell_content->priv->search_state = GTK_STATE_NORMAL;
 	gtk_widget_show (widget);
 
 	g_signal_connect_swapped (
@@ -1486,3 +1491,69 @@ exit:
 	g_object_unref (rule);
 	gtk_widget_destroy (dialog);
 }
+
+void
+e_shell_content_restore_state (EShellContent *shell_content,
+                               const gchar *group_name)
+{
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+	GKeyFile *key_file;
+	GtkAction *action;
+	GtkWidget *widget;
+	const gchar *key;
+	gchar *string;
+
+	g_return_if_fail (E_IS_SHELL_CONTENT (shell_content));
+	g_return_if_fail (group_name != NULL);
+
+	shell_view = e_shell_content_get_shell_view (shell_content);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	/* Changing the combo boxes triggers searches, so block
+	 * the search action until the state is fully restored. */
+	action = e_shell_window_get_action (shell_window, "search-execute");
+	gtk_action_block_activate (action);
+
+	key = STATE_KEY_SEARCH_FILTER;
+	string = g_key_file_get_string (key_file, group_name, key, NULL);
+	if (string != NULL && *string != '\0')
+		action = e_shell_window_get_action (shell_window, string);
+	else
+		action = NULL;
+	if (action != NULL)
+		gtk_action_activate (action);
+	else {
+		/* Pick the first combo box item. */
+		widget = shell_content->priv->filter_combo_box;
+		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+	}
+	g_free (string);
+
+	key = STATE_KEY_SEARCH_SCOPE;
+	string = g_key_file_get_string (key_file, group_name, key, NULL);
+	if (string != NULL && *string != '\0')
+		action = e_shell_window_get_action (shell_window, string);
+	else
+		action = NULL;
+	if (action != NULL)
+		gtk_action_activate (action);
+	else {
+		/* Pick the first combo box item. */
+		widget = shell_content->priv->scope_combo_box;
+		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+	}
+	g_free (string);
+
+	key = STATE_KEY_SEARCH_TEXT;
+	string = g_key_file_get_string (key_file, group_name, key, NULL);
+	e_shell_content_set_search_text (shell_content, string);
+	g_free (string);
+
+	action = e_shell_window_get_action (shell_window, "search-execute");
+	gtk_action_unblock_activate (action);
+
+	/* Now execute the search. */
+	gtk_action_activate (action);
+}
diff --git a/shell/e-shell-content.h b/shell/e-shell-content.h
index af2799c..8e754ff 100644
--- a/shell/e-shell-content.h
+++ b/shell/e-shell-content.h
@@ -140,6 +140,8 @@ void		e_shell_content_run_edit_searches_dialog
 						(EShellContent *shell_content);
 void		e_shell_content_run_save_search_dialog
 						(EShellContent *shell_content);
+void		e_shell_content_restore_state	(EShellContent *shell_content,
+						 const gchar *group_name);
 
 G_END_DECLS
 
diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c
index cf89442..d1ab1af 100644
--- a/shell/e-shell-view.c
+++ b/shell/e-shell-view.c
@@ -33,10 +33,15 @@
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_SHELL_VIEW, EShellViewPrivate))
 
+#define STATE_SAVE_TIMEOUT_SECONDS 3
+
 struct _EShellViewPrivate {
 
 	gpointer shell_window;  /* weak pointer */
 
+	GKeyFile *state_key_file;
+	guint state_save_source_id;
+
 	gchar *title;
 	gchar *view_id;
 	gint page_num;
@@ -118,6 +123,72 @@ shell_view_update_view_id (EShellView *shell_view,
 }
 
 static void
+shell_view_load_state (EShellView *shell_view)
+{
+	EShellBackend *shell_backend;
+	GKeyFile *key_file;
+	const gchar *config_dir;
+	gchar *filename;
+	GError *error = NULL;
+
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	config_dir = e_shell_backend_get_config_dir (shell_backend);
+	filename = g_build_filename (config_dir, "state", NULL);
+
+	/* XXX Should do this asynchronously. */
+	key_file = shell_view->priv->state_key_file;
+	g_key_file_load_from_file (key_file, filename, 0, &error);
+
+	if (error == NULL)
+		goto exit;
+
+	if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+		g_warning ("%s", error->message);
+
+	g_error_free (error);
+
+exit:
+	g_free (filename);
+}
+
+static void
+shell_view_save_state (EShellView *shell_view)
+{
+	EShellBackend *shell_backend;
+	GKeyFile *key_file;
+	const gchar *config_dir;
+	gchar *contents;
+	gchar *filename;
+	GError *error = NULL;
+
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	config_dir = e_shell_backend_get_config_dir (shell_backend);
+	filename = g_build_filename (config_dir, "state", NULL);
+
+	/* XXX Should do this asynchronously. */
+	key_file = shell_view->priv->state_key_file;
+	contents = g_key_file_to_data (key_file, NULL, NULL);
+	g_file_set_contents (filename, contents, -1, &error);
+	g_free (contents);
+
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+
+	g_free (filename);
+}
+
+static gboolean
+shell_view_state_timeout_cb (EShellView *shell_view)
+{
+	shell_view_save_state (shell_view);
+	shell_view->priv->state_save_source_id = 0;
+
+	return FALSE;
+}
+
+static void
 shell_view_emit_toggled (EShellView *shell_view)
 {
 	g_signal_emit (shell_view, signals[TOGGLED], 0);
@@ -267,6 +338,13 @@ shell_view_dispose (GObject *object)
 
 	priv = E_SHELL_VIEW_GET_PRIVATE (object);
 
+	/* Expedite any pending state saves. */
+	if (priv->state_save_source_id > 0) {
+		g_source_remove (priv->state_save_source_id);
+		priv->state_save_source_id = 0;
+		shell_view_save_state (E_SHELL_VIEW (object));
+	}
+
 	if (priv->shell_window != NULL) {
 		g_object_remove_weak_pointer (
 			G_OBJECT (priv->shell_window), &priv->shell_window);
@@ -304,6 +382,8 @@ shell_view_finalize (GObject *object)
 
 	priv = E_SHELL_VIEW_GET_PRIVATE (object);
 
+	g_key_file_free (priv->state_key_file);
+
 	g_free (priv->title);
 	g_free (priv->view_id);
 
@@ -330,6 +410,8 @@ shell_view_constructed (GObject *object)
 
 	e_plugin_ui_register_manager (ui_manager, id, shell_view);
 
+	shell_view_load_state (shell_view);
+
 	/* Invoke factory methods. */
 
 	widget = class->new_shell_content (shell_view);
@@ -598,6 +680,7 @@ shell_view_init (EShellView *shell_view,
 	size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
 
 	shell_view->priv = E_SHELL_VIEW_GET_PRIVATE (shell_view);
+	shell_view->priv->state_key_file = g_key_file_new ();
 	shell_view->priv->size_group = size_group;
 }
 
@@ -947,6 +1030,48 @@ e_shell_view_get_shell_taskbar (EShellView *shell_view)
 }
 
 /**
+ * e_shell_view_get_state_key_file:
+ * @shell_view: an #EShellView
+ *
+ * Returns the #GKeyFile holding widget state data for @shell_view.
+ *
+ * Returns: the #GKeyFile for @shell_view
+ **/
+GKeyFile *
+e_shell_view_get_state_key_file (EShellView *shell_view)
+{
+	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+	return shell_view->priv->state_key_file;
+}
+
+/**
+ * e_shell_view_set_state_dirty:
+ * @shell_view: an #EShellView
+ *
+ * Marks the widget state data as modified (or "dirty") and schedules it
+ * to be saved to disk after a short delay.  The delay caps the frequency
+ * of saving to disk.
+ **/
+void
+e_shell_view_set_state_dirty (EShellView *shell_view)
+{
+	guint source_id;
+
+	g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
+
+	/* If a timeout is already scheduled, do nothing. */
+	if (shell_view->priv->state_save_source_id > 0)
+		return;
+
+	source_id = g_timeout_add_seconds (
+		STATE_SAVE_TIMEOUT_SECONDS, (GSourceFunc)
+		shell_view_state_timeout_cb, shell_view);
+
+	shell_view->priv->state_save_source_id = source_id;
+}
+
+/**
  * e_shell_view_update_actions:
  * @shell_view: an #EShellView
  *
diff --git a/shell/e-shell-view.h b/shell/e-shell-view.h
index a3ca595..b323839 100644
--- a/shell/e-shell-view.h
+++ b/shell/e-shell-view.h
@@ -171,6 +171,8 @@ EShellContent *	e_shell_view_get_shell_content	(EShellView *shell_view);
 EShellSidebar *	e_shell_view_get_shell_sidebar	(EShellView *shell_view);
 EShellTaskbar *	e_shell_view_get_shell_taskbar	(EShellView *shell_view);
 EShellWindow *	e_shell_view_get_shell_window	(EShellView *shell_view);
+GKeyFile *	e_shell_view_get_state_key_file	(EShellView *shell_view);
+void		e_shell_view_set_state_dirty	(EShellView *shell_view);
 void		e_shell_view_update_actions	(EShellView *shell_view);
 void		e_shell_view_show_popup_menu	(EShellView *shell_view,
 						 const gchar *widget_path,



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