[evolution] Implement account-wide search scope in mail.



commit 31e449ebed6a136e643ec3bf4c0d9f1b3fc87897
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Feb 3 10:01:58 2010 -0500

    Implement account-wide search scope in mail.
    
    Also, let EShellSearchbar handle search state persistence.

 doc/reference/shell/tmpl/e-shell-searchbar.sgml |   14 +-
 doc/reference/shell/tmpl/eshell-unused.sgml     |    8 +
 modules/addressbook/e-book-shell-view-actions.c |   26 +-
 modules/calendar/e-cal-shell-view-actions.c     |   26 +-
 modules/calendar/e-memo-shell-view-actions.c    |   26 +-
 modules/calendar/e-task-shell-view-actions.c    |   26 +-
 modules/mail/e-mail-shell-view-actions.c        |   89 +-----
 modules/mail/e-mail-shell-view-private.c        |   59 ++++-
 modules/mail/e-mail-shell-view-private.h        |   10 +
 modules/mail/e-mail-shell-view.c                |  385 +++++++++++++++++++++--
 shell/e-shell-searchbar.c                       |  263 ++++++++++++++--
 shell/e-shell-searchbar.h                       |   21 +-
 shell/e-shell-view.c                            |    8 +-
 shell/e-shell-window.c                          |    3 -
 widgets/misc/e-action-combo-box.c               |    3 +
 15 files changed, 745 insertions(+), 222 deletions(-)
---
diff --git a/doc/reference/shell/tmpl/e-shell-searchbar.sgml b/doc/reference/shell/tmpl/e-shell-searchbar.sgml
index aeedaf3..3c55a58 100644
--- a/doc/reference/shell/tmpl/e-shell-searchbar.sgml
+++ b/doc/reference/shell/tmpl/e-shell-searchbar.sgml
@@ -73,6 +73,11 @@ EShellSearchbar
 
 </para>
 
+<!-- ##### ARG EShellSearchbar:state-group ##### -->
+<para>
+
+</para>
+
 <!-- ##### FUNCTION e_shell_searchbar_new ##### -->
 <para>
 
@@ -217,12 +222,3 @@ EShellSearchbar
 @scope_visible: 
 
 
-<!-- ##### FUNCTION e_shell_searchbar_restore_state ##### -->
-<para>
-
-</para>
-
- searchbar: 
- group_name: 
-
-
diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml
index f0e339d..8769407 100644
--- a/doc/reference/shell/tmpl/eshell-unused.sgml
+++ b/doc/reference/shell/tmpl/eshell-unused.sgml
@@ -2649,6 +2649,14 @@ intelligent
 @online: 
 @Returns: 
 
+<!-- ##### FUNCTION e_shell_searchbar_restore_state ##### -->
+<para>
+
+</para>
+
+ searchbar: 
+ group_name: 
+
 <!-- ##### FUNCTION e_shell_set_line_status ##### -->
 <para>
 
diff --git a/modules/addressbook/e-book-shell-view-actions.c b/modules/addressbook/e-book-shell-view-actions.c
index 32b227c..c439faf 100644
--- a/modules/addressbook/e-book-shell-view-actions.c
+++ b/modules/addressbook/e-book-shell-view-actions.c
@@ -655,14 +655,6 @@ action_gal_save_custom_view_cb (GtkAction *action,
 	gal_view_instance_save_as (view_instance);
 }
 
-static void
-action_search_filter_cb (GtkRadioAction *action,
-                         GtkRadioAction *current,
-                         EShellView *shell_view)
-{
-	e_shell_view_execute_search (shell_view);
-}
-
 static GtkActionEntry contact_entries[] = {
 
 	{ "address-book-copy",
@@ -1022,9 +1014,6 @@ e_book_shell_view_actions_init (EBookShellView *book_shell_view)
 	gtk_action_set_visible (action, FALSE);
 	e_shell_searchbar_set_search_option (
 		searchbar, GTK_RADIO_ACTION (action));
-	gtk_radio_action_set_current_value (
-		GTK_RADIO_ACTION (action),
-		CONTACT_SEARCH_NAME_CONTAINS);
 
 	/* Lockdown Printing Actions */
 	action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
@@ -1097,13 +1086,13 @@ e_book_shell_view_update_search_filter (EBookShellView *book_shell_view)
 	action_group = ACTION_GROUP (CONTACTS_FILTER);
 	e_action_group_remove_all_actions (action_group);
 
-	/* Add the standard filter actions. */
+	/* Add the standard filter actions.  No callback is needed
+	 * because changes in the EActionComboBox are detected and
+	 * handled by EShellSearchbar. */
 	gtk_action_group_add_radio_actions (
 		action_group, contact_filter_entries,
 		G_N_ELEMENTS (contact_filter_entries),
-		CONTACT_FILTER_ANY_CATEGORY,
-		G_CALLBACK (action_search_filter_cb),
-		book_shell_view);
+		CONTACT_FILTER_ANY_CATEGORY, NULL, NULL);
 
 	/* Retrieve the radio group from an action we just added. */
 	list = gtk_action_group_list_actions (action_group);
@@ -1154,12 +1143,17 @@ e_book_shell_view_update_search_filter (EBookShellView *book_shell_view)
 	}
 	g_list_free (list);
 
-	/* Use any action in the group; doesn't matter which. */
 	book_shell_content = book_shell_view->priv->book_shell_content;
 	searchbar = e_book_shell_content_get_searchbar (book_shell_content);
 	combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
+
+	e_shell_view_block_execute_search (shell_view);
+
+	/* Use any action in the group; doesn't matter which. */
 	e_action_combo_box_set_action (combo_box, radio_action);
 
 	ii = CONTACT_FILTER_UNMATCHED;
 	e_action_combo_box_add_separator_after (combo_box, ii);
+
+	e_shell_view_unblock_execute_search (shell_view);
 }
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
index 19fc7e6..03a6eb6 100644
--- a/modules/calendar/e-cal-shell-view-actions.c
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -1222,14 +1222,6 @@ action_gal_save_custom_view_cb (GtkAction *action,
 	gal_view_instance_save_as (view_instance);
 }
 
-static void
-action_search_filter_cb (GtkRadioAction *action,
-                         GtkRadioAction *current,
-                         EShellView *shell_view)
-{
-	e_shell_view_execute_search (shell_view);
-}
-
 static GtkActionEntry calendar_entries[] = {
 
 	{ "calendar-copy",
@@ -1719,9 +1711,6 @@ e_cal_shell_view_actions_init (ECalShellView *cal_shell_view)
 	gtk_action_set_visible (action, FALSE);
 	e_shell_searchbar_set_search_option (
 		searchbar, GTK_RADIO_ACTION (action));
-	gtk_radio_action_set_current_value (
-		GTK_RADIO_ACTION (action),
-		CALENDAR_SEARCH_SUMMARY_CONTAINS);
 
 	/* Lockdown Printing Actions */
 	action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
@@ -1784,13 +1773,13 @@ e_cal_shell_view_update_search_filter (ECalShellView *cal_shell_view)
 	action_group = ACTION_GROUP (CALENDAR_FILTER);
 	e_action_group_remove_all_actions (action_group);
 
-	/* Add the standard filter actions. */
+	/* Add the standard filter actions.  No callback is needed
+	 * because changes in the EActionComboBox are detected and
+	 * handled by EShellSearchbar. */
 	gtk_action_group_add_radio_actions (
 		action_group, calendar_filter_entries,
 		G_N_ELEMENTS (calendar_filter_entries),
-		CALENDAR_FILTER_ANY_CATEGORY,
-		G_CALLBACK (action_search_filter_cb),
-		cal_shell_view);
+		CALENDAR_FILTER_ANY_CATEGORY, NULL, NULL);
 
 	/* Retrieve the radio group from an action we just added. */
 	list = gtk_action_group_list_actions (action_group);
@@ -1841,10 +1830,13 @@ e_cal_shell_view_update_search_filter (ECalShellView *cal_shell_view)
 	}
 	g_list_free (list);
 
-	/* Use any action in the group; doesn't matter which. */
 	cal_shell_content = cal_shell_view->priv->cal_shell_content;
 	searchbar = e_cal_shell_content_get_searchbar (cal_shell_content);
 	combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
+
+	e_shell_view_block_execute_search (shell_view);
+
+	/* Use any action in the group; doesn't matter which. */
 	e_action_combo_box_set_action (combo_box, radio_action);
 
 	ii = CALENDAR_FILTER_UNMATCHED;
@@ -1852,4 +1844,6 @@ e_cal_shell_view_update_search_filter (ECalShellView *cal_shell_view)
 
 	ii = CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS;
 	e_action_combo_box_add_separator_after (combo_box, ii);
+
+	e_shell_view_unblock_execute_search (shell_view);
 }
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
index 24aced8..f53b8ff 100644
--- a/modules/calendar/e-memo-shell-view-actions.c
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -560,14 +560,6 @@ action_memo_view_cb (GtkRadioAction *action,
 	gtk_orientable_set_orientation (orientable, orientation);
 }
 
-static void
-action_search_filter_cb (GtkRadioAction *action,
-                         GtkRadioAction *current,
-                         EShellView *shell_view)
-{
-	e_shell_view_execute_search (shell_view);
-}
-
 static GtkActionEntry memo_entries[] = {
 
 	{ "memo-delete",
@@ -892,9 +884,6 @@ e_memo_shell_view_actions_init (EMemoShellView *memo_shell_view)
 	gtk_action_set_visible (action, FALSE);
 	e_shell_searchbar_set_search_option (
 		searchbar, GTK_RADIO_ACTION (action));
-	gtk_radio_action_set_current_value (
-		GTK_RADIO_ACTION (action),
-		MEMO_SEARCH_SUMMARY_CONTAINS);
 
 	/* Lockdown Printing Actions */
 	action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
@@ -967,13 +956,13 @@ e_memo_shell_view_update_search_filter (EMemoShellView *memo_shell_view)
 	action_group = ACTION_GROUP (MEMOS_FILTER);
 	e_action_group_remove_all_actions (action_group);
 
-	/* Add the standard filter actions. */
+	/* Add the standard filter actions.  No callback is needed
+	 * because changes in the EActionComboBox are detected and
+	 * handled by EShellSearchbar. */
 	gtk_action_group_add_radio_actions (
 		action_group, memo_filter_entries,
 		G_N_ELEMENTS (memo_filter_entries),
-		MEMO_FILTER_ANY_CATEGORY,
-		G_CALLBACK (action_search_filter_cb),
-		memo_shell_view);
+		MEMO_FILTER_ANY_CATEGORY, NULL, NULL);
 
 	/* Retrieve the radio group from an action we just added. */
 	list = gtk_action_group_list_actions (action_group);
@@ -1024,12 +1013,17 @@ e_memo_shell_view_update_search_filter (EMemoShellView *memo_shell_view)
 	}
 	g_list_free (list);
 
-	/* Use any action in the group; doesn't matter which. */
 	memo_shell_content = memo_shell_view->priv->memo_shell_content;
 	searchbar = e_memo_shell_content_get_searchbar (memo_shell_content);
 	combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
+
+	e_shell_view_block_execute_search (shell_view);
+
+	/* Use any action in the group; doesn't matter which. */
 	e_action_combo_box_set_action (combo_box, radio_action);
 
 	ii = MEMO_FILTER_UNMATCHED;
 	e_action_combo_box_add_separator_after (combo_box, ii);
+
+	e_shell_view_unblock_execute_search (shell_view);
 }
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
index c18882d..55873dd 100644
--- a/modules/calendar/e-task-shell-view-actions.c
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -43,14 +43,6 @@ action_gal_save_custom_view_cb (GtkAction *action,
 }
 
 static void
-action_search_filter_cb (GtkRadioAction *action,
-                         GtkRadioAction *current,
-                         EShellView *shell_view)
-{
-	e_shell_view_execute_search (shell_view);
-}
-
-static void
 action_task_assign_cb (GtkAction *action,
                        ETaskShellView *task_shell_view)
 {
@@ -1091,9 +1083,6 @@ e_task_shell_view_actions_init (ETaskShellView *task_shell_view)
 	gtk_action_set_visible (action, FALSE);
 	e_shell_searchbar_set_search_option (
 		searchbar, GTK_RADIO_ACTION (action));
-	gtk_radio_action_set_current_value (
-		GTK_RADIO_ACTION (action),
-		TASK_SEARCH_SUMMARY_CONTAINS);
 
 	/* Lockdown Printing Actions */
 	action_group = ACTION_GROUP (LOCKDOWN_PRINTING);
@@ -1166,13 +1155,13 @@ e_task_shell_view_update_search_filter (ETaskShellView *task_shell_view)
 	action_group = ACTION_GROUP (TASKS_FILTER);
 	e_action_group_remove_all_actions (action_group);
 
-	/* Add the standard filter actions. */
+	/* Add the standard filter actions.  No callback is needed
+	 * because changes in the EActionComboBox are detected and
+	 * handled by EShellSearchbar. */
 	gtk_action_group_add_radio_actions (
 		action_group, task_filter_entries,
 		G_N_ELEMENTS (task_filter_entries),
-		TASK_FILTER_ANY_CATEGORY,
-		G_CALLBACK (action_search_filter_cb),
-		task_shell_view);
+		TASK_FILTER_ANY_CATEGORY, NULL, NULL);
 
 	/* Retrieve the radio group from an action we just added. */
 	list = gtk_action_group_list_actions (action_group);
@@ -1223,10 +1212,13 @@ e_task_shell_view_update_search_filter (ETaskShellView *task_shell_view)
 	}
 	g_list_free (list);
 
-	/* Use any action in the group; doesn't matter which. */
 	task_shell_content = task_shell_view->priv->task_shell_content;
 	searchbar = e_task_shell_content_get_searchbar (task_shell_content);
 	combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
+
+	e_shell_view_block_execute_search (shell_view);
+
+	/* Use any action in the group; doesn't matter which. */
 	e_action_combo_box_set_action (combo_box, radio_action);
 
 	ii = TASK_FILTER_UNMATCHED;
@@ -1234,4 +1226,6 @@ e_task_shell_view_update_search_filter (ETaskShellView *task_shell_view)
 
 	ii = TASK_FILTER_TASKS_WITH_ATTACHMENTS;
 	e_action_combo_box_add_separator_after (combo_box, ii);
+
+	e_shell_view_unblock_execute_search (shell_view);
 }
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index 6b72c77..766489f 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -848,74 +848,6 @@ action_mail_view_cb (GtkRadioAction *action,
 	gtk_orientable_set_orientation (orientable, orientation);
 }
 
-static void
-action_search_filter_cb (GtkRadioAction *action,
-                         GtkRadioAction *current,
-                         EShellView *shell_view)
-{
-	EShellContent *shell_content;
-	EMailReader *reader;
-	GKeyFile *key_file;
-	const gchar *folder_uri;
-
-	shell_content = e_shell_view_get_shell_content (shell_view);
-	key_file = e_shell_view_get_state_key_file (shell_view);
-
-	reader = E_MAIL_READER (shell_content);
-	folder_uri = e_mail_reader_get_folder_uri (reader);
-
-	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);
-	}
-
-	e_shell_view_execute_search (shell_view);
-}
-
-static void
-action_search_scope_cb (GtkRadioAction *action,
-                        GtkRadioAction *current,
-                        EShellView *shell_view)
-{
-	EShellContent *shell_content;
-	EMailReader *reader;
-	GKeyFile *key_file;
-	const gchar *folder_uri;
-
-	shell_content = e_shell_view_get_shell_content (shell_view);
-	key_file = e_shell_view_get_state_key_file (shell_view);
-
-	reader = E_MAIL_READER (shell_content);
-	folder_uri = e_mail_reader_get_folder_uri (reader);
-
-	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);
-	}
-
-	e_shell_view_execute_search (shell_view);
-}
-
 static GtkActionEntry mail_entries[] = {
 
 	{ "mail-account-disable",
@@ -1449,8 +1381,7 @@ e_mail_shell_view_actions_init (EMailShellView *mail_shell_view)
 	gtk_action_group_add_radio_actions (
 		action_group, mail_scope_entries,
 		G_N_ELEMENTS (mail_scope_entries),
-		MAIL_SCOPE_CURRENT_FOLDER,
-		G_CALLBACK (action_search_scope_cb), mail_shell_view);
+		MAIL_SCOPE_CURRENT_FOLDER, NULL, NULL);
 
 	action = ACTION (MAIL_SCOPE_ALL_ACCOUNTS);
 	combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
@@ -1462,9 +1393,6 @@ e_mail_shell_view_actions_init (EMailShellView *mail_shell_view)
 	gtk_action_set_visible (action, FALSE);
 	e_shell_searchbar_set_search_option (
 		searchbar, GTK_RADIO_ACTION (action));
-	gtk_radio_action_set_current_value (
-		GTK_RADIO_ACTION (action),
-		MAIL_SEARCH_SUBJECT_OR_ADDRESSES_CONTAIN);
 
 	/* Bind GObject properties for GConf keys. */
 
@@ -1716,13 +1644,13 @@ e_mail_shell_view_update_search_filter (EMailShellView *mail_shell_view)
 	action_group = ACTION_GROUP (MAIL_FILTER);
 	e_action_group_remove_all_actions (action_group);
 
-	/* Add the standard filter actions. */
+	/* Add the standard filter actions.  No callback is needed
+	 * because changes in the EActionComboBox are detected and
+	 * handled by EShellSearchbar. */
 	gtk_action_group_add_radio_actions (
 		action_group, mail_filter_entries,
 		G_N_ELEMENTS (mail_filter_entries),
-		MAIL_FILTER_ALL_MESSAGES,
-		G_CALLBACK (action_search_filter_cb),
-		mail_shell_view);
+		MAIL_FILTER_ALL_MESSAGES, NULL, NULL);
 
 	/* Retrieve the radio group from an action we just added. */
 	list = gtk_action_group_list_actions (action_group);
@@ -1763,10 +1691,13 @@ e_mail_shell_view_update_search_filter (EMailShellView *mail_shell_view)
 		ii++;
 	}
 
-	/* Use any action in the group; doesn't matter which. */
 	mail_shell_content = mail_shell_view->priv->mail_shell_content;
 	searchbar = e_mail_shell_content_get_searchbar (mail_shell_content);
 	combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
+
+	e_shell_view_block_execute_search (shell_view);
+
+	/* Use any action in the group; doesn't matter which. */
 	e_action_combo_box_set_action (combo_box, radio_action);
 
 	ii = MAIL_FILTER_UNREAD_MESSAGES;
@@ -1775,5 +1706,7 @@ e_mail_shell_view_update_search_filter (EMailShellView *mail_shell_view)
 	ii = MAIL_FILTER_READ_MESSAGES;
 	e_action_combo_box_add_separator_after (combo_box, ii);
 
+	e_shell_view_unblock_execute_search (shell_view);
+
 	g_object_unref (tree_model);
 }
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index 377811e..125432c 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -416,6 +416,7 @@ void
 e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 {
 	EMailShellViewPrivate *priv = mail_shell_view->priv;
+	EMailShellContent *mail_shell_content;
 	EMailShellSidebar *mail_shell_sidebar;
 	EShell *shell;
 	EShellView *shell_view;
@@ -424,8 +425,10 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	EShellSettings *shell_settings;
 	EShellSidebar *shell_sidebar;
 	EShellWindow *shell_window;
+	EShellSearchbar *searchbar;
 	EMFormatHTMLDisplay *html_display;
 	EMFolderTree *folder_tree;
+	EActionComboBox *combo_box;
 	ERuleContext *context;
 	EFilterRule *rule = NULL;
 	GtkTreeSelection *selection;
@@ -472,6 +475,16 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
 
+	mail_shell_content = E_MAIL_SHELL_CONTENT (shell_content);
+	searchbar = e_mail_shell_content_get_searchbar (mail_shell_content);
+	combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
+
+	/* The folder tree and scope combo box are both insensitive
+	 * when searching beyond the currently selected folder. */
+	e_mutual_binding_new (
+		folder_tree, "sensitive",
+		combo_box, "sensitive");
+
 	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
 
 	g_signal_connect_swapped (
@@ -616,6 +629,21 @@ e_mail_shell_view_private_dispose (EMailShellView *mail_shell_view)
 
 	for (ii = 0; ii < MAIL_NUM_SEARCH_RULES; ii++)
 		DISPOSE (priv->search_rules[ii]);
+
+	if (priv->search_account_all != NULL) {
+		camel_object_unref (priv->search_account_all);
+		priv->search_account_all = NULL;
+	}
+
+	if (priv->search_account_current != NULL) {
+		camel_object_unref (priv->search_account_current);
+		priv->search_account_current = NULL;
+	}
+
+	if (priv->search_account_cancel != NULL) {
+		camel_operation_unref (priv->search_account_cancel);
+		priv->search_account_cancel = NULL;
+	}
 }
 
 void
@@ -630,8 +658,11 @@ e_mail_shell_view_restore_state (EMailShellView *mail_shell_view)
 	EMailShellContent *mail_shell_content;
 	EShellSearchbar *searchbar;
 	EMailReader *reader;
+	CamelFolder *folder;
+	CamelVeeFolder *vee_folder;
+	const gchar *old_state_group;
 	const gchar *folder_uri;
-	gchar *group_name;
+	gchar *new_state_group;
 
 	/* XXX Move this to EMailShellContent. */
 
@@ -641,14 +672,34 @@ e_mail_shell_view_restore_state (EMailShellView *mail_shell_view)
 	searchbar = e_mail_shell_content_get_searchbar (mail_shell_content);
 
 	reader = E_MAIL_READER (mail_shell_content);
+	folder = e_mail_reader_get_folder (reader);
 	folder_uri = e_mail_reader_get_folder_uri (reader);
 
 	if (folder_uri == NULL)
 		return;
 
-	group_name = g_strdup_printf ("Folder %s", folder_uri);
-	e_shell_searchbar_restore_state (searchbar, group_name);
-	g_free (group_name);
+	/* Do not restore state if we're running a "Current Account"
+	 * or "All Accounts" search, since we don't want the search
+	 * criteria to be destroyed in those cases. */
+
+	vee_folder = mail_shell_view->priv->search_account_all;
+	if (vee_folder != NULL && folder == CAMEL_FOLDER (vee_folder))
+		return;
+
+	vee_folder = mail_shell_view->priv->search_account_current;
+	if (vee_folder != NULL && folder == CAMEL_FOLDER (vee_folder))
+		return;
+
+	new_state_group = g_strdup_printf ("Folder %s", folder_uri);
+	old_state_group = e_shell_searchbar_get_state_group (searchbar);
+
+	/* Avoid loading search state unnecessarily. */
+	if (g_strcmp0 (new_state_group, old_state_group) != 0) {
+		e_shell_searchbar_set_state_group (searchbar, new_state_group);
+		e_shell_searchbar_load_state (searchbar);
+	}
+
+	g_free (new_state_group);
 }
 
 /* Helper for e_mail_shell_view_create_filter_from_selected() */
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index 44a02ed..9cdd71d 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -28,6 +28,9 @@
 #include <gtkhtml/gtkhtml.h>
 #include <camel/camel-disco-store.h>
 #include <camel/camel-offline-store.h>
+#include <camel/camel-operation.h>
+#include <camel/camel-vee-folder.h>
+#include <camel/camel-vee-store.h>
 #include <camel/camel-vtrash-folder.h>
 #include <camel/camel-search-private.h>  /* for camel_search_word */
 
@@ -58,6 +61,8 @@
 #include "mail-config.h"
 #include "mail-ops.h"
 #include "mail-send-recv.h"
+#include "mail-session.h"
+#include "mail-tools.h"
 #include "mail-vfolder.h"
 #include "message-list.h"
 
@@ -147,6 +152,11 @@ struct _EMailShellViewPrivate {
 	/* EShell::prepare-for-quit */
 	gulong prepare_for_quit_handler_id;
 
+	/* Search folders for interactive search. */
+	CamelVeeFolder *search_account_all;
+	CamelVeeFolder *search_account_current;
+	CamelOperation *search_account_cancel;
+
 	guint show_deleted : 1;
 };
 
diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c
index 6016a14..ce3c285 100644
--- a/modules/mail/e-mail-shell-view.c
+++ b/modules/mail/e-mail-shell-view.c
@@ -25,6 +25,114 @@
 static gpointer parent_class;
 static GType mail_shell_view_type;
 
+/* ETable spec for search results */
+static const gchar *SEARCH_RESULTS_STATE =
+"<ETableState>"
+"  <column source=\"0\"/>"
+"  <column source=\"3\"/>"
+"  <column source=\"1\"/>"
+"  <column source=\"14\"/>"
+"  <column source=\"5\"/>"
+"  <column source=\"7\"/>"
+"  <column source=\"13\"/>"
+"  <grouping>"
+"    <leaf column=\"7\" ascending=\"false\"/>"
+"  </grouping>"
+"</ETableState>";
+
+typedef struct {
+	MailMsg base;
+
+	CamelFolder *folder;
+	CamelOperation *cancel;
+	GList *folder_list;
+} SearchResultsMsg;
+
+static gchar *
+search_results_desc (SearchResultsMsg *msg)
+{
+	return g_strdup (_("Searching"));
+}
+
+static void
+search_results_exec (SearchResultsMsg *msg)
+{
+	GList *copied_list;
+
+	camel_operation_register (msg->cancel);
+
+	copied_list = g_list_copy (msg->folder_list);
+	g_list_foreach (copied_list, (GFunc) camel_object_ref, NULL);
+
+	camel_vee_folder_set_folders (
+		CAMEL_VEE_FOLDER (msg->folder), copied_list);
+
+	g_list_foreach (copied_list, (GFunc) camel_object_unref, NULL);
+	g_list_free (copied_list);
+}
+
+static void
+search_results_done (SearchResultsMsg *msg)
+{
+}
+
+static void
+search_results_free (SearchResultsMsg *msg)
+{
+	camel_object_unref (msg->folder);
+
+	g_list_foreach (msg->folder_list, (GFunc) camel_object_unref, NULL);
+	g_list_free (msg->folder_list);
+}
+
+static MailMsgInfo search_results_setup_info = {
+	sizeof (SearchResultsMsg),
+	(MailMsgDescFunc) search_results_desc,
+	(MailMsgExecFunc) search_results_exec,
+	(MailMsgDoneFunc) search_results_done,
+	(MailMsgFreeFunc) search_results_free
+};
+
+static gint
+mail_shell_view_setup_search_results_folder (CamelFolder *folder,
+                                             GList *folder_list,
+                                             CamelOperation *cancel)
+{
+	SearchResultsMsg *msg;
+	gint id;
+
+	camel_object_ref (folder);
+
+	msg = mail_msg_new (&search_results_setup_info);
+	msg->folder = folder;
+	msg->cancel = cancel;
+	msg->folder_list = folder_list;
+
+	id = msg->base.seq;
+	mail_msg_slow_ordered_push (msg);
+
+	return id;
+}
+
+static void
+mail_shell_view_show_search_results_folder (EMailShellView *mail_shell_view,
+                                            CamelFolder *folder,
+                                            const gchar *folder_uri)
+{
+	GtkWidget *message_list;
+	EMailReader *reader;
+
+	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
+	message_list = e_mail_reader_get_message_list (reader);
+
+	message_list_freeze (MESSAGE_LIST (message_list));
+
+	e_mail_reader_set_folder (reader, folder, folder_uri);
+	e_tree_set_state (E_TREE (message_list), SEARCH_RESULTS_STATE);
+
+	message_list_thaw (MESSAGE_LIST (message_list));
+}
+
 static void
 mail_shell_view_dispose (GObject *object)
 {
@@ -88,36 +196,47 @@ mail_shell_view_execute_search (EShellView *shell_view)
 {
 	EMailShellViewPrivate *priv;
 	EMailShellContent *mail_shell_content;
+	EMailShellSidebar *mail_shell_sidebar;
 	EShell *shell;
 	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	EShellContent *shell_content;
+	EShellSidebar *shell_sidebar;
 	EShellSettings *shell_settings;
 	EShellSearchbar *searchbar;
 	EActionComboBox *combo_box;
+	EMFolderTree *folder_tree;
+	GtkTreeSelection *selection;
 	GtkWidget *message_list;
 	EFilterRule *rule;
 	EMailReader *reader;
+	CamelVeeFolder *search_folder;
 	CamelFolder *folder;
+	CamelStore *store;
 	GtkAction *action;
 	GtkTreeModel *model;
 	GtkTreePath *path;
 	GtkTreeIter tree_iter;
 	GString *string;
-	GList *iter;
+	GList *list, *iter;
 	GSList *search_strings = NULL;
 	const gchar *folder_uri;
+	const gchar *data_dir;
 	const gchar *text;
 	gboolean valid;
 	gchar *query;
 	gchar *temp;
 	gchar *tag;
+	gchar *uri;
 	const gchar *use_tag;
 	gint value;
 
 	priv = E_MAIL_SHELL_VIEW_GET_PRIVATE (shell_view);
 
 	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
 	shell_content = e_shell_view_get_shell_content (shell_view);
+	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 
 	shell = e_shell_window_get_shell (shell_window);
 	shell_settings = e_shell_get_shell_settings (shell);
@@ -125,33 +244,16 @@ mail_shell_view_execute_search (EShellView *shell_view)
 	mail_shell_content = E_MAIL_SHELL_CONTENT (shell_content);
 	searchbar = e_mail_shell_content_get_searchbar (mail_shell_content);
 
+	mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar);
+	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
+
 	reader = E_MAIL_READER (shell_content);
 	folder = e_mail_reader_get_folder (reader);
 	folder_uri = e_mail_reader_get_folder_uri (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
-	if (folder_uri != NULL) {
-		GKeyFile *key_file;
-		const gchar *key;
-		const gchar *string;
-		gchar *group_name;
-
-		key_file = e_shell_view_get_state_key_file (shell_view);
-
-		key = STATE_KEY_SEARCH_TEXT;
-		string = e_shell_searchbar_get_search_text (searchbar);
-		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);
-	}
+	data_dir = e_shell_backend_get_data_dir (shell_backend);
 
 	/* This returns a new object reference. */
 	model = e_shell_settings_get_object (
@@ -357,6 +459,243 @@ filter:
 			break;
 	}
 
+	/* Apply selected scope. */
+
+	combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
+	value = e_action_combo_box_get_current_value (combo_box);
+	switch (value) {
+		case MAIL_SCOPE_CURRENT_FOLDER:
+			goto execute;
+
+		case MAIL_SCOPE_CURRENT_ACCOUNT:
+			goto current_account;
+
+		case MAIL_SCOPE_ALL_ACCOUNTS:
+			goto all_accounts;
+
+		default:
+			g_warn_if_reached ();
+			goto execute;
+	}
+
+all_accounts:
+
+	/* Prepare search folder for all accounts. */
+
+	/* If the search text is empty, cancel any
+	 * account-wide searches still in progress. */
+	text = e_shell_searchbar_get_search_text (searchbar);
+	if (text == NULL || *text == '\0') {
+		if (priv->search_account_all != NULL) {
+			camel_object_unref (priv->search_account_all);
+			priv->search_account_all = NULL;
+		}
+
+		if (priv->search_account_cancel != NULL) {
+			camel_operation_cancel (priv->search_account_cancel);
+			camel_operation_unref (priv->search_account_cancel);
+			priv->search_account_cancel = NULL;
+		}
+
+		/* Simulate a folder tree selection change, so the
+		 * message list is reset to the correct folder via
+		 * our EMFolderTree::folder-selected handler. */
+		g_signal_emit_by_name (selection, "changed");
+
+		gtk_widget_set_sensitive (GTK_WIDGET (combo_box), TRUE);
+
+		goto execute;
+	}
+
+	search_folder = priv->search_account_all;
+
+	/* Skip the search if we already have the results. */
+	if (search_folder != NULL)
+		if (g_strcmp0 (query, search_folder->expression) == 0)
+			goto execute;
+
+	/* Disable the scope combo while search is in progress. */
+	gtk_widget_set_sensitive (GTK_WIDGET (combo_box), FALSE);
+
+	/* If we already have a search folder, reuse it. */
+	if (search_folder != NULL) {
+		if (priv->search_account_cancel != NULL) {
+			camel_operation_cancel (priv->search_account_cancel);
+			camel_operation_unref (priv->search_account_cancel);
+			priv->search_account_cancel = NULL;
+		}
+
+		camel_vee_folder_set_expression (search_folder, query);
+
+		goto execute;
+	}
+
+	/* Create a new search folder. */
+
+	list = NULL;  /* list of CamelFolders */
+
+	/* FIXME Using data_dir like this is not portable. */
+	uri = g_strdup_printf ("vfolder:%s/vfolder", data_dir);
+	store = camel_session_get_store (session, uri, NULL);
+	g_free (uri);
+
+	search_folder = (CamelVeeFolder *) camel_vee_folder_new (
+		store, _("All Account Search"), CAMEL_STORE_VEE_FOLDER_AUTO);
+	priv->search_account_all = search_folder;
+
+	/* Add local folders. */
+	iter = mail_vfolder_get_sources_local ();
+	while (iter != NULL) {
+		CamelException ex;
+
+		camel_exception_init (&ex);
+
+		folder_uri = iter->data;
+		folder = mail_tool_uri_to_folder (folder_uri, 0, &ex);
+
+		if (folder != NULL)
+			list = g_list_append (list, folder);
+		else
+			g_warning ("Could not open vfolder source: %s", folder_uri);
+
+		camel_exception_clear (&ex);
+		iter = g_list_next (iter);
+	}
+
+	/* Add remote folders. */
+	iter = mail_vfolder_get_sources_remote ();
+	while (iter != NULL) {
+		CamelException ex;
+
+		camel_exception_init (&ex);
+
+		folder_uri = iter->data;
+		folder = mail_tool_uri_to_folder (folder_uri, 0, &ex);
+
+		if (folder != NULL)
+			list = g_list_append (list, folder);
+		else
+			g_warning ("Could not open vfolder source: %s", folder_uri);
+
+		camel_exception_clear (&ex);
+		iter = g_list_next (iter);
+	}
+
+	camel_vee_folder_set_expression (search_folder, query);
+
+	priv->search_account_cancel = camel_operation_new (NULL, NULL);
+
+	/* This takes ownership of the folder list. */
+	mail_shell_view_setup_search_results_folder (
+		CAMEL_FOLDER (search_folder), list,
+		priv->search_account_cancel);
+
+	uri = mail_tools_folder_to_url (CAMEL_FOLDER (search_folder));
+
+	mail_shell_view_show_search_results_folder (
+		E_MAIL_SHELL_VIEW (shell_view),
+		CAMEL_FOLDER (search_folder), uri);
+
+	g_free (uri);
+
+	goto execute;
+
+current_account:
+
+	/* Prepare search folder for current account only. */
+
+	/* If the search text is empty, cancel any
+	 * account-wide searches still in progress. */
+	text = e_shell_searchbar_get_search_text (searchbar);
+	if (text == NULL || *text == '\0') {
+		if (priv->search_account_current != NULL) {
+			camel_object_unref (priv->search_account_current);
+			priv->search_account_current = NULL;
+		}
+
+		if (priv->search_account_cancel != NULL) {
+			camel_operation_cancel (priv->search_account_cancel);
+			camel_operation_unref (priv->search_account_cancel);
+			priv->search_account_cancel = NULL;
+		}
+
+		/* Simulate a folder tree selection change, so the
+		 * message list is reset to the correct folder via
+		 * our EMFolderTree::folder-selected handler. */
+		g_signal_emit_by_name (selection, "changed");
+
+		gtk_widget_set_sensitive (GTK_WIDGET (combo_box), TRUE);
+
+		goto execute;
+	}
+
+	search_folder = priv->search_account_current;
+
+	/* Skip the search if we already have the results. */
+	if (search_folder != NULL)
+		if (g_strcmp0 (query, search_folder->expression) == 0)
+			goto execute;
+
+	/* Disable the scope combo while search is in progress. */
+	gtk_widget_set_sensitive (GTK_WIDGET (combo_box), FALSE);
+
+	/* If we already have a search folder, reuse it. */
+	if (search_folder != NULL) {
+		if (priv->search_account_cancel != NULL) {
+			camel_operation_cancel (priv->search_account_cancel);
+			camel_operation_unref (priv->search_account_cancel);
+			priv->search_account_cancel = NULL;
+		}
+
+		camel_vee_folder_set_expression (search_folder, query);
+
+		goto execute;
+	}
+
+	/* Create a new search folder. */
+
+	store = folder->parent_store;
+	list = NULL;  /* list of CamelFolders */
+
+	if (store->folders != NULL) {
+		GPtrArray *array;
+		guint ii;
+
+		array = camel_object_bag_list (store->folders);
+		for (ii = 0; ii < array->len; ii++)
+			list = g_list_append (list, array->pdata[ii]);
+	}
+
+	/* FIXME Using data_dir like this is not portable. */
+	uri = g_strdup_printf ("vfolder:%s/vfolder", data_dir);
+	store = camel_session_get_store (session, uri, NULL);
+	g_free (uri);
+
+	search_folder = (CamelVeeFolder *) camel_vee_folder_new (
+		store, _("Account Search"), CAMEL_STORE_VEE_FOLDER_AUTO);
+	priv->search_account_current = search_folder;
+
+	camel_vee_folder_set_expression (search_folder, query);
+
+	priv->search_account_cancel = camel_operation_new (NULL, NULL);
+
+	/* This takes ownership of the folder list. */
+	mail_shell_view_setup_search_results_folder (
+		CAMEL_FOLDER (search_folder), list,
+		priv->search_account_cancel);
+
+	uri = mail_tools_folder_to_url (CAMEL_FOLDER (search_folder));
+
+	mail_shell_view_show_search_results_folder (
+		E_MAIL_SHELL_VIEW (shell_view),
+		CAMEL_FOLDER (search_folder), uri);
+
+	g_free (uri);
+
+execute:
+
+	/* Finally, execute the search. */
+
 	message_list_set_search (MESSAGE_LIST (message_list), query);
 
 	e_mail_shell_content_set_search_strings (
diff --git a/shell/e-shell-searchbar.c b/shell/e-shell-searchbar.c
index a84e1eb..f334e82 100644
--- a/shell/e-shell-searchbar.c
+++ b/shell/e-shell-searchbar.c
@@ -24,6 +24,7 @@
 #include <config.h>
 #include <glib/gi18n-lib.h>
 
+#include "e-util/e-util.h"
 #include "e-util/e-binding.h"
 #include "widgets/misc/e-action-combo-box.h"
 #include "widgets/misc/e-hinted-entry.h"
@@ -36,7 +37,11 @@
 
 #define SEARCH_OPTION_ADVANCED		(-1)
 
+/* Default "state key file" group: [Search Bar] */
+#define STATE_GROUP_DEFAULT		"Search Bar"
+
 #define STATE_KEY_SEARCH_FILTER		"SearchFilter"
+#define STATE_KEY_SEARCH_OPTION		"SearchOption"
 #define STATE_KEY_SEARCH_SCOPE		"SearchScope"
 #define STATE_KEY_SEARCH_TEXT		"SearchText"
 
@@ -52,16 +57,21 @@ struct _EShellSearchbarPrivate {
 	GtkWidget *search_entry;
 	GtkWidget *scope_combo_box;
 
+	/* State Key File */
+	gchar *state_group;
+
 	guint filter_visible : 1;
+	guint label_visible  : 1;
 	guint search_visible : 1;
 	guint scope_visible  : 1;
-	guint label_visible  : 1;
+	guint state_dirty    : 1;
 };
 
 enum {
 	PROP_0,
 	PROP_FILTER_COMBO_BOX,
 	PROP_FILTER_VISIBLE,
+	PROP_LABEL_VISIBLE,
 	PROP_SEARCH_HINT,
 	PROP_SEARCH_OPTION,
 	PROP_SEARCH_TEXT,
@@ -69,7 +79,7 @@ enum {
 	PROP_SCOPE_COMBO_BOX,
 	PROP_SCOPE_VISIBLE,
 	PROP_SHELL_VIEW,
-	PROP_LABEL_VISIBLE,
+	PROP_STATE_GROUP
 };
 
 static gpointer parent_class;
@@ -159,6 +169,8 @@ shell_searchbar_execute_search_cb (EShellView *shell_view,
 
 	shell_searchbar_update_search_widgets (searchbar);
 
+	e_shell_searchbar_save_state (searchbar);
+
 	if (!e_shell_view_is_active (shell_view))
 		return;
 
@@ -324,13 +336,14 @@ shell_searchbar_set_property (GObject *object,
                               GParamSpec *pspec)
 {
 	switch (property_id) {
-		case PROP_LABEL_VISIBLE:
-			e_shell_searchbar_set_label_visible (
+		case PROP_FILTER_VISIBLE:
+			e_shell_searchbar_set_filter_visible (
 				E_SHELL_SEARCHBAR (object),
 				g_value_get_boolean (value));
 			return;
-		case PROP_FILTER_VISIBLE:
-			e_shell_searchbar_set_filter_visible (
+
+		case PROP_LABEL_VISIBLE:
+			e_shell_searchbar_set_label_visible (
 				E_SHELL_SEARCHBAR (object),
 				g_value_get_boolean (value));
 			return;
@@ -370,6 +383,12 @@ shell_searchbar_set_property (GObject *object,
 				E_SHELL_SEARCHBAR (object),
 				g_value_get_object (value));
 			return;
+
+		case PROP_STATE_GROUP:
+			e_shell_searchbar_set_state_group (
+				E_SHELL_SEARCHBAR (object),
+				g_value_get_string (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -441,6 +460,12 @@ shell_searchbar_get_property (GObject *object,
 				value, e_shell_searchbar_get_shell_view (
 				E_SHELL_SEARCHBAR (object)));
 			return;
+
+		case PROP_STATE_GROUP:
+			g_value_set_string (
+				value, e_shell_searchbar_get_state_group (
+				E_SHELL_SEARCHBAR (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -488,18 +513,25 @@ shell_searchbar_constructed (GObject *object)
 
 	g_signal_connect (
 		shell_view, "clear-search",
-		G_CALLBACK (shell_searchbar_clear_search_cb),
-		searchbar);
+		G_CALLBACK (shell_searchbar_clear_search_cb), searchbar);
 
 	g_signal_connect (
 		shell_view, "custom-search",
-		G_CALLBACK (shell_searchbar_custom_search_cb),
-		searchbar);
+		G_CALLBACK (shell_searchbar_custom_search_cb), searchbar);
 
 	g_signal_connect_after (
 		shell_view, "execute-search",
-		G_CALLBACK (shell_searchbar_execute_search_cb),
-		searchbar);
+		G_CALLBACK (shell_searchbar_execute_search_cb), searchbar);
+
+	widget = searchbar->priv->filter_combo_box;
+
+	g_signal_connect_swapped (
+		widget, "changed",
+		G_CALLBACK (e_shell_searchbar_set_state_dirty), searchbar);
+
+	g_signal_connect_swapped (
+		widget, "changed",
+		G_CALLBACK (e_shell_view_execute_search), shell_view);
 
 	widget = searchbar->priv->search_entry;
 
@@ -532,9 +564,22 @@ shell_searchbar_constructed (GObject *object)
 }
 
 static void
+shell_searchbar_map (GtkWidget *widget)
+{
+	/* Chain up to parent's map() method. */
+	GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+	/* Load state after constructed() so we don't derail
+	 * subclass initialization.  We wait until map() so we
+	 * have usable style colors for the entry box. */
+	e_shell_searchbar_load_state (E_SHELL_SEARCHBAR (widget));
+}
+
+static void
 shell_searchbar_class_init (EShellSearchbarClass *class)
 {
 	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
 
 	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (EShellSearchbarPrivate));
@@ -545,6 +590,9 @@ shell_searchbar_class_init (EShellSearchbarClass *class)
 	object_class->dispose = shell_searchbar_dispose;
 	object_class->constructed = shell_searchbar_constructed;
 
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->map = shell_searchbar_map;
+
 	g_object_class_install_property (
 		object_class,
 		PROP_FILTER_COMBO_BOX,
@@ -654,6 +702,22 @@ shell_searchbar_class_init (EShellSearchbarClass *class)
 			E_TYPE_SHELL_VIEW,
 			G_PARAM_READWRITE |
 			G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * EShellContent:state-group
+	 *
+	 * Key file group name to read and write search bar state.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_STATE_GROUP,
+		g_param_spec_string (
+			"state-group",
+			NULL,
+			NULL,
+			STATE_GROUP_DEFAULT,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
 }
 
 static void
@@ -737,6 +801,11 @@ shell_searchbar_init (EShellSearchbar *searchbar)
 		searchbar);
 
 	g_signal_connect_swapped (
+		widget, "changed",
+		G_CALLBACK (e_shell_searchbar_set_state_dirty),
+		searchbar);
+
+	g_signal_connect_swapped (
 		widget, "icon-press",
 		G_CALLBACK (shell_searchbar_entry_icon_press_cb),
 		searchbar);
@@ -777,6 +846,11 @@ shell_searchbar_init (EShellSearchbar *searchbar)
 	gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
 	searchbar->priv->scope_combo_box = widget;
 	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "changed",
+		G_CALLBACK (e_shell_searchbar_set_state_dirty),
+		searchbar);
 }
 
 GType
@@ -856,7 +930,7 @@ e_shell_searchbar_get_label_visible (EShellSearchbar *searchbar)
 
 void
 e_shell_searchbar_set_label_visible (EShellSearchbar *searchbar,
-                                      gboolean label_visible)
+                                     gboolean label_visible)
 {
 	g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
 
@@ -1015,7 +1089,7 @@ e_shell_searchbar_get_scope_visible (EShellSearchbar *searchbar)
 
 void
 e_shell_searchbar_set_scope_visible (EShellSearchbar *searchbar,
-                                      gboolean scope_visible)
+                                     gboolean scope_visible)
 {
 	g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
 
@@ -1025,8 +1099,38 @@ e_shell_searchbar_set_scope_visible (EShellSearchbar *searchbar,
 }
 
 void
-e_shell_searchbar_restore_state (EShellSearchbar *searchbar,
-                                 const gchar *group_name)
+e_shell_searchbar_set_state_dirty (EShellSearchbar *searchbar)
+{
+	g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
+
+	searchbar->priv->state_dirty = TRUE;
+}
+
+const gchar *
+e_shell_searchbar_get_state_group (EShellSearchbar *searchbar)
+{
+	g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
+
+	return searchbar->priv->state_group;
+}
+
+void
+e_shell_searchbar_set_state_group (EShellSearchbar *searchbar,
+                                   const gchar *state_group)
+{
+	g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
+
+	if (state_group == NULL)
+		state_group = STATE_GROUP_DEFAULT;
+
+	g_free (searchbar->priv->state_group);
+	searchbar->priv->state_group = g_strdup (state_group);
+
+	g_object_notify (G_OBJECT (searchbar), "state-group");
+}
+
+void
+e_shell_searchbar_load_state (EShellSearchbar *searchbar)
 {
 	EShellView *shell_view;
 	EShellWindow *shell_window;
@@ -1034,15 +1138,19 @@ e_shell_searchbar_restore_state (EShellSearchbar *searchbar,
 	GtkAction *action;
 	GtkWidget *widget;
 	const gchar *search_text;
+	const gchar *state_group;
 	const gchar *key;
 	gchar *string;
+	gint value = 0;
 
 	g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
-	g_return_if_fail (group_name != NULL);
 
 	shell_view = e_shell_searchbar_get_shell_view (searchbar);
-	shell_window = e_shell_view_get_shell_window (shell_view);
+	state_group = e_shell_searchbar_get_state_group (searchbar);
+	g_return_if_fail (state_group != NULL);
+
 	key_file = e_shell_view_get_state_key_file (shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
 	/* Changing the combo boxes triggers searches, so block
 	 * the search action until the state is fully restored. */
@@ -1052,13 +1160,14 @@ e_shell_searchbar_restore_state (EShellSearchbar *searchbar,
 	e_shell_view_block_execute_search (shell_view);
 
 	e_shell_view_set_search_rule (shell_view, NULL);
+
 	key = STATE_KEY_SEARCH_FILTER;
-	string = g_key_file_get_string (key_file, group_name, key, NULL);
+	string = g_key_file_get_string (key_file, state_group, key, NULL);
 	if (string != NULL && *string != '\0')
 		action = e_shell_window_get_action (shell_window, string);
 	else
 		action = NULL;
-	if (action != NULL)
+	if (GTK_IS_RADIO_ACTION (action))
 		gtk_action_activate (action);
 	else {
 		/* Pick the first combo box item. */
@@ -1067,23 +1176,26 @@ e_shell_searchbar_restore_state (EShellSearchbar *searchbar,
 	}
 	g_free (string);
 
-	key = STATE_KEY_SEARCH_SCOPE;
-	string = g_key_file_get_string (key_file, group_name, key, NULL);
+	/* Avoid restoring to the "Advanced Search" option, since we
+	 * don't currently save the search rule (TODO but we should). */
+	key = STATE_KEY_SEARCH_OPTION;
+	string = g_key_file_get_string (key_file, state_group, key, NULL);
 	if (string != NULL && *string != '\0')
 		action = e_shell_window_get_action (shell_window, string);
 	else
 		action = NULL;
-	if (action != NULL)
+	if (GTK_IS_RADIO_ACTION (action))
+		g_object_get (action, "value", &value, NULL);
+	else
+		value = SEARCH_OPTION_ADVANCED;
+	if (value != SEARCH_OPTION_ADVANCED)
 		gtk_action_activate (action);
-	else {
-		/* Pick the first combo box item. */
-		widget = searchbar->priv->scope_combo_box;
-		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
-	}
-	g_free (string);
+	else if (searchbar->priv->search_option != NULL)
+		gtk_radio_action_set_current_value (
+			searchbar->priv->search_option, 0);
 
 	key = STATE_KEY_SEARCH_TEXT;
-	string = g_key_file_get_string (key_file, group_name, key, NULL);
+	string = g_key_file_get_string (key_file, state_group, key, NULL);
 	search_text = e_shell_searchbar_get_search_text (searchbar);
 	if (search_text != NULL && *search_text == '\0')
 		search_text = NULL;
@@ -1091,11 +1203,102 @@ e_shell_searchbar_restore_state (EShellSearchbar *searchbar,
 		e_shell_searchbar_set_search_text (searchbar, string);
 	g_free (string);
 
+	/* Search scope is hard-coded to the default state group. */
+	state_group = STATE_GROUP_DEFAULT;
+
+	key = STATE_KEY_SEARCH_SCOPE;
+	string = g_key_file_get_string (key_file, state_group, key, NULL);
+	if (string != NULL && *string != '\0')
+		action = e_shell_window_get_action (shell_window, string);
+	else
+		action = NULL;
+	if (GTK_IS_RADIO_ACTION (action))
+		gtk_action_activate (action);
+	else {
+		/* Pick the first combo box item. */
+		widget = searchbar->priv->scope_combo_box;
+		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+	}
+	g_free (string);
+
 	e_shell_view_unblock_execute_search (shell_view);
 
 	action = E_SHELL_WINDOW_ACTION_SEARCH_QUICK (shell_window);
 	gtk_action_unblock_activate (action);
 
 	/* Now execute the search. */
+	searchbar->priv->state_dirty = FALSE;
 	e_shell_view_execute_search (shell_view);
 }
+
+void
+e_shell_searchbar_save_state (EShellSearchbar *searchbar)
+{
+	EShellView *shell_view;
+	GKeyFile *key_file;
+	GtkRadioAction *radio_action;
+	EActionComboBox *action_combo_box;
+	const gchar *action_name;
+	const gchar *search_text;
+	const gchar *state_group;
+	const gchar *key;
+
+	g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
+
+	/* Skip saving state if it hasn't changed since it was loaded. */
+	if (!searchbar->priv->state_dirty)
+		return;
+
+	shell_view = e_shell_searchbar_get_shell_view (searchbar);
+	state_group = e_shell_searchbar_get_state_group (searchbar);
+	g_return_if_fail (state_group != NULL);
+
+	key_file = e_shell_view_get_state_key_file (shell_view);
+
+	key = STATE_KEY_SEARCH_FILTER;
+	action_combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
+	radio_action = e_action_combo_box_get_action (action_combo_box);
+	if (radio_action != NULL)
+		radio_action = e_radio_action_get_current_action (radio_action);
+	if (radio_action != NULL) {
+		action_name = gtk_action_get_name (GTK_ACTION (radio_action));
+		g_key_file_set_string (key_file, state_group, key, action_name);
+	} else
+		g_key_file_remove_key (key_file, state_group, key, NULL);
+
+	key = STATE_KEY_SEARCH_OPTION;
+	radio_action = e_shell_searchbar_get_search_option (searchbar);
+	if (radio_action != NULL)
+		radio_action = e_radio_action_get_current_action (radio_action);
+	if (radio_action != NULL) {
+		action_name = gtk_action_get_name (GTK_ACTION (radio_action));
+		g_key_file_set_string (key_file, state_group, key, action_name);
+	} else
+		g_key_file_remove_key (key_file, state_group, key, NULL);
+
+	key = STATE_KEY_SEARCH_TEXT;
+	search_text = e_shell_searchbar_get_search_text (searchbar);
+	if (search_text != NULL && *search_text == '\0')
+		search_text = NULL;
+	if (search_text != NULL)
+		g_key_file_set_string (key_file, state_group, key, search_text);
+	else
+		g_key_file_remove_key (key_file, state_group, key, NULL);
+
+	/* Search scope is hard-coded to the default state group. */
+	state_group = STATE_GROUP_DEFAULT;
+
+	key = STATE_KEY_SEARCH_SCOPE;
+	action_combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
+	radio_action = e_action_combo_box_get_action (action_combo_box);
+	if (radio_action != NULL)
+		radio_action = e_radio_action_get_current_action (radio_action);
+	if (radio_action != NULL) {
+		action_name = gtk_action_get_name (GTK_ACTION (radio_action));
+		g_key_file_set_string (key_file, state_group, key, action_name);
+	} else
+		g_key_file_remove_key (key_file, state_group, key, NULL);
+
+	searchbar->priv->state_dirty = FALSE;
+	e_shell_view_set_state_dirty (shell_view);
+}
diff --git a/shell/e-shell-searchbar.h b/shell/e-shell-searchbar.h
index 79b593b..23fa480 100644
--- a/shell/e-shell-searchbar.h
+++ b/shell/e-shell-searchbar.h
@@ -83,6 +83,11 @@ gboolean	e_shell_searchbar_get_filter_visible
 void		e_shell_searchbar_set_filter_visible
 						(EShellSearchbar *searchbar,
 						 gboolean filter_visible);
+gboolean	e_shell_searchbar_get_label_visible
+						(EShellSearchbar *searchbar);
+void		e_shell_searchbar_set_label_visible
+						(EShellSearchbar *searchbar,
+						 gboolean label_visible);
 const gchar *	e_shell_searchbar_get_search_hint
 						(EShellSearchbar *searchbar);
 void		e_shell_searchbar_set_search_hint
@@ -103,11 +108,6 @@ gboolean	e_shell_searchbar_get_search_visible
 void		e_shell_searchbar_set_search_visible
 						(EShellSearchbar *searchbar,
 						 gboolean search_visible);
-gboolean	e_shell_searchbar_get_label_visible
-						(EShellSearchbar *searchbar);
-void		e_shell_searchbar_set_label_visible
-						(EShellSearchbar *searchbar,
-						 gboolean label_visible);
 EActionComboBox *
 		e_shell_searchbar_get_scope_combo_box
 						(EShellSearchbar *searchbar);
@@ -116,8 +116,15 @@ gboolean	e_shell_searchbar_get_scope_visible
 void		e_shell_searchbar_set_scope_visible
 						(EShellSearchbar *searchbar,
 						 gboolean scope_visible);
-void		e_shell_searchbar_restore_state	(EShellSearchbar *searchbar,
-						 const gchar *group_name);
+void		e_shell_searchbar_set_state_dirty
+						(EShellSearchbar *searchbar);
+const gchar *	e_shell_searchbar_get_state_group
+						(EShellSearchbar *searchbar);
+void		e_shell_searchbar_set_state_group
+						(EShellSearchbar *searchbar,
+						 const gchar *state_group);
+void		e_shell_searchbar_load_state	(EShellSearchbar *searchbar);
+void		e_shell_searchbar_save_state	(EShellSearchbar *searchbar);
 
 G_END_DECLS
 
diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c
index fdffe92..83be139 100644
--- a/shell/e-shell-view.c
+++ b/shell/e-shell-view.c
@@ -1402,8 +1402,8 @@ e_shell_view_execute_search (EShellView *shell_view)
  * e_shell_view_block_execute_search:
  * @shell_view: an #EShellView
  *
- * Blocks e_shell_view_execute_search in a way it does nothing.
- * Pair function for this is e_shell_view_unblock_execute_search.
+ * Blocks e_shell_view_execute_search() in a way it does nothing.
+ * Pair function for this is e_shell_view_unblock_execute_search().
  **/
 void
 e_shell_view_block_execute_search (EShellView *shell_view)
@@ -1418,8 +1418,8 @@ e_shell_view_block_execute_search (EShellView *shell_view)
  * e_shell_view_unblock_execute_search:
  * @shell_view: an #EShellView
  *
- * Unblocks previously blocked e_shell_view_execute_search with
- * function e_shell_view_block_execute_search.
+ * Unblocks previously blocked e_shell_view_execute_search() with
+ * function e_shell_view_block_execute_search().
  **/
 void
 e_shell_view_unblock_execute_search (EShellView *shell_view)
diff --git a/shell/e-shell-window.c b/shell/e-shell-window.c
index d159406..5484573 100644
--- a/shell/e-shell-window.c
+++ b/shell/e-shell-window.c
@@ -578,9 +578,6 @@ shell_window_create_shell_view (EShellWindow *shell_window,
 		shell_view, "notify::view-id",
 		G_CALLBACK (e_shell_window_update_view_menu), shell_window);
 
-	/* Execute an initial search. */
-	e_shell_view_execute_search (shell_view);
-
 	return shell_view;
 }
 
diff --git a/widgets/misc/e-action-combo-box.c b/widgets/misc/e-action-combo-box.c
index 7010dc0..2fa376d 100644
--- a/widgets/misc/e-action-combo-box.c
+++ b/widgets/misc/e-action-combo-box.c
@@ -502,6 +502,7 @@ e_action_combo_box_set_action (EActionComboBox *combo_box,
 		g_object_get (
 			g_object_ref (action), "action-group",
 			&combo_box->priv->action_group, NULL);
+
 	combo_box->priv->action = action;
 	action_combo_box_update_model (combo_box);
 
@@ -526,6 +527,8 @@ e_action_combo_box_set_action (EActionComboBox *combo_box,
 				action_combo_box_action_group_notify_cb),
 				combo_box);
 	}
+
+	g_object_notify (G_OBJECT (combo_box), "action");
 }
 
 gint



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